Work on resource classes and generic interfaces.

This commit is contained in:
H. Utku Maden 2025-01-18 20:43:23 +03:00
parent 8ce1329dfc
commit 5014d218e2
5 changed files with 149 additions and 13 deletions

@ -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<int, IResourceManager> _managers = new Dictionary<int, IResourceManager>();
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<T>(bool init = true) where T : IResourceManager, new()
{
int index = ManagerAtom<T>.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<T> : ManagerAtom where T : IResourceManager
{
public static int Atom { get; } = Acquire();
}
}
}

@ -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<GradientUniformStruct> _buffer = new MappableBuffer<GradientUniformStruct>();
private readonly Dictionary<Gradient, Entry> _entries = new Dictionary<Gradient, Entry>();
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))]

@ -0,0 +1,9 @@
namespace Dashboard.Drawing.OpenGL
{
public interface IInitializer
{
bool IsInitialized { get; }
void Initialize();
}
}

@ -0,0 +1,7 @@
namespace Dashboard.Drawing.OpenGL
{
public interface IResourceManager
{
public string Name { get; }
}
}

@ -4,23 +4,28 @@ using OpenTK.Graphics.OpenGL;
namespace Dashboard.Drawing.OpenGL
{
public class MappableBuffer<T> : IDisposable
public class MappableBuffer<T> : 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();
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.CopyBufferSubData(CopyBufferSubDataTarget.CopyReadBuffer, CopyBufferSubDataTarget.CopyWriteBuffer, 0, 0, Math.Min(newsize, oldsize));
}
GL.DeleteBuffer(Handle);
Handle = dest;
Capacity = newsize;
Capacity = newsize / Unsafe.SizeOf<T>();
}
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<T> AsSpan()
@ -92,7 +115,7 @@ namespace Dashboard.Drawing.OpenGL
AssertInitialized();
return new Span<T>(Pointer.ToPointer(), Capacity / Unsafe.SizeOf<T>());
return new Span<T>(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);
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<T> : MappableBuffer<T> 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;
}
}
}