namespace AcDream.App.Rendering.Gpu.Gl; /// /// Pure bump-plus-free-list allocator for the GL texture table (the storage /// buffer of bindless handles at ). /// /// GL-free by design: it knows nothing about bindless handles, retirement /// queues, or the SSBO itself. calls /// to get a slot to write a handle into; /// defers the call to /// through the device's IGpuResourceRetirementQueue /// so a freed slot is never handed back out while a submitted frame could /// still be reading the old handle at that index. /// internal sealed class GlTextureSlotAllocator { private readonly uint _capacity; private readonly Stack _freeList = new(); private uint _nextBumpSlot; public GlTextureSlotAllocator(uint capacity) { ArgumentOutOfRangeException.ThrowIfZero(capacity); _capacity = capacity; } /// Slots currently handed out (bumped or reused) and not yet released. public int LiveCount => (int)_nextBumpSlot - _freeList.Count; public uint Allocate() { if (_freeList.Count > 0) return _freeList.Pop(); if (_nextBumpSlot >= _capacity) { throw new InvalidOperationException( $"The GL texture table is exhausted: {_capacity} slots are all live. " + "Every texture cache/atlas must release slots it no longer needs before " + "registering new ones."); } return _nextBumpSlot++; } /// /// Returns a slot to the free list. Callers must only call this once the /// retirement queue confirms no live frame could still reference the slot. /// public void Release(uint slot) { if (slot >= _nextBumpSlot) { throw new ArgumentOutOfRangeException( nameof(slot), slot, "Cannot release a slot that was never allocated."); } _freeList.Push(slot); } }