using System; using System.Collections.Generic; namespace AcDream.Core.Terrain; /// /// Pure-CPU slot allocator for the terrain modern dispatcher's global VBO/EBO. /// One slot = one landblock's worth of mesh data (384 verts + 384 indices). /// Uses a FIFO free-list for slot recycling and a monotonic counter for /// first-time growth, mirroring WorldBuilder's TerrainRenderManager pattern. /// All bookkeeping is CPU-side; the GPU buffer growth itself is performed /// by TerrainModernRenderer when sets needsGrow=true. /// public sealed class TerrainSlotAllocator { private readonly Queue _freeSlots = new(); private readonly HashSet _liveSlots = new(); private int _nextFreeSlot; private int _capacity; public TerrainSlotAllocator(int initialCapacity = 64) { if (initialCapacity <= 0) throw new ArgumentOutOfRangeException(nameof(initialCapacity), "must be > 0"); _capacity = initialCapacity; } /// Current capacity in slots. Growable via . public int Capacity => _capacity; /// Slots currently in use (allocated minus freed). public int LoadedCount => _liveSlots.Count; /// /// Allocate a slot index. Reuses a freed slot via FIFO if available, /// otherwise hands out the next monotonic index. Sets /// to true when the returned slot index is /// at or beyond current capacity — caller must /// before using the slot. /// public int Allocate(out bool needsGrow) { int slot; if (_freeSlots.TryDequeue(out var freed)) { slot = freed; } else { slot = _nextFreeSlot++; } _liveSlots.Add(slot); needsGrow = slot >= _capacity; return slot; } /// /// Return a slot to the free list. Throws if the slot wasn't currently /// allocated (catches double-free bugs). /// public void Free(int slot) { if (!_liveSlots.Remove(slot)) throw new InvalidOperationException( $"Slot {slot} was not allocated (double-free or unknown slot)."); _freeSlots.Enqueue(slot); } /// Update capacity counter after the caller has grown the GPU buffers. public void GrowTo(int newCapacity) { if (newCapacity < _capacity) throw new ArgumentException("Capacity can only grow", nameof(newCapacity)); _capacity = newCapacity; } }