using System.Runtime.CompilerServices; using Dashboard.Drawing; using OpenTK.Graphics.OpenGL; namespace Dashboard.OpenGL { public class GLBuffer(GLDeviceContext dc, BufferAccessPattern pattern, int handle, long size, long offset) : IBuffer { // TODO: At the moment, this class does no pooling. Convert it to a pool for better API usage. public BufferAccessPattern AccessPattern { get; } = pattern; public long Size { get; private set; } = size; public long Offset { get; private set; } = offset; public int Handle { get; private set; } public void Reallocate(long newSize) { GL.CreateBuffer(out int handle); GL.BindBuffer(BufferTarget.ArrayBuffer, handle); GL.BufferData(BufferTarget.ArrayBuffer, (nint)newSize, (nint)0, AccessPattern switch { BufferAccessPattern.Stream => BufferUsage.StreamDraw, BufferAccessPattern.Static => BufferUsage.StaticDraw, BufferAccessPattern.Download => BufferUsage.DynamicCopy, BufferAccessPattern.Upload => BufferUsage.DynamicCopy, _ => BufferUsage.DynamicDraw, }); if (Handle != -1) GL.DeleteBuffer(Handle); Handle = handle; Size = newSize; Offset = 0; } public unsafe Span Map(long offset, int count = -1) where T : unmanaged { ArgumentOutOfRangeException.ThrowIfNegative(offset); if (count < 0) count = (int)(Size / Unsafe.SizeOf()); long absOffset = Offset + offset; long absCount = count * Unsafe.SizeOf(); ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + absCount, Size); GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); nint ptr = (nint)GL.MapBuffer(BufferTarget.ArrayBuffer, BufferAccess.ReadWrite); return new Span((T*)(ptr + absOffset), count); } public void Unmap() { GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); GL.UnmapBuffer(BufferTarget.ArrayBuffer); } public void Read(long offset, Span span) where T : unmanaged { long absCount = span.Length * Unsafe.SizeOf(); ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + absCount, Size); GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); GL.GetBufferSubData(BufferTarget.ArrayBuffer, (nint)(Offset + offset), (nint)absCount, span); } public void Write(long offset, ReadOnlySpan span) where T : unmanaged { long absCount = span.Length * Unsafe.SizeOf(); ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + absCount, Size); GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); GL.BufferSubData(BufferTarget.ArrayBuffer, (nint)(Offset + offset), (nint)absCount, span); } public void Dispose() { if (Handle == -1) return; dc.Collector.DeleteBufffer(Handle); Handle = -1; GC.SuppressFinalize(this); } public static GLBuffer Create(GLDeviceContext dc, BufferAccessPattern pattern, long size) { GLBuffer buffer = new GLBuffer(dc, pattern, -1, 0, 0); buffer.Reallocate(size); return buffer; } } }