acdream/src/AcDream.App/Rendering/Wb/TextureAtlasSlotAllocator.cs

56 lines
1.7 KiB
C#

namespace AcDream.App.Rendering.Wb;
/// <summary>
/// Owns the physical layer slots in one texture array. A released texture
/// remains rented until the GPU-retirement callback returns its layer, so
/// <see cref="AvailableCount"/> never advertises a CPU-unreferenced layer
/// that an in-flight draw may still sample.
/// </summary>
internal sealed class TextureAtlasSlotAllocator
{
private readonly bool[] _rented;
private readonly Stack<int> _returned = new();
private int _next;
public TextureAtlasSlotAllocator(int capacity)
{
ArgumentOutOfRangeException.ThrowIfLessThan(capacity, 1);
_rented = new bool[capacity];
}
public int AvailableCount => _returned.Count + (_rented.Length - _next);
public int Capacity => _rented.Length;
public int Rent()
{
int slot;
if (_returned.Count != 0)
{
slot = _returned.Pop();
}
else
{
if (_next == _rented.Length)
throw new InvalidOperationException(
$"Texture atlas has no GPU-safe layer available ({_rented.Length} layers)."
);
slot = _next++;
}
if (_rented[slot])
throw new InvalidOperationException($"Texture atlas layer {slot} was rented twice.");
_rented[slot] = true;
return slot;
}
public void Return(int slot)
{
ArgumentOutOfRangeException.ThrowIfNegative(slot);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(slot, _next);
if (!_rented[slot])
throw new InvalidOperationException($"Texture atlas layer {slot} was returned twice.");
_rented[slot] = false;
_returned.Push(slot);
}
}