diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp index f96ddc8..8738494 100644 --- a/implementations/Crafter.Graphics-Device.cpp +++ b/implementations/Crafter.Graphics-Device.cpp @@ -1010,21 +1010,22 @@ void Device::EnqueueDeletion(VkBuffer buffer, VkDeviceMemory memory) { } void Device::ReclaimDeletions() { - // Free every entry whose retire frame has been reached, compacting the - // survivors down in place. An entry tagged at frame F retires at - // F + framesInFlight: by the time the CPU has begun that many later - // frames (each gated by a per-image fence wait), single-queue submission - // order guarantees all GPU work from frame F is complete. - std::size_t kept = 0; - for (PendingDeletion& entry : deletionQueue) { - if (entry.retireAfter + framesInFlight <= frameCounter) { - vkDestroyBuffer(device, entry.buffer, nullptr); - vkFreeMemory(device, entry.memory, nullptr); - } else { - deletionQueue[kept++] = entry; - } + // Free every entry whose retire frame has been reached. An entry tagged at + // frame F retires at F + framesInFlight: by the time the CPU has begun that + // many later frames (each gated by a per-image fence wait), single-queue + // submission order guarantees all GPU work from frame F is complete. + // + // frameCounter is monotonic, so retireAfter is non-decreasing down the + // queue and the ready entries are a contiguous prefix: pop them off the + // front and stop at the first one still in flight (everything behind it is + // newer, hence also in flight). O(ready), not O(queue). + while (!deletionQueue.empty() && + deletionQueue.front().retireAfter + framesInFlight <= frameCounter) { + const PendingDeletion& entry = deletionQueue.front(); + vkDestroyBuffer(device, entry.buffer, nullptr); + vkFreeMemory(device, entry.memory, nullptr); + deletionQueue.pop_front(); } - deletionQueue.resize(kept); } void Device::DrainDeletions() { diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm index 95f7583..17f77c5 100644 --- a/interfaces/Crafter.Graphics-Device.cppm +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -320,7 +320,11 @@ export namespace Crafter { // Zero until set, which only makes ReclaimDeletions more eager — and it // is never called before a Window (which sets this) starts rendering. inline static std::uint8_t framesInFlight = 0; - inline static std::vector deletionQueue; + // A deque, not a vector: frameCounter is monotonic, so retireAfter is + // non-decreasing down the queue and the entries ready to reclaim are + // always a prefix. ReclaimDeletions pops that prefix off the front in + // O(ready) instead of walking and compacting the whole queue. + inline static std::deque deletionQueue; // Tag {buffer, memory} for deletion after framesInFlight more frames. // A VK_NULL_HANDLE buffer is ignored (nothing to free). static void EnqueueDeletion(VkBuffer buffer, VkDeviceMemory memory);