From 9a6a430768719ef19ee512b1558244b37fdee5d2 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 24 Mar 2026 13:05:27 -0700 Subject: [PATCH] fix: track render pass subpass mode to prevent ImGui secondary violation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When parallel recording is active, the scene pass uses VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS. Post-processing paths (FSR/FXAA) end the scene pass and begin a new INLINE render pass for the swapchain output. ImGui rendering must use the correct mode — secondary buffers for SECONDARY passes, direct calls for INLINE. Previously the check used a static condition based on enabled features (!fsr && !fsr2 && !fxaa && parallel), which could mismatch if a feature was enabled but initialization failed. Replace with endFrameInlineMode_ flag that tracks the actual current render pass mode at runtime, eliminating the validation error VUID-vkCmdDrawIndexed-commandBuffer-recording that caused intermittent NVIDIA driver crashes. --- include/rendering/renderer.hpp | 1 + src/rendering/renderer.cpp | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/include/rendering/renderer.hpp b/include/rendering/renderer.hpp index b53e87d1..80b33fe6 100644 --- a/include/rendering/renderer.hpp +++ b/include/rendering/renderer.hpp @@ -668,6 +668,7 @@ private: VkCommandBuffer secondaryCmds_[NUM_SECONDARIES][MAX_FRAMES] = {}; bool parallelRecordingEnabled_ = false; // set true after pools/buffers created + bool endFrameInlineMode_ = false; // true when endFrame switched to INLINE render pass bool createSecondaryCommandResources(); void destroySecondaryCommandResources(); VkCommandBuffer beginSecondary(uint32_t secondaryIndex); diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 7fd90840..801f28e2 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -1215,6 +1215,11 @@ void Renderer::beginFrame() { void Renderer::endFrame() { if (!vkCtx || currentCmd == VK_NULL_HANDLE) return; + // Track whether a post-processing path switched to an INLINE render pass. + // beginFrame() may have started the scene pass with SECONDARY_COMMAND_BUFFERS; + // post-proc paths end it and begin a new INLINE pass for the swapchain output. + endFrameInlineMode_ = false; + if (fsr2_.enabled && fsr2_.sceneFramebuffer) { // End the off-screen scene render pass vkCmdEndRenderPass(currentCmd); @@ -1297,7 +1302,7 @@ void Renderer::endFrame() { rpInfo.clearValueCount = msaaOn ? (vkCtx->getDepthResolveImageView() ? 4u : 3u) : 2u; rpInfo.pClearValues = clearValues; - vkCmdBeginRenderPass(currentCmd, &rpInfo, VK_SUBPASS_CONTENTS_INLINE); + endFrameInlineMode_ = true; vkCmdBeginRenderPass(currentCmd, &rpInfo, VK_SUBPASS_CONTENTS_INLINE); VkExtent2D ext = vkCtx->getSwapchainExtent(); VkViewport vp{}; @@ -1434,18 +1439,22 @@ void Renderer::endFrame() { renderFSRUpscale(); } - // ImGui rendering — must respect subpass contents mode - // Parallel recording only applies when no post-process pass is active. - if (!fsr_.enabled && !fsr2_.enabled && !fxaa_.enabled && parallelRecordingEnabled_) { - // Scene pass was begun with VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, - // so ImGui must be recorded into a secondary command buffer. + // ImGui rendering — must respect the subpass contents mode of the + // CURRENT render pass. Post-processing paths (FSR/FXAA) end the scene + // pass and begin a new INLINE pass; if none ran, we're still inside the + // scene pass which may be SECONDARY_COMMAND_BUFFERS when parallel recording + // is active. Track this via endFrameInlineMode_ (set true by any post-proc + // path that started an INLINE render pass). + if (parallelRecordingEnabled_ && !endFrameInlineMode_) { + // Still in the scene pass with SECONDARY_COMMAND_BUFFERS — record + // ImGui into a secondary command buffer. VkCommandBuffer imguiCmd = beginSecondary(SEC_IMGUI); setSecondaryViewportScissor(imguiCmd); ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), imguiCmd); vkEndCommandBuffer(imguiCmd); vkCmdExecuteCommands(currentCmd, 1, &imguiCmd); } else { - // FSR swapchain pass uses INLINE mode; non-parallel also uses INLINE. + // INLINE render pass (post-process pass or non-parallel mode). ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), currentCmd); }