namespace AcDream.App.Rendering.Wb; internal readonly record struct MeshBufferRange(int Offset, int Length) { public int End => checked(Offset + Length); } /// /// Best-fit allocator for one element-addressed GPU buffer. Released ranges /// are coalesced immediately so ObjectMeshManager eviction returns storage to /// the shared modern-rendering buffers instead of only dropping cache keys. /// internal sealed class ContiguousRangeAllocator { private readonly List _free = new(); public ContiguousRangeAllocator(int capacity) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity); Capacity = capacity; _free.Add(new MeshBufferRange(0, capacity)); } public int Capacity { get; private set; } public int Used { get; private set; } public int HighWaterMark { get; private set; } public int Free => Capacity - Used; public int LargestFreeRange => _free.Count == 0 ? 0 : _free.Max(range => range.Length); public int TrailingFreeLength => _free.Count != 0 && _free[^1].End == Capacity ? _free[^1].Length : 0; public bool TryAllocate(int length, out MeshBufferRange allocation) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); int bestIndex = -1; int bestLength = int.MaxValue; for (int i = 0; i < _free.Count; i++) { int candidateLength = _free[i].Length; if (candidateLength >= length && candidateLength < bestLength) { bestIndex = i; bestLength = candidateLength; if (candidateLength == length) break; } } if (bestIndex < 0) { allocation = default; return false; } MeshBufferRange free = _free[bestIndex]; allocation = new MeshBufferRange(free.Offset, length); if (free.Length == length) _free.RemoveAt(bestIndex); else _free[bestIndex] = new MeshBufferRange(free.Offset + length, free.Length - length); Used = checked(Used + length); HighWaterMark = Math.Max(HighWaterMark, allocation.End); return true; } public void Grow(int newCapacity) { if (newCapacity <= Capacity) throw new ArgumentOutOfRangeException(nameof(newCapacity)); int oldCapacity = Capacity; Capacity = newCapacity; InsertAndCoalesce(new MeshBufferRange(oldCapacity, newCapacity - oldCapacity)); } /// /// Removes an unused tail after the matching physical GPU buffer has been /// replaced. Live offsets are unchanged, so no render-data rewrite is /// required. /// public void Shrink(int newCapacity) { if (newCapacity <= 0 || newCapacity >= Capacity) throw new ArgumentOutOfRangeException(nameof(newCapacity)); if (newCapacity < HighWaterMark) throw new InvalidOperationException("Cannot trim a GPU arena through a live allocation."); MeshBufferRange tail = _free.Count == 0 ? default : _free[^1]; if (tail.End != Capacity || tail.Offset > newCapacity) throw new InvalidOperationException("The requested GPU arena tail is not wholly free."); if (tail.Offset == newCapacity) _free.RemoveAt(_free.Count - 1); else _free[^1] = new MeshBufferRange(tail.Offset, newCapacity - tail.Offset); Capacity = newCapacity; RecalculateHighWaterMark(); } public void Release(MeshBufferRange allocation) { if (allocation.Length <= 0 || allocation.Offset < 0 || allocation.End > Capacity) { throw new ArgumentOutOfRangeException(nameof(allocation)); } InsertAndCoalesce(allocation); Used = checked(Used - allocation.Length); RecalculateHighWaterMark(); } private void InsertAndCoalesce(MeshBufferRange released) { int index = _free.BinarySearch( released, Comparer.Create((left, right) => left.Offset.CompareTo(right.Offset))); if (index < 0) index = ~index; if (index > 0 && _free[index - 1].End > released.Offset) throw new InvalidOperationException("GPU buffer range was released more than once."); if (index < _free.Count && released.End > _free[index].Offset) throw new InvalidOperationException("GPU buffer range overlaps an existing free range."); int start = released.Offset; int end = released.End; if (index > 0 && _free[index - 1].End == start) { start = _free[index - 1].Offset; _free.RemoveAt(--index); } if (index < _free.Count && end == _free[index].Offset) { end = _free[index].End; _free.RemoveAt(index); } _free.Insert(index, new MeshBufferRange(start, end - start)); } private void RecalculateHighWaterMark() { HighWaterMark = _free.Count != 0 && _free[^1].End == Capacity ? _free[^1].Offset : Capacity; } }