diff --git a/Dashboard.Drawing.OpenGL/ContextResourcePool.cs b/Dashboard.Drawing.OpenGL/ContextResourcePool.cs index ddfc556..9e85daa 100644 --- a/Dashboard.Drawing.OpenGL/ContextResourcePool.cs +++ b/Dashboard.Drawing.OpenGL/ContextResourcePool.cs @@ -12,6 +12,7 @@ namespace Dashboard.Drawing.OpenGL if (!_unique.TryGetValue(context, out ContextResourcePool? pool)) { pool = new ContextResourcePool(this, context); + _unique.Add(context, pool); } return pool; @@ -21,6 +22,7 @@ namespace Dashboard.Drawing.OpenGL if (!_shared.TryGetValue(context.ContextGroup, out ContextResourcePool? pool)) { pool = new ContextResourcePool(this, context.ContextGroup); + _shared.Add(context.ContextGroup, pool); } return pool; @@ -36,11 +38,14 @@ namespace Dashboard.Drawing.OpenGL public class ContextResourcePool : IGLDisposable, IArc { private int _references = 0; + private bool _isDisposed = false; + private readonly Dictionary _managers = new Dictionary(); public ContextResourcePoolManager Manager { get; } public IGLContext? Context { get; private set; } = null; public int ContextGroup { get; private set; } = -1; public int References => _references; + public ContextCollector Collector { get; } = new ContextCollector(); internal ContextResourcePool(ContextResourcePoolManager manager, int contextGroup) { @@ -54,6 +59,23 @@ namespace Dashboard.Drawing.OpenGL Context = context; } + public T GetResourceManager(bool init = true) where T : IResourceManager, new() + { + int index = ManagerAtom.Atom; + + if (!_managers.TryGetValue(index, out IResourceManager? resourceClass)) + { + _managers[index] = resourceClass = new T(); + } + + if (init && resourceClass is IInitializer initializer) + { + initializer.Initialize(); + } + + return (T)resourceClass; + } + ~ContextResourcePool() { Dispose(true, false); @@ -65,7 +87,23 @@ namespace Dashboard.Drawing.OpenGL private void Dispose(bool safeExit, bool disposing) { + if (_isDisposed) + return; + _isDisposed = true; + Manager.Disposed(this); + + if (disposing) + { + foreach ((int _, IResourceManager manager) in _managers) + { + if (manager is IGLDisposable glDisposable) + glDisposable.Dispose(safeExit); + else if (manager is IDisposable disposable) + disposable.Dispose(); + } + GC.SuppressFinalize(this); + } } public void IncrementReference() @@ -77,5 +115,17 @@ namespace Dashboard.Drawing.OpenGL { return Interlocked.Decrement(ref _references) == 0; } + + + private class ManagerAtom + { + private static int _counter = -1; + + protected static int Acquire() => Interlocked.Increment(ref _counter); + } + private class ManagerAtom : ManagerAtom where T : IResourceManager + { + public static int Atom { get; } = Acquire(); + } } } diff --git a/Dashboard.Drawing.OpenGL/GradientUniformBuffer.cs b/Dashboard.Drawing.OpenGL/GradientUniformBuffer.cs index 478616d..55628f5 100644 --- a/Dashboard.Drawing.OpenGL/GradientUniformBuffer.cs +++ b/Dashboard.Drawing.OpenGL/GradientUniformBuffer.cs @@ -3,14 +3,22 @@ using OpenTK.Mathematics; namespace Dashboard.Drawing.OpenGL { - public class GradientUniformBuffer + public class GradientUniformBuffer : IInitializer, IGLDisposable, IResourceManager { + private bool _isDisposed; private int _top = 0; private readonly MappableBuffer _buffer = new MappableBuffer(); private readonly Dictionary _entries = new Dictionary(); + public bool IsInitialized { get; private set; } = false; + public void Initialize() { + if (IsInitialized) + return; + + IsInitialized = true; + _buffer.Initialize(); } @@ -54,6 +62,19 @@ namespace Dashboard.Drawing.OpenGL } public record struct Entry(int Offset, int Count); + + public void Dispose() => Dispose(true); + + public void Dispose(bool safeExit) + { + if (_isDisposed) + return; + _isDisposed = true; + + _buffer.Dispose(safeExit); + } + + string IResourceManager.Name { get; } = nameof(GradientUniformBuffer); } [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(float))] diff --git a/Dashboard.Drawing.OpenGL/IInitializer.cs b/Dashboard.Drawing.OpenGL/IInitializer.cs new file mode 100644 index 0000000..c6d5702 --- /dev/null +++ b/Dashboard.Drawing.OpenGL/IInitializer.cs @@ -0,0 +1,9 @@ +namespace Dashboard.Drawing.OpenGL +{ + public interface IInitializer + { + bool IsInitialized { get; } + + void Initialize(); + } +} diff --git a/Dashboard.Drawing.OpenGL/IResourceManager.cs b/Dashboard.Drawing.OpenGL/IResourceManager.cs new file mode 100644 index 0000000..fe73b58 --- /dev/null +++ b/Dashboard.Drawing.OpenGL/IResourceManager.cs @@ -0,0 +1,7 @@ +namespace Dashboard.Drawing.OpenGL +{ + public interface IResourceManager + { + public string Name { get; } + } +} diff --git a/Dashboard.Drawing.OpenGL/MappableBuffer.cs b/Dashboard.Drawing.OpenGL/MappableBuffer.cs index 8081dea..55a9082 100644 --- a/Dashboard.Drawing.OpenGL/MappableBuffer.cs +++ b/Dashboard.Drawing.OpenGL/MappableBuffer.cs @@ -4,23 +4,28 @@ using OpenTK.Graphics.OpenGL; namespace Dashboard.Drawing.OpenGL { - public class MappableBuffer : IDisposable + public class MappableBuffer : IInitializer, IGLDisposable where T : struct { public int Handle { get; private set; } = 0; public int Capacity { get; set; } = BASE_CAPACITY; public IntPtr Pointer { get; private set; } = IntPtr.Zero; + public bool IsInitialized => Handle != 0; + 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); + Dispose(true, false); } public void Initialize() { + if (IsInitialized) + return; + Handle = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); GL.BufferData(BufferTarget.ArrayBuffer, Capacity, IntPtr.Zero, BufferUsage.DynamicDraw); @@ -31,9 +36,14 @@ namespace Dashboard.Drawing.OpenGL if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (Capacity >= count) + if (Capacity > count) return; + SetSize(count, false); + } + + public void SetSize(int count, bool clear = false) + { AssertInitialized(); Unmap(); @@ -41,6 +51,10 @@ namespace Dashboard.Drawing.OpenGL int oldsize = Capacity * sz; int request = count * sz; int newsize; + + if (request < BASE_CAPACITY) + request = BASE_CAPACITY; + if (request > MAX_INCREMENT) { newsize = ((request + MAX_INCREMENT - 1) / MAX_INCREMENT) * MAX_INCREMENT; @@ -52,15 +66,23 @@ namespace Dashboard.Drawing.OpenGL int dest = GL.GenBuffer(); - GL.BindBuffer(BufferTarget.CopyWriteBuffer, dest); - GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle); + if (clear) + { + GL.BindBuffer(BufferTarget.ArrayBuffer, dest); + GL.BufferData(BufferTarget.ArrayBuffer, newsize, IntPtr.Zero, BufferUsage.DynamicDraw); + } + else + { + 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.BufferData(BufferTarget.CopyWriteBuffer, newsize, IntPtr.Zero, BufferUsage.DynamicDraw); + GL.CopyBufferSubData(CopyBufferSubDataTarget.CopyReadBuffer, CopyBufferSubDataTarget.CopyWriteBuffer, 0, 0, Math.Min(newsize, oldsize)); + } GL.DeleteBuffer(Handle); Handle = dest; - Capacity = newsize; + Capacity = newsize / Unsafe.SizeOf(); } public unsafe void Map() @@ -83,6 +105,7 @@ namespace Dashboard.Drawing.OpenGL GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); GL.UnmapBuffer(BufferTarget.ArrayBuffer); + Pointer = IntPtr.Zero; } public unsafe Span AsSpan() @@ -92,7 +115,7 @@ namespace Dashboard.Drawing.OpenGL AssertInitialized(); - return new Span(Pointer.ToPointer(), Capacity / Unsafe.SizeOf()); + return new Span(Pointer.ToPointer(), Capacity); } private void AssertInitialized() @@ -101,7 +124,7 @@ namespace Dashboard.Drawing.OpenGL throw new InvalidOperationException("The buffer is not initialized."); } - private void Dispose(bool disposing) + private void Dispose(bool safeExit, bool disposing) { if (_isDisposed) return; @@ -110,9 +133,35 @@ namespace Dashboard.Drawing.OpenGL if (disposing) GC.SuppressFinalize(this); - ContextCollector.Global.DeleteBufffer(Handle); + if (safeExit) + ContextCollector.Global.DeleteBufffer(Handle); } - public void Dispose() => Dispose(true); + public void Dispose() => Dispose(true, true); + public void Dispose(bool safeExit) => Dispose(safeExit, true); + } + + public class MappableBumpAllocator : MappableBuffer where T : struct + { + private int _top = 0; + private int _previousTop = 0; + + public ref T Take(out int index) + { + index = _top; + EnsureCapacity(++_top); + Map(); + + return ref AsSpan()[index]; + } + + public ref T Take() => ref Take(out _); + + public void Clear() + { + SetSize(0, true); + _previousTop = _top; + _top = 0; + } } }