using System.Runtime.InteropServices; using OpenTK.Mathematics; namespace Dashboard.Drawing.OpenGL { 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(); } 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 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); 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))] public struct GradientUniformStruct { [FieldOffset(0 * sizeof(float))] public float Position; [FieldOffset(4 * sizeof(float))] public Vector4 Color; } }