fix(rendering): use deferAfterAllFrameFences for bone destruction

destroyInstanceBones loops over both frame slots (i=0,1), deferring
both boneSet[0] and boneSet[1] to the current frame's fence. When
currentFrame=0, boneSet[1] is freed after only slot 0's fence completes
while slot 1's command buffer may still be using it.

Switch both M2Renderer and CharacterRenderer bone destruction from
deferAfterFrameFence to deferAfterAllFrameFences to ensure all
in-flight frames have completed before freeing cross-slot resources.
This commit is contained in:
Kelsi 2026-04-03 19:54:54 -07:00
parent 3ac8c4d95f
commit b092bc2e90
2 changed files with 7 additions and 4 deletions

View file

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

View file

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