using System.Collections.Concurrent; using System.Collections.Immutable; using Dashboard.OpenGL; using Dashboard.Pal; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; namespace Dashboard.Drawing.OpenGL.Pal { internal class GLContextBindingsContext(IGLContext context) : IBindingsContext { public IntPtr GetProcAddress(string procName) { return context.GetProcAddress(procName); } } public class GLDeviceContext : DeviceContext { public IGLContext GLContext { get; } public override string DriverName => "Dashboard OpenGL Device Context"; public override string DriverVendor => "Dashboard"; public override Version DriverVersion => new Version(0, 1, 0); public Version GLVersion { get; } public string GLRenderer { get; } public string GLVendor { get; } public ImmutableHashSet Extensions { get; } public Thread RendererThread { get; } = Thread.CurrentThread; public bool IsRenderThread => RendererThread == Thread.CurrentThread; private readonly ConcurrentQueue _beforeDrawActions = new ConcurrentQueue(); private readonly ConcurrentQueue _afterDrawActions = new ConcurrentQueue(); public GLDeviceContext(IGLContext context) { GLContext = context; context.MakeCurrent(); GLLoader.LoadBindings(new GLContextBindingsContext(context)); context.Disposed += Dispose; GL.GetInteger(GetPName.MajorVersion, out int major); GL.GetInteger(GetPName.MinorVersion, out int minor); GLVersion = new Version(major, minor); GLRenderer = GL.GetString(StringName.Renderer) ?? string.Empty; GLVendor = GL.GetString(StringName.Vendor) ?? string.Empty; HashSet extensions = new HashSet(); GL.GetInteger(GetPName.NumExtensions, out int extensionCount); for (uint i = 0; i < extensionCount; i++) { string? ext = GL.GetStringi(StringName.Extensions, i); if (ext != null) extensions.Add(ext); } Extensions = extensions.ToImmutableHashSet(); ExtensionPreload(); } public bool IsGLExtensionAvailable(string name) { return Extensions.Contains(name); } public void AssertGLExtension(string name) { if (IsGLExtensionAvailable(name)) return; throw new NotSupportedException($"The OpenGL extension \"{name}\" is not supported by this context."); } public void InvokeBeforeDraw(Task task) => _beforeDrawActions.Enqueue(task); public void InvokeAfterDraw(Task task) => _afterDrawActions.Enqueue(task); public Task InvokeBeforeDraw(Action action) { Task task = new Task(action); _beforeDrawActions.Enqueue(task); return task; } public Task InvokeAfterDraw(Action action) { Task task = new Task(action); _afterDrawActions.Enqueue(task); return task; } public Task InvokeBeforeDraw(Func function) { Task task = new Task(function); _beforeDrawActions.Enqueue(task); return task; } public Task InvokeAfterDraw(Func function) { Task task = new Task(function); _afterDrawActions.Enqueue(task); return task; } public Task InvokeOnRenderThread(Action action) { if (IsRenderThread) { action(); return Task.CompletedTask; } return InvokeBeforeDraw(action); } public Task InvokeOnRenderThread(Func function) { return IsRenderThread ? Task.FromResult(function()) : InvokeBeforeDraw(function); } public override void Begin() { base.Begin(); GLContext.MakeCurrent(); while (_beforeDrawActions.TryDequeue(out Task? action)) { action.RunSynchronously(); } } public override void End() { base.End(); while (_afterDrawActions.TryDequeue(out Task? action)) { action.RunSynchronously(); } } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (isDisposing) { GLContext.Disposed -= Dispose; } } } }