161 lines
4.7 KiB
C#

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<string> Extensions { get; }
public Thread RendererThread { get; } = Thread.CurrentThread;
public bool IsRenderThread => RendererThread == Thread.CurrentThread;
private readonly ConcurrentQueue<Task> _beforeDrawActions = new ConcurrentQueue<Task>();
private readonly ConcurrentQueue<Task> _afterDrawActions = new ConcurrentQueue<Task>();
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<string> extensions = new HashSet<string>();
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<GLTextureExtension>();
}
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<T> InvokeBeforeDraw<T>(Func<T> function)
{
Task<T> task = new Task<T>(function);
_beforeDrawActions.Enqueue(task);
return task;
}
public Task<T> InvokeAfterDraw<T>(Func<T> function)
{
Task<T> task = new Task<T>(function);
_afterDrawActions.Enqueue(task);
return task;
}
public Task InvokeOnRenderThread(Action action)
{
if (IsRenderThread)
{
action();
return Task.CompletedTask;
}
return InvokeBeforeDraw(action);
}
public Task<T> InvokeOnRenderThread<T>(Func<T> 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;
}
}
}
}