fix(rendering): defer model buffer destruction and per-frame FXAA descriptors

CharacterRenderer::destroyModelGPU now defers vertex/index buffer
destruction when replacing models mid-stream, preventing use-after-free
on AMD RADV. FXAA descriptor sets are now per-frame to eliminate
write-read races between in-flight command buffers. Water reflection
descriptor update narrowed to current frame only.
This commit is contained in:
Kelsi 2026-04-03 19:17:55 -07:00
parent e19bf76d88
commit 40e72d535e
5 changed files with 71 additions and 43 deletions

View file

@ -1920,27 +1920,25 @@ void WaterRenderer::endReflectionPass(VkCommandBuffer cmd) {
vkCmdEndRenderPass(cmd);
reflectionColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Update all per-frame scene descriptor sets with the freshly rendered reflection texture
if (reflectionColorView && reflectionSampler) {
VkDescriptorImageInfo reflInfo{};
reflInfo.sampler = reflectionSampler;
reflInfo.imageView = reflectionColorView;
reflInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Update only the current frame's scene descriptor set with the reflection texture.
// Updating all frames would race with in-flight command buffers that have the
// other frame's descriptor set bound.
if (reflectionColorView && reflectionSampler && vkCtx) {
uint32_t fi = vkCtx->getCurrentFrame() % SCENE_HISTORY_FRAMES;
if (sceneHistory[fi].sceneSet) {
VkDescriptorImageInfo reflInfo{};
reflInfo.sampler = reflectionSampler;
reflInfo.imageView = reflectionColorView;
reflInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
std::vector<VkWriteDescriptorSet> writes;
for (uint32_t f = 0; f < SCENE_HISTORY_FRAMES; f++) {
if (!sceneHistory[f].sceneSet) continue;
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = sceneHistory[f].sceneSet;
write.dstSet = sceneHistory[fi].sceneSet;
write.dstBinding = 2;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &reflInfo;
writes.push_back(write);
}
if (!writes.empty()) {
vkUpdateDescriptorSets(vkCtx->getDevice(), static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
vkUpdateDescriptorSets(vkCtx->getDevice(), 1, &write, 0, nullptr);
}
}
}