diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index fb04ca7b..12765800 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -520,8 +520,10 @@ void CharacterRenderer::destroyInstanceBones(CharacterInstance& inst, bool defer vmaDestroyBuffer(alloc, boneBuf, boneAlloc); } } else if (boneSet != VK_NULL_HANDLE || boneBuf) { + // Loop destroys bone sets for ALL frame slots — the other slot's + // command buffer may still be in flight. Wait for all fences. VkDescriptorPool pool = boneDescPool_; - vkCtx_->deferAfterFrameFence([device, alloc, pool, boneSet, boneBuf, boneAlloc]() { + vkCtx_->deferAfterAllFrameFences([device, alloc, pool, boneSet, boneBuf, boneAlloc]() { if (boneSet != VK_NULL_HANDLE && pool != VK_NULL_HANDLE) { VkDescriptorSet s = boneSet; vkFreeDescriptorSets(device, pool, 1, &s); diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index 456acb7e..d87a6844 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -867,10 +867,11 @@ void M2Renderer::destroyInstanceBones(M2Instance& inst, bool defer) { vmaDestroyBuffer(alloc, boneBuf, boneAlloc); } } else if (boneSet != VK_NULL_HANDLE || boneBuf) { - // Deferred destruction — previous frame's command buffer may still - // reference these descriptor sets and buffers. + // Deferred destruction — the loop destroys bone sets for ALL frame + // slots, so the other slot's command buffer may still be in flight. + // Must wait for all fences, not just the current frame's. VkDescriptorPool pool = boneDescPool_; - vkCtx_->deferAfterFrameFence([device, alloc, pool, boneSet, boneBuf, boneAlloc]() { + vkCtx_->deferAfterAllFrameFences([device, alloc, pool, boneSet, boneBuf, boneAlloc]() { if (boneSet != VK_NULL_HANDLE) { VkDescriptorSet s = boneSet; vkFreeDescriptorSets(device, pool, 1, &s);