mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-14 00:23:50 +00:00
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:
parent
e19bf76d88
commit
40e72d535e
5 changed files with 71 additions and 43 deletions
|
|
@ -472,11 +472,31 @@ void CharacterRenderer::createFallbackTextures(VkDevice device) {
|
|||
}
|
||||
}
|
||||
|
||||
void CharacterRenderer::destroyModelGPU(M2ModelGPU& gpuModel) {
|
||||
void CharacterRenderer::destroyModelGPU(M2ModelGPU& gpuModel, bool defer) {
|
||||
if (!vkCtx_) return;
|
||||
VmaAllocator alloc = vkCtx_->getAllocator();
|
||||
if (gpuModel.vertexBuffer) { vmaDestroyBuffer(alloc, gpuModel.vertexBuffer, gpuModel.vertexAlloc); gpuModel.vertexBuffer = VK_NULL_HANDLE; }
|
||||
if (gpuModel.indexBuffer) { vmaDestroyBuffer(alloc, gpuModel.indexBuffer, gpuModel.indexAlloc); gpuModel.indexBuffer = VK_NULL_HANDLE; }
|
||||
|
||||
// Snapshot raw handles and null the model fields immediately
|
||||
::VkBuffer vb = gpuModel.vertexBuffer;
|
||||
VmaAllocation vbAlloc = gpuModel.vertexAlloc;
|
||||
::VkBuffer ib = gpuModel.indexBuffer;
|
||||
VmaAllocation ibAlloc = gpuModel.indexAlloc;
|
||||
gpuModel.vertexBuffer = VK_NULL_HANDLE;
|
||||
gpuModel.vertexAlloc = VK_NULL_HANDLE;
|
||||
gpuModel.indexBuffer = VK_NULL_HANDLE;
|
||||
gpuModel.indexAlloc = VK_NULL_HANDLE;
|
||||
|
||||
if (!defer) {
|
||||
// Safe after vkDeviceWaitIdle (shutdown / clear paths)
|
||||
if (vb) vmaDestroyBuffer(alloc, vb, vbAlloc);
|
||||
if (ib) vmaDestroyBuffer(alloc, ib, ibAlloc);
|
||||
} else if (vb || ib) {
|
||||
// Streaming path: in-flight command buffers may still reference these
|
||||
vkCtx_->deferAfterFrameFence([alloc, vb, vbAlloc, ib, ibAlloc]() {
|
||||
if (vb) vmaDestroyBuffer(alloc, vb, vbAlloc);
|
||||
if (ib) vmaDestroyBuffer(alloc, ib, ibAlloc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterRenderer::destroyInstanceBones(CharacterInstance& inst, bool defer) {
|
||||
|
|
@ -1412,7 +1432,7 @@ bool CharacterRenderer::loadModel(const pipeline::M2Model& model, uint32_t id) {
|
|||
|
||||
if (models.find(id) != models.end()) {
|
||||
core::Logger::getInstance().warning("Model ID ", id, " already loaded, replacing");
|
||||
destroyModelGPU(models[id]);
|
||||
destroyModelGPU(models[id], /*defer=*/true);
|
||||
}
|
||||
|
||||
M2ModelGPU gpuModel;
|
||||
|
|
|
|||
|
|
@ -198,7 +198,9 @@ bool PostProcessPipeline::executePostProcessing(VkCommandBuffer cmd, uint32_t im
|
|||
// FSR3+FXAA combined: re-point FXAA's descriptor to the FSR3 temporal output
|
||||
// so renderFXAAPass() applies spatial AA on the temporally-stabilized frame.
|
||||
// This must happen outside the render pass (descriptor updates are CPU-side).
|
||||
if (fxaa_.enabled && fxaa_.descSet && fxaa_.sceneSampler) {
|
||||
// Use per-frame descriptor set to avoid race with in-flight command buffers.
|
||||
uint32_t fxaaFrameIdx = vkCtx_->getCurrentFrame();
|
||||
if (fxaa_.enabled && fxaa_.descSet[fxaaFrameIdx] && fxaa_.sceneSampler) {
|
||||
VkImageView fsr3OutputView = VK_NULL_HANDLE;
|
||||
if (fsr2_.useAmdBackend) {
|
||||
if (fsr2_.amdFsr3FramegenRuntimeActive && fsr2_.framegenOutput.image)
|
||||
|
|
@ -215,7 +217,7 @@ bool PostProcessPipeline::executePostProcessing(VkCommandBuffer cmd, uint32_t im
|
|||
imgInfo.sampler = fxaa_.sceneSampler;
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = fxaa_.descSet;
|
||||
write.dstSet = fxaa_.descSet[fxaaFrameIdx];
|
||||
write.dstBinding = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
|
|
@ -257,23 +259,23 @@ bool PostProcessPipeline::executePostProcessing(VkCommandBuffer cmd, uint32_t im
|
|||
// of RCAS sharpening. FXAA descriptor is temporarily pointed to the FSR3
|
||||
// history buffer (which is already in SHADER_READ_ONLY_OPTIMAL). This gives
|
||||
// FSR3 temporal stability + FXAA spatial edge smoothing ("ultra quality native").
|
||||
if (fxaa_.enabled && fxaa_.pipeline && fxaa_.descSet) {
|
||||
if (fxaa_.enabled && fxaa_.pipeline && fxaa_.descSet[fxaaFrameIdx]) {
|
||||
renderFXAAPass();
|
||||
} else {
|
||||
// Draw RCAS sharpening from accumulated history buffer
|
||||
renderFSR2Sharpen();
|
||||
}
|
||||
|
||||
// Restore FXAA descriptor to its normal scene color source so standalone
|
||||
// FXAA frames are not affected by the FSR3 history pointer set above.
|
||||
if (fxaa_.enabled && fxaa_.descSet && fxaa_.sceneSampler && fxaa_.sceneColor.imageView) {
|
||||
// Restore this frame's FXAA descriptor to its normal scene color source
|
||||
// so standalone FXAA frames are not affected by the FSR3 history pointer.
|
||||
if (fxaa_.enabled && fxaa_.descSet[fxaaFrameIdx] && fxaa_.sceneSampler && fxaa_.sceneColor.imageView) {
|
||||
VkDescriptorImageInfo restoreInfo{};
|
||||
restoreInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
restoreInfo.imageView = fxaa_.sceneColor.imageView;
|
||||
restoreInfo.sampler = fxaa_.sceneSampler;
|
||||
VkWriteDescriptorSet restoreWrite{};
|
||||
restoreWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
restoreWrite.dstSet = fxaa_.descSet;
|
||||
restoreWrite.dstSet = fxaa_.descSet[fxaaFrameIdx];
|
||||
restoreWrite.dstBinding = 0;
|
||||
restoreWrite.descriptorCount = 1;
|
||||
restoreWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
|
|
@ -1754,36 +1756,41 @@ bool PostProcessPipeline::initFXAAResources() {
|
|||
layoutInfo.pBindings = &binding;
|
||||
vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &fxaa_.descSetLayout);
|
||||
|
||||
constexpr uint32_t setCount = FXAAState::DESC_SET_COUNT;
|
||||
VkDescriptorPoolSize poolSize{};
|
||||
poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
poolSize.descriptorCount = 1;
|
||||
poolSize.descriptorCount = setCount;
|
||||
VkDescriptorPoolCreateInfo poolInfo{};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolInfo.maxSets = 1;
|
||||
poolInfo.maxSets = setCount;
|
||||
poolInfo.poolSizeCount = 1;
|
||||
poolInfo.pPoolSizes = &poolSize;
|
||||
vkCreateDescriptorPool(device, &poolInfo, nullptr, &fxaa_.descPool);
|
||||
|
||||
VkDescriptorSetLayout layouts[setCount];
|
||||
for (uint32_t i = 0; i < setCount; i++) layouts[i] = fxaa_.descSetLayout;
|
||||
VkDescriptorSetAllocateInfo dsAllocInfo{};
|
||||
dsAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
dsAllocInfo.descriptorPool = fxaa_.descPool;
|
||||
dsAllocInfo.descriptorSetCount = 1;
|
||||
dsAllocInfo.pSetLayouts = &fxaa_.descSetLayout;
|
||||
vkAllocateDescriptorSets(device, &dsAllocInfo, &fxaa_.descSet);
|
||||
dsAllocInfo.descriptorSetCount = setCount;
|
||||
dsAllocInfo.pSetLayouts = layouts;
|
||||
vkAllocateDescriptorSets(device, &dsAllocInfo, fxaa_.descSet);
|
||||
|
||||
// Bind the resolved 1x sceneColor
|
||||
// Bind the resolved 1x sceneColor to all per-frame sets
|
||||
VkDescriptorImageInfo imgInfo{};
|
||||
imgInfo.sampler = fxaa_.sceneSampler;
|
||||
imgInfo.imageView = fxaa_.sceneColor.imageView;
|
||||
imgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = fxaa_.descSet;
|
||||
write.dstBinding = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &imgInfo;
|
||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||
for (uint32_t i = 0; i < setCount; i++) {
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = fxaa_.descSet[i];
|
||||
write.dstBinding = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.pImageInfo = &imgInfo;
|
||||
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||
}
|
||||
|
||||
// Pipeline layout — push constant holds vec4(rcpFrame.xy, sharpness, pad)
|
||||
VkPushConstantRange pc{};
|
||||
|
|
@ -1843,7 +1850,7 @@ void PostProcessPipeline::destroyFXAAResources() {
|
|||
|
||||
if (fxaa_.pipeline) { vkDestroyPipeline(device, fxaa_.pipeline, nullptr); fxaa_.pipeline = VK_NULL_HANDLE; }
|
||||
if (fxaa_.pipelineLayout) { vkDestroyPipelineLayout(device, fxaa_.pipelineLayout, nullptr); fxaa_.pipelineLayout = VK_NULL_HANDLE; }
|
||||
if (fxaa_.descPool) { vkDestroyDescriptorPool(device, fxaa_.descPool, nullptr); fxaa_.descPool = VK_NULL_HANDLE; fxaa_.descSet = VK_NULL_HANDLE; }
|
||||
if (fxaa_.descPool) { vkDestroyDescriptorPool(device, fxaa_.descPool, nullptr); fxaa_.descPool = VK_NULL_HANDLE; for (auto& s : fxaa_.descSet) s = VK_NULL_HANDLE; }
|
||||
if (fxaa_.descSetLayout) { vkDestroyDescriptorSetLayout(device, fxaa_.descSetLayout, nullptr); fxaa_.descSetLayout = VK_NULL_HANDLE; }
|
||||
if (fxaa_.sceneFramebuffer) { vkDestroyFramebuffer(device, fxaa_.sceneFramebuffer, nullptr); fxaa_.sceneFramebuffer = VK_NULL_HANDLE; }
|
||||
fxaa_.sceneSampler = VK_NULL_HANDLE; // Owned by VkContext sampler cache
|
||||
|
|
@ -1857,9 +1864,10 @@ void PostProcessPipeline::renderFXAAPass() {
|
|||
if (!fxaa_.pipeline || currentCmd_ == VK_NULL_HANDLE) return;
|
||||
VkExtent2D ext = vkCtx_->getSwapchainExtent();
|
||||
|
||||
uint32_t fi = vkCtx_->getCurrentFrame();
|
||||
vkCmdBindPipeline(currentCmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, fxaa_.pipeline);
|
||||
vkCmdBindDescriptorSets(currentCmd_, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
fxaa_.pipelineLayout, 0, 1, &fxaa_.descSet, 0, nullptr);
|
||||
fxaa_.pipelineLayout, 0, 1, &fxaa_.descSet[fi], 0, nullptr);
|
||||
|
||||
// Pass rcpFrame + sharpness + effect flag (vec4, 16 bytes).
|
||||
// When FSR2/FSR3 is active alongside FXAA, forward FSR2's sharpness so the
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue