using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using CommunityToolkit.Mvvm.ComponentModel;
using IFSEngine.Generation;
using IFSEngine.Model;
using IFSEngine.Rendering;
using IFSEngine.Utility;
using OpenTK.Windowing.Desktop;
namespace WpfDisplay.Models;
/// <summary>
/// The generator workspace model that contains a utility <see cref="RendererGL"/>
/// and lists of <see cref="IFSEngine.Model.IFS"/> that it is rendering.
/// </summary>
public partial class GeneratorWorkspace : ObservableObject
{
public IReadOnlyList<IFS> PinnedIFS => _pinnedIFS;
public IReadOnlyList<IFS> GeneratedIFS => _generatedIFS;
public IReadOnlyDictionary<IFS, ImageSource> Thumbnails => _thumbnails;
private readonly IGLFWGraphicsContext _context;
private readonly RendererGL _renderer;
private readonly Generator _generator;
private readonly ConcurrentQueue<IFS> _renderQueue = new();
private readonly ConcurrentDictionary<IFS, ImageSource> _thumbnails = new();
private readonly List<IFS> _pinnedIFS = [];
private readonly List<IFS> _generatedIFS = [];
/// <summary>
/// Call <see cref="Initialize"/> before using
/// </summary>
/// <param name="loadedTransforms"></param>
public GeneratorWorkspace(IReadOnlyCollection<string> includeSources, IReadOnlyCollection<IFSEngine.Model.TransformPlugin> loadedTransforms)
{
_includeSources = includeSources;
//init thumbnail renderer
GameWindow hw = new(new GameWindowSettings(), new NativeWindowSettings
{
Flags = OpenTK.Windowing.Common.ContextFlags.Offscreen,
IsEventDriven = true,
StartFocused = false,
StartVisible = false,
WindowState = OpenTK.Windowing.Common.WindowState.Minimized
});
_context = hw.Context;
_renderer = new RendererGL(_context);
_renderer.SetDisplayResolution(200, 200);
_generator = new Generator(loadedTransforms);
}
public async Task Initialize()
{
await _renderer.Initialize(_includeSources, _generator.SelectedTransforms, []);
//performance settings
await _renderer.SetWorkgroupCount(100);
_context.MakeNoneCurrent();
}
public void GenerateNewRandomBatch(GeneratorOptions options)
{
options.BaseParams = _pinnedIFS.LastOrDefault() ?? GeneratorOptions.DefaultStartingIfs;//TODO: use selection of pinned fractals
_generatedIFS.Clear();
foreach (var r in _generator.GenerateBatch(options))
{
_generatedIFS.Add(r);
_renderQueue.Enqueue(r);
}
}
public void PinIFS(IFS ifs)
{
_pinnedIFS.Add(ifs);
if (!_thumbnails.ContainsKey(ifs))
_renderQueue.Enqueue(ifs);
}
public void UnpinIFS(IFS ifs) => _pinnedIFS.Remove(ifs);
private readonly object _queueLocker = new();
private readonly IReadOnlyCollection<string> _includeSources;
public void ProcessQueue()
{
lock (_queueLocker)
{
while (_renderQueue.TryDequeue(out IFS ifs))
{
_context.MakeCurrent();
//modify settings to be optimal for thumbnail rendering
var previewIfs = ifs.DeepClone();
previewIfs.ImageResolution = new System.Drawing.Size(200, 200);
previewIfs.Entropy = 1.0 / 100;
previewIfs.Warmup = 0;
//render image after 1 compute pass
_renderer.LoadParams(previewIfs);
_renderer.SetHistogramScaleToDisplay();
_renderer.DispatchCompute();
_renderer.RenderImage();
//read rendered image data
WriteableBitmap wbm = new WriteableBitmap(_renderer.HistogramWidth, _renderer.HistogramHeight, 96, 96, PixelFormats.Bgra32, null);
_context.MakeNoneCurrent();
_renderer.CopyPixelDataToBitmap(wbm.BackBuffer).Wait();
wbm.Freeze();
//save thumbnail image
var thumbnail = new FormatConvertedBitmap(wbm, PixelFormats.Bgr32, null, 0);
thumbnail.Freeze();
_thumbnails[ifs] = thumbnail;
//Dispatcher.CurrentDispatcher.Invoke(() =>
//{
// OnPropertyChanged(nameof(Thumbnails));
//}, DispatcherPriority.ApplicationIdle);
}
}
//cleanup old thumbnails
var removedParams = _thumbnails.Keys.Where(k => !(_pinnedIFS.Contains(k) || _generatedIFS.Contains(k))).ToList();
foreach (var t in removedParams)
_thumbnails.TryRemove(t, out _);
}
}