Begin implementing the GL engine.
This commit is contained in:
parent
bb5b87417f
commit
b841dbe6c2
@ -30,7 +30,7 @@ namespace Dashboard
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a linear gradient.
|
/// Represents a linear gradient.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct Gradient : ICollection<GradientStop>, ICloneable
|
public struct Gradient : ICollection<GradientStop>, ICloneable, IEquatable<Gradient>
|
||||||
{
|
{
|
||||||
private readonly List<GradientStop> _stops = new List<GradientStop>();
|
private readonly List<GradientStop> _stops = new List<GradientStop>();
|
||||||
|
|
||||||
@ -199,5 +199,29 @@ namespace Dashboard
|
|||||||
code.Add(item.GetHashCode());
|
code.Add(item.GetHashCode());
|
||||||
return code.ToHashCode();
|
return code.ToHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Equals(Gradient other)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
Type == other.Type &&
|
||||||
|
C0 == other.C0 &&
|
||||||
|
C1 == other.C1 &&
|
||||||
|
_stops.Equals(other._stops);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is Gradient other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(Gradient left, Gradient right)
|
||||||
|
{
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(Gradient left, Gradient right)
|
||||||
|
{
|
||||||
|
return !left.Equals(right);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
77
Dashboard.Drawing.OpenGL/ContextCollector.cs
Normal file
77
Dashboard.Drawing.OpenGL/ContextCollector.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
public class ContextCollector : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ConcurrentQueue<GLObject> _disposedObjects = new ConcurrentQueue<GLObject>();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
while (_disposedObjects.TryDequeue(out GLObject obj))
|
||||||
|
{
|
||||||
|
obj.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteObject(ObjectIdentifier identifier, int handle) => _disposedObjects.Enqueue(new GLObject(identifier, handle));
|
||||||
|
|
||||||
|
public void DeleteTexture(int texture) => DeleteObject(ObjectIdentifier.Texture, texture);
|
||||||
|
public void DeleteBufffer(int buffer) => DeleteObject(ObjectIdentifier.Buffer, buffer);
|
||||||
|
public void DeleteFramebuffer(int framebuffer) => DeleteObject(ObjectIdentifier.Framebuffer, framebuffer);
|
||||||
|
public void DeleteRenderBuffer(int renderbuffer) => DeleteObject(ObjectIdentifier.Renderbuffer, renderbuffer);
|
||||||
|
public void DeleteSampler(int sampler) => DeleteObject(ObjectIdentifier.Sampler, sampler);
|
||||||
|
public void DeleteShader(int shader) => DeleteObject(ObjectIdentifier.Shader, shader);
|
||||||
|
public void DeleteProgram(int program) => DeleteObject(ObjectIdentifier.Program, program);
|
||||||
|
public void DeleteVertexArray(int vertexArray) => DeleteObject(ObjectIdentifier.VertexArray, vertexArray);
|
||||||
|
public void DeleteQuery(int query) => DeleteObject(ObjectIdentifier.Query, query);
|
||||||
|
public void DeleteProgramPipeline(int programPipeline) => DeleteObject(ObjectIdentifier.ProgramPipeline, programPipeline);
|
||||||
|
public void DeleteTransformFeedback(int transformFeedback) => DeleteObject(ObjectIdentifier.TransformFeedback, transformFeedback);
|
||||||
|
|
||||||
|
private readonly record struct GLObject(ObjectIdentifier Type, int Handle)
|
||||||
|
{
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case ObjectIdentifier.Texture:
|
||||||
|
GL.DeleteTexture(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.Buffer:
|
||||||
|
GL.DeleteBuffer(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.Framebuffer:
|
||||||
|
GL.DeleteFramebuffer(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.Renderbuffer:
|
||||||
|
GL.DeleteRenderbuffer(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.Sampler:
|
||||||
|
GL.DeleteSampler(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.Shader:
|
||||||
|
GL.DeleteShader(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.VertexArray:
|
||||||
|
GL.DeleteVertexArray(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.Program:
|
||||||
|
GL.DeleteProgram(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.Query:
|
||||||
|
GL.DeleteQuery(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.ProgramPipeline:
|
||||||
|
GL.DeleteProgramPipeline(Handle);
|
||||||
|
break;
|
||||||
|
case ObjectIdentifier.TransformFeedback:
|
||||||
|
GL.DeleteTransformFeedback(Handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly ContextCollector Global = new ContextCollector();
|
||||||
|
}
|
||||||
|
}
|
134
Dashboard.Drawing.OpenGL/ContextExecutor.cs
Normal file
134
Dashboard.Drawing.OpenGL/ContextExecutor.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
public class ContextExecutor : IDisposable
|
||||||
|
{
|
||||||
|
public GLEngine Engine { get; }
|
||||||
|
public IGLContext Context { get; }
|
||||||
|
public ContextResourcePool ResourcePool { get; }
|
||||||
|
protected bool IsDisposed { get; private set; } = false;
|
||||||
|
|
||||||
|
public bool IsInitialized { get; private set; } = false;
|
||||||
|
|
||||||
|
private int _program = 0;
|
||||||
|
public ContextExecutor(GLEngine engine, IGLContext context)
|
||||||
|
{
|
||||||
|
Engine = engine;
|
||||||
|
Context = context;
|
||||||
|
|
||||||
|
ResourcePool = Engine.ResourcePoolManager.Get(context);
|
||||||
|
ResourcePool.IncrementReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ContextExecutor()
|
||||||
|
{
|
||||||
|
DisposeInvoker(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
if (IsInitialized)
|
||||||
|
return;
|
||||||
|
IsInitialized = true;
|
||||||
|
|
||||||
|
LoadShaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadShaders()
|
||||||
|
{
|
||||||
|
using Stream vsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.default.vert");
|
||||||
|
using Stream fsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.default.frag");
|
||||||
|
int vs = CompileShader(ShaderType.VertexShader, vsource);
|
||||||
|
int fs = CompileShader(ShaderType.FragmentShader, fsource);
|
||||||
|
_program = LinkProgram(vs, fs);
|
||||||
|
GL.DeleteShader(vs);
|
||||||
|
GL.DeleteShader(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(DrawQueue drawqueue) => Draw(drawqueue, new RectangleF(new PointF(0f,0f), Context.FramebufferSize));
|
||||||
|
public virtual void Draw(DrawQueue drawQueue, RectangleF bounds)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void EndFrame()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeInvoker(bool disposing)
|
||||||
|
{
|
||||||
|
if (!IsDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsDisposed = true;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
ContextCollector.Global.DeleteProgram(_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => DisposeInvoker(true);
|
||||||
|
|
||||||
|
private static int CompileShader(ShaderType type, string source)
|
||||||
|
{
|
||||||
|
int shader = GL.CreateShader(type);
|
||||||
|
|
||||||
|
GL.ShaderSource(shader, source);
|
||||||
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
|
int compileStatus = 0;
|
||||||
|
GL.GetShaderi(shader, ShaderParameterName.CompileStatus, out compileStatus);
|
||||||
|
|
||||||
|
if (compileStatus == 0)
|
||||||
|
{
|
||||||
|
GL.GetShaderInfoLog(shader, out string log);
|
||||||
|
GL.DeleteShader(shader);
|
||||||
|
throw new Exception($"{type} Shader compilation failed: " + log);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CompileShader(ShaderType type, Stream stream)
|
||||||
|
{
|
||||||
|
using StreamReader reader = new StreamReader(stream, leaveOpen: true);
|
||||||
|
|
||||||
|
return CompileShader(type, reader.ReadToEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int LinkProgram(int s1, int s2)
|
||||||
|
{
|
||||||
|
int program = GL.CreateProgram();
|
||||||
|
|
||||||
|
GL.AttachShader(program, s1);
|
||||||
|
GL.AttachShader(program, s2);
|
||||||
|
|
||||||
|
GL.LinkProgram(program);
|
||||||
|
|
||||||
|
int linkStatus = 0;
|
||||||
|
GL.GetProgrami(program, ProgramProperty.LinkStatus, out linkStatus);
|
||||||
|
if (linkStatus == 0)
|
||||||
|
{
|
||||||
|
GL.GetProgramInfoLog(program, out string log);
|
||||||
|
GL.DeleteProgram(program);
|
||||||
|
throw new Exception("Shader program linking failed: " + log);
|
||||||
|
}
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream FetchEmbeddedResource(string name)
|
||||||
|
{
|
||||||
|
return typeof(ContextExecutor).Assembly.GetManifestResourceStream(name)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
Dashboard.Drawing.OpenGL/ContextResourcePool.cs
Normal file
81
Dashboard.Drawing.OpenGL/ContextResourcePool.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
public class ContextResourcePoolManager
|
||||||
|
{
|
||||||
|
private readonly Dictionary<IGLContext, ContextResourcePool> _unique = new Dictionary<IGLContext, ContextResourcePool>();
|
||||||
|
private readonly Dictionary<int, ContextResourcePool> _shared = new Dictionary<int, ContextResourcePool>();
|
||||||
|
|
||||||
|
public ContextResourcePool Get(IGLContext context)
|
||||||
|
{
|
||||||
|
if (context.ContextGroup == -1)
|
||||||
|
{
|
||||||
|
if (!_unique.TryGetValue(context, out ContextResourcePool? pool))
|
||||||
|
{
|
||||||
|
pool = new ContextResourcePool(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_shared.TryGetValue(context.ContextGroup, out ContextResourcePool? pool))
|
||||||
|
{
|
||||||
|
pool = new ContextResourcePool(this, context.ContextGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Disposed(ContextResourcePool pool)
|
||||||
|
{
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContextResourcePool : IGLDisposable, IArc
|
||||||
|
{
|
||||||
|
private int _references = 0;
|
||||||
|
|
||||||
|
public ContextResourcePoolManager Manager { get; }
|
||||||
|
public IGLContext? Context { get; private set; } = null;
|
||||||
|
public int ContextGroup { get; private set; } = -1;
|
||||||
|
public int References => _references;
|
||||||
|
|
||||||
|
internal ContextResourcePool(ContextResourcePoolManager manager, int contextGroup)
|
||||||
|
{
|
||||||
|
Manager = manager;
|
||||||
|
ContextGroup = contextGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ContextResourcePool(ContextResourcePoolManager manager, IGLContext context)
|
||||||
|
{
|
||||||
|
Manager = manager;
|
||||||
|
Context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ContextResourcePool()
|
||||||
|
{
|
||||||
|
Dispose(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Dispose(true, false);
|
||||||
|
|
||||||
|
public void Dispose(bool safeExit) => Dispose(safeExit, true);
|
||||||
|
|
||||||
|
private void Dispose(bool safeExit, bool disposing)
|
||||||
|
{
|
||||||
|
Manager.Disposed(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementReference()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _references);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DecrementReference()
|
||||||
|
{
|
||||||
|
return Interlocked.Decrement(ref _references) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
Dashboard.Drawing.OpenGL/Dashboard.Drawing.OpenGL.csproj
Normal file
25
Dashboard.Drawing.OpenGL/Dashboard.Drawing.OpenGL.csproj
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="OpenTK.Graphics" Version="5.0.0-pre.13" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="default.frag" />
|
||||||
|
<EmbeddedResource Include="default.frag" />
|
||||||
|
<None Remove="default.vert" />
|
||||||
|
<EmbeddedResource Include="default.vert" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
36
Dashboard.Drawing.OpenGL/GLEngine.cs
Normal file
36
Dashboard.Drawing.OpenGL/GLEngine.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
public class GLEngine
|
||||||
|
{
|
||||||
|
private readonly Dictionary<IGLContext, ContextExecutor> _executors = new Dictionary<IGLContext, ContextExecutor>();
|
||||||
|
|
||||||
|
public bool IsInitialized { get; private set; } = false;
|
||||||
|
public ContextResourcePoolManager ResourcePoolManager { get; private set; } = new ContextResourcePoolManager();
|
||||||
|
|
||||||
|
public void Initialize(IBindingsContext? bindingsContext = null)
|
||||||
|
{
|
||||||
|
if (IsInitialized)
|
||||||
|
return;
|
||||||
|
IsInitialized = true;
|
||||||
|
|
||||||
|
if (bindingsContext != null)
|
||||||
|
GLLoader.LoadBindings(bindingsContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContextExecutor GetExecutor(IGLContext glContext)
|
||||||
|
{
|
||||||
|
if (!_executors.TryGetValue(glContext, out ContextExecutor? executor))
|
||||||
|
{
|
||||||
|
executor = new ContextExecutor(this, glContext);
|
||||||
|
executor.Initialize();
|
||||||
|
|
||||||
|
_executors.Add(glContext, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
Dashboard.Drawing.OpenGL/GradientUniformBuffer.cs
Normal file
68
Dashboard.Drawing.OpenGL/GradientUniformBuffer.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
public class GradientUniformBuffer
|
||||||
|
{
|
||||||
|
private int _top = 0;
|
||||||
|
private readonly MappableBuffer<GradientUniformStruct> _buffer = new MappableBuffer<GradientUniformStruct>();
|
||||||
|
private readonly Dictionary<Gradient, Entry> _entries = new Dictionary<Gradient, Entry>();
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_buffer.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry InternGradient(Gradient gradient)
|
||||||
|
{
|
||||||
|
if (_entries.TryGetValue(gradient, out Entry entry))
|
||||||
|
return entry;
|
||||||
|
|
||||||
|
int count = gradient.Count;
|
||||||
|
int offset = _top;
|
||||||
|
_top += count;
|
||||||
|
|
||||||
|
_buffer.EnsureCapacity(_top);
|
||||||
|
_buffer.Map();
|
||||||
|
Span<GradientUniformStruct> span = _buffer.AsSpan()[offset.._top];
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
GradientStop stop = gradient[i];
|
||||||
|
span[i] = new GradientUniformStruct()
|
||||||
|
{
|
||||||
|
Position = stop.Position,
|
||||||
|
Color = new Vector4(
|
||||||
|
stop.Color.R / 255f,
|
||||||
|
stop.Color.G / 255f,
|
||||||
|
stop.Color.B / 255f,
|
||||||
|
stop.Color.A / 255f),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = new Entry(offset, count);
|
||||||
|
_entries.Add(gradient, entry);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_entries.Clear();
|
||||||
|
_top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record struct Entry(int Offset, int Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(float))]
|
||||||
|
public struct GradientUniformStruct
|
||||||
|
{
|
||||||
|
[FieldOffset(0 * sizeof(float))]
|
||||||
|
public float Position;
|
||||||
|
|
||||||
|
[FieldOffset(4 * sizeof(float))]
|
||||||
|
public Vector4 Color;
|
||||||
|
}
|
||||||
|
}
|
24
Dashboard.Drawing.OpenGL/IArc.cs
Normal file
24
Dashboard.Drawing.OpenGL/IArc.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Atomic reference counter.
|
||||||
|
/// </summary>
|
||||||
|
public interface IArc : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The number of references to this.
|
||||||
|
/// </summary>
|
||||||
|
int References { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increment the number of references.
|
||||||
|
/// </summary>
|
||||||
|
void IncrementReference();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decrement the number of references.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if this was the last reference.</returns>
|
||||||
|
bool DecrementReference();
|
||||||
|
}
|
||||||
|
}
|
26
Dashboard.Drawing.OpenGL/IGLContext.cs
Normal file
26
Dashboard.Drawing.OpenGL/IGLContext.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for GL context operations
|
||||||
|
/// </summary>
|
||||||
|
public interface IGLContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The associated group for context sharing.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>-1 assigns no group.</remarks>
|
||||||
|
public int ContextGroup { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the framebuffer in pixels.
|
||||||
|
/// </summary>
|
||||||
|
public Size FramebufferSize { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the context is disposed.
|
||||||
|
/// </summary>
|
||||||
|
event Action Disposed;
|
||||||
|
}
|
||||||
|
}
|
16
Dashboard.Drawing.OpenGL/IGLDisposable.cs
Normal file
16
Dashboard.Drawing.OpenGL/IGLDisposable.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface much like <see cref="IDisposable"/> except GL resources are dropped.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The main reason this interface exists is that you need a way to differentiate
|
||||||
|
/// when a context is deleted, versus when the context is to remain present.
|
||||||
|
/// </remarks>
|
||||||
|
public interface IGLDisposable : IDisposable
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="IDisposable.Dispose"/>
|
||||||
|
/// <param name="safeExit">Set to true to spend the time clearing out GL objects.</param>
|
||||||
|
void Dispose(bool safeExit);
|
||||||
|
}
|
||||||
|
}
|
118
Dashboard.Drawing.OpenGL/MappableBuffer.cs
Normal file
118
Dashboard.Drawing.OpenGL/MappableBuffer.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
public class MappableBuffer<T> : IDisposable
|
||||||
|
{
|
||||||
|
public int Handle { get; private set; } = 0;
|
||||||
|
public int Capacity { get; set; } = BASE_CAPACITY;
|
||||||
|
public IntPtr Pointer { get; private set; } = IntPtr.Zero;
|
||||||
|
|
||||||
|
private bool _isDisposed = false;
|
||||||
|
private const int BASE_CAPACITY = 4 << 10; // 4 KiB
|
||||||
|
private const int MAX_INCREMENT = 4 << 20; // 4 MiB
|
||||||
|
|
||||||
|
~MappableBuffer()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
Handle = GL.GenBuffer();
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
|
GL.BufferData(BufferTarget.ArrayBuffer, Capacity, IntPtr.Zero, BufferUsage.DynamicDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureCapacity(int count)
|
||||||
|
{
|
||||||
|
if (count < 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
|
|
||||||
|
if (Capacity >= count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AssertInitialized();
|
||||||
|
Unmap();
|
||||||
|
|
||||||
|
int sz = Unsafe.SizeOf<T>();
|
||||||
|
int oldsize = Capacity * sz;
|
||||||
|
int request = count * sz;
|
||||||
|
int newsize;
|
||||||
|
if (request > MAX_INCREMENT)
|
||||||
|
{
|
||||||
|
newsize = ((request + MAX_INCREMENT - 1) / MAX_INCREMENT) * MAX_INCREMENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newsize = checked((int)BitOperations.RoundUpToPowerOf2((ulong)request));
|
||||||
|
}
|
||||||
|
|
||||||
|
int dest = GL.GenBuffer();
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.CopyWriteBuffer, dest);
|
||||||
|
GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle);
|
||||||
|
|
||||||
|
GL.BufferData(BufferTarget.CopyWriteBuffer, newsize, IntPtr.Zero, BufferUsage.DynamicDraw);
|
||||||
|
GL.CopyBufferSubData(CopyBufferSubDataTarget.CopyReadBuffer, CopyBufferSubDataTarget.CopyWriteBuffer, 0, 0, oldsize);
|
||||||
|
|
||||||
|
GL.DeleteBuffer(Handle);
|
||||||
|
Handle = dest;
|
||||||
|
Capacity = newsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void Map()
|
||||||
|
{
|
||||||
|
if (Pointer != IntPtr.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AssertInitialized();
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
|
Pointer = (IntPtr)GL.MapBuffer(BufferTarget.ArrayBuffer, BufferAccess.ReadWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unmap()
|
||||||
|
{
|
||||||
|
if (Pointer == IntPtr.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AssertInitialized();
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
|
GL.UnmapBuffer(BufferTarget.ArrayBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe Span<T> AsSpan()
|
||||||
|
{
|
||||||
|
if (Pointer == IntPtr.Zero)
|
||||||
|
throw new InvalidOperationException("The buffer is not currently mapped.");
|
||||||
|
|
||||||
|
AssertInitialized();
|
||||||
|
|
||||||
|
return new Span<T>(Pointer.ToPointer(), Capacity / Unsafe.SizeOf<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AssertInitialized()
|
||||||
|
{
|
||||||
|
if (Handle == 0)
|
||||||
|
throw new InvalidOperationException("The buffer is not initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
return;
|
||||||
|
_isDisposed = true;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
ContextCollector.Global.DeleteBufffer(Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Dispose(true);
|
||||||
|
}
|
||||||
|
}
|
51
Dashboard.Drawing.OpenGL/TransformStack.cs
Normal file
51
Dashboard.Drawing.OpenGL/TransformStack.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing.OpenGL
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current stack of transformations.
|
||||||
|
/// </summary>
|
||||||
|
public class TransformStack
|
||||||
|
{
|
||||||
|
private Matrix4 _top = Matrix4.Identity;
|
||||||
|
private readonly Stack<Matrix4> _stack = new Stack<Matrix4>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The top-most transform matrix.
|
||||||
|
/// </summary>
|
||||||
|
public ref readonly Matrix4 Top => ref _top;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of matrices in the stack.
|
||||||
|
/// </summary>
|
||||||
|
public int Count => _stack.Count;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Push a transform.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="transform">The transform to push.</param>
|
||||||
|
public void Push(in Matrix4 transform)
|
||||||
|
{
|
||||||
|
_stack.Push(_top);
|
||||||
|
_top = transform * _top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pop a transform.
|
||||||
|
/// </summary>
|
||||||
|
public void Pop()
|
||||||
|
{
|
||||||
|
if (!_stack.TryPop(out _top))
|
||||||
|
_top = Matrix4.Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear the stack of transformations.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_stack.Clear();
|
||||||
|
_top = Matrix4.Identity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
Dashboard.Drawing.OpenGL/default.frag
Normal file
97
Dashboard.Drawing.OpenGL/default.frag
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#version 140
|
||||||
|
|
||||||
|
#define DB_GRADIENT_MAX 16
|
||||||
|
#define DB_COMMAND_MAX 64
|
||||||
|
|
||||||
|
#define CMD_POINT 1
|
||||||
|
#define CMD_LINE 2
|
||||||
|
#define CMD_RECT 3
|
||||||
|
#define CMD_TEXT 4
|
||||||
|
|
||||||
|
#define STRIKE_INSET -1
|
||||||
|
#define STRIKE_CENTER 0
|
||||||
|
#define STRIKE_OUTSET 1
|
||||||
|
|
||||||
|
in vec3 v_v3Position;
|
||||||
|
in vec2 v_v2TexCoords;
|
||||||
|
in int v_iCmdIndex;
|
||||||
|
|
||||||
|
out vec4 f_Color;
|
||||||
|
|
||||||
|
uniform sampler2D txForeground;
|
||||||
|
uniform sampler2D txBackground;
|
||||||
|
uniform sampler2DArray txCharacters;
|
||||||
|
|
||||||
|
struct Gradient_t {
|
||||||
|
float fPosition;
|
||||||
|
float pad0;
|
||||||
|
float pad1;
|
||||||
|
float pad2;
|
||||||
|
vec4 v4Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform GradientBlock
|
||||||
|
{
|
||||||
|
Gradient_t vstGradientStops[DB_GRADIENT_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
vec4 getGradientColor(float position, int index, int count)
|
||||||
|
{
|
||||||
|
position = clamp(position, 0, 1);
|
||||||
|
|
||||||
|
int i0 = 0;
|
||||||
|
float p0 = vstGradientStops[index + i0].fPosition;
|
||||||
|
|
||||||
|
int i1 = count - 1;
|
||||||
|
float p1 = vstGradientStops[index + i1].fPosition;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
float px = vstGradientStops[index + i].fPosition;
|
||||||
|
|
||||||
|
if (px > p0 && px <= position)
|
||||||
|
{
|
||||||
|
p0 = px;
|
||||||
|
i0 = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (px < p1 && px >= position)
|
||||||
|
{
|
||||||
|
p1 = px;
|
||||||
|
i1 = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 c0 = vstGradientStops[index + i0].v4Color;
|
||||||
|
vec4 c1 = vstGradientStops[index + i1].v4Color;
|
||||||
|
|
||||||
|
float l = p1 - p0;
|
||||||
|
float w = (l > 0) ? (position - p0) / (p1 - p0) : 0;
|
||||||
|
|
||||||
|
return mix(c0, c1, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CommandCommon {
|
||||||
|
int iCommand;
|
||||||
|
int iStrikeKind;
|
||||||
|
float fBorderRadius;
|
||||||
|
int _padding;
|
||||||
|
|
||||||
|
int iFgGradientIndex;
|
||||||
|
int iFgGradientCount;
|
||||||
|
int iBgGradientIndex;
|
||||||
|
int iBgGradientCount;
|
||||||
|
|
||||||
|
vec4 v4FillColor;
|
||||||
|
vec4 v4BorderColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform CommandBlock
|
||||||
|
{
|
||||||
|
CommandCommon vstCommands[DB_COMMAND_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
24
Dashboard.Drawing.OpenGL/default.vert
Normal file
24
Dashboard.Drawing.OpenGL/default.vert
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#version 140
|
||||||
|
|
||||||
|
in vec3 a_v3Position;
|
||||||
|
in vec2 a_v2TexCoords;
|
||||||
|
in vec3 a_v3CharCoords;
|
||||||
|
in int a_iCmdIndex;
|
||||||
|
|
||||||
|
out vec3 v_v3Position;
|
||||||
|
out vec2 v_v2TexCoords;
|
||||||
|
out vec3 v_v3CharCoords;
|
||||||
|
flat out int v_iCmdIndex;
|
||||||
|
|
||||||
|
uniform mat4 m4Transforms;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec4 position = vec4(a_v3Position, 1) * m4Transforms;
|
||||||
|
gl_Position = position;
|
||||||
|
v_v3Position = position.xyz/position.w;
|
||||||
|
|
||||||
|
v_v2TexCoords = a_v2TexCoords;
|
||||||
|
v_v3CharCoords = a_v3CharCoords;
|
||||||
|
v_iCmdIndex = a_iCmdIndex;
|
||||||
|
}
|
@ -49,7 +49,7 @@ namespace Dashboard.Drawing
|
|||||||
_resources.Clear();
|
_resources.Clear();
|
||||||
_commands.Clear();
|
_commands.Clear();
|
||||||
_extensions.Clear();
|
_extensions.Clear();
|
||||||
_commandStream.Capacity = 0;
|
_commandStream.SetLength(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int RequireExtension(IDrawExtension extension)
|
public int RequireExtension(IDrawExtension extension)
|
||||||
|
@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dashboard.TestApplication",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Common", "Dashboard.Common\Dashboard.Common.csproj", "{C77CDD2B-2482-45F9-B330-47A52F5F13C0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Common", "Dashboard.Common\Dashboard.Common.csproj", "{C77CDD2B-2482-45F9-B330-47A52F5F13C0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Drawing.OpenGL", "Dashboard.Drawing.OpenGL\Dashboard.Drawing.OpenGL.csproj", "{454198BA-CB95-41C5-A934-B1C8FDA35A6B}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -39,6 +41,10 @@ Global
|
|||||||
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{454198BA-CB95-41C5-A934-B1C8FDA35A6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{454198BA-CB95-41C5-A934-B1C8FDA35A6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{454198BA-CB95-41C5-A934-B1C8FDA35A6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{454198BA-CB95-41C5-A934-B1C8FDA35A6B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -8,7 +8,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Dashboard.Drawing.OpenGL\Dashboard.Drawing.OpenGL.csproj" />
|
||||||
<ProjectReference Include="..\..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
|
<ProjectReference Include="..\..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="OpenTK" Version="5.0.0-pre.13" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,23 +1,131 @@
|
|||||||
using Dashboard.Drawing;
|
using Dashboard.Drawing;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Numerics;
|
using Dashboard.Drawing.OpenGL;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using OpenTK.Platform;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
using TK = OpenTK.Platform.Toolkit;
|
||||||
|
using Vector3 = System.Numerics.Vector3;
|
||||||
|
|
||||||
|
TK.Init(new ToolkitOptions()
|
||||||
|
{
|
||||||
|
ApplicationName = "Dashboard Test Application",
|
||||||
|
Windows = new ToolkitOptions.WindowsOptions()
|
||||||
|
{
|
||||||
|
EnableVisualStyles = true,
|
||||||
|
IsDPIAware = true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
WindowHandle wnd = TK.Window.Create(new OpenGLGraphicsApiHints()
|
||||||
|
{
|
||||||
|
Version = new Version(3, 2),
|
||||||
|
ForwardCompatibleFlag = true,
|
||||||
|
DebugFlag = true,
|
||||||
|
Profile = OpenGLProfile.Core,
|
||||||
|
sRGBFramebuffer = true,
|
||||||
|
|
||||||
|
SwapMethod = ContextSwapMethod.Undefined,
|
||||||
|
|
||||||
|
RedColorBits = 8,
|
||||||
|
GreenColorBits = 8,
|
||||||
|
BlueColorBits = 8,
|
||||||
|
AlphaColorBits = 8,
|
||||||
|
|
||||||
|
SupportTransparentFramebufferX11 = true,
|
||||||
|
});
|
||||||
|
TK.Window.SetTitle(wnd, "Dashboard Test Application");
|
||||||
|
TK.Window.SetMinClientSize(wnd, 300, 200);
|
||||||
|
TK.Window.SetClientSize(wnd, new Vector2i(320, 240));
|
||||||
|
TK.Window.SetBorderStyle(wnd, WindowBorderStyle.ResizableBorder);
|
||||||
|
|
||||||
|
OpenGLContextHandle context = TK.OpenGL.CreateFromWindow(wnd);
|
||||||
|
|
||||||
|
TK.OpenGL.SetCurrentContext(context);
|
||||||
|
TK.OpenGL.SetSwapInterval(1);
|
||||||
|
|
||||||
|
GLLoader.LoadBindings(new Pal2BindingsContext(TK.OpenGL, context));
|
||||||
|
|
||||||
DrawQueue queue = new DrawQueue();
|
DrawQueue queue = new DrawQueue();
|
||||||
|
|
||||||
SolidBrush fg = new SolidBrush(Color.Black);
|
SolidBrush fg = new SolidBrush(Color.Black);
|
||||||
SolidBrush bg = new SolidBrush(Color.White);
|
SolidBrush bg = new SolidBrush(Color.White);
|
||||||
|
bool shouldExit = false;
|
||||||
|
|
||||||
queue.Point(Vector3.Zero, 2f, fg);
|
GLEngine engine = new GLEngine();
|
||||||
queue.Line(Vector3.Zero, Vector3.One * 2, 2f, bg);
|
engine.Initialize();
|
||||||
queue.Rect(Vector3.UnitX, 2 * Vector3.UnitX + Vector3.UnitY, fg, bg, 2f);
|
|
||||||
|
|
||||||
foreach (ICommandFrame frame in queue)
|
GlContext dbGlContext = new GlContext(wnd, context);
|
||||||
|
ContextExecutor executor = engine.GetExecutor(dbGlContext);
|
||||||
|
|
||||||
|
EventQueue.EventRaised += (handle, type, eventArgs) =>
|
||||||
{
|
{
|
||||||
Console.WriteLine("{0}", frame.Command.Name);
|
if (handle != wnd)
|
||||||
|
return;
|
||||||
|
|
||||||
if (frame.HasParameters)
|
switch (type)
|
||||||
Console.WriteLine("Param: {0}", frame.GetParameter());
|
{
|
||||||
|
case PlatformEventType.Close:
|
||||||
|
shouldExit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TK.Window.SetMode(wnd, WindowMode.Normal);
|
||||||
|
|
||||||
|
while (!shouldExit)
|
||||||
|
{
|
||||||
|
TK.Window.ProcessEvents(true);
|
||||||
|
|
||||||
|
TK.Window.GetFramebufferSize(wnd, out Vector2i framebufferSize);
|
||||||
|
|
||||||
|
queue.Clear();
|
||||||
|
queue.Point(Vector3.Zero, 2f, fg);
|
||||||
|
queue.Line(Vector3.Zero, Vector3.One * 2, 2f, bg);
|
||||||
|
queue.Rect(Vector3.UnitX, 2 * Vector3.UnitX + Vector3.UnitY, fg, bg, 2f);
|
||||||
|
|
||||||
|
GL.Viewport(0, 0, framebufferSize.X, framebufferSize.Y);
|
||||||
|
GL.ClearColor(0.3f, 0.3f, 0.3f, 1.0f);
|
||||||
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||||
|
|
||||||
|
executor.Draw(queue);
|
||||||
|
executor.EndFrame();
|
||||||
|
|
||||||
|
TK.OpenGL.SwapBuffers(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugger.Break();
|
class GlContext : IGLContext
|
||||||
|
{
|
||||||
|
public WindowHandle Window { get; }
|
||||||
|
public OpenGLContextHandle Context { get; }
|
||||||
|
public int ContextGroup { get; } = -1;
|
||||||
|
|
||||||
|
public Size FramebufferSize
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
TK.Window.GetFramebufferSize(Window, out Vector2i framebufferSize);
|
||||||
|
return new Size(framebufferSize.X, framebufferSize.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action? Disposed;
|
||||||
|
|
||||||
|
public GlContext(WindowHandle window, OpenGLContextHandle context)
|
||||||
|
{
|
||||||
|
Window = window;
|
||||||
|
Context = context;
|
||||||
|
|
||||||
|
OpenGLContextHandle? shared = TK.OpenGL.GetSharedContext(context);
|
||||||
|
if (shared != null)
|
||||||
|
{
|
||||||
|
ContextGroup = _contexts.IndexOf(shared);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_contexts.Add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly List<OpenGLContextHandle> _contexts = new List<OpenGLContextHandle>();
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user