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);
}
}