From 1d1295b17e0505a3e5afe8d25b2c9fee58d15120 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 31 Aug 2023 10:22:46 +0100 Subject: [PATCH] Work around NV issue with dynamic state setting & rebinding pipeline --- renderdoc/driver/vulkan/vk_common.cpp | 11 +++++++++++ renderdoc/driver/vulkan/vk_common.h | 8 ++++++++ renderdoc/driver/vulkan/vk_debug.cpp | 28 ++++++++++++++++++++++++++- renderdoc/driver/vulkan/vk_debug.h | 5 +++++ renderdoc/driver/vulkan/vk_state.cpp | 4 ++++ 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index bafb65f6b..bd6e445b8 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -1057,6 +1057,17 @@ VkDriverInfo::VkDriverInfo(const VkPhysicalDeviceProperties &physProps, RDCLOG("Enabling NV texel fetch workaround - update to a newer driver for fix"); texelFetchBrokenDriver = true; } + + // this isn't exactly when the root problem started happening, but it is when it started + // happening in a way that was easy to notice. In this version NV applied a optimisation + // to not re-set static pipeline state when a renderpass was begun, which was previously + // hiding the issue by conservatively re-setting the state. + if(Major() > 532) + { + if(active) + RDCLOG("Enabling NV workaround for static pipeline force-bind to preserve state"); + nvidiaStaticPipelineRebindStates = true; + } } if(driverProps.driverID == VK_DRIVER_ID_AMD_PROPRIETARY || diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index 5e37592b8..16021d3cf 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -285,6 +285,13 @@ public: // on Intel, occlusion queries are broken unless the shader has some effects. When we don't want // it to have visible effects during pixel history we have to insert some manual side-effects bool IntelBrokenOcclusionQueries() const { return intelBrokenOcclusionQueries; } + // on NV binding a static pipeline does not re-set static state which may have been perturbed by + // dynamic state setting, if the previous bound pipeline was identical. + // to work around this, whenever we are setting state and we do not have a pipeline to bind, we + // bind a dummy pipeline to ensure the pipeline always changes when we are setting dynamic state. + // If we do have a pipeline to bind, we should never be perturbing dynamic state in between static + // pipeline binds. + bool NVStaticPipelineRebindStates() const { return nvidiaStaticPipelineRebindStates; } private: GPUVendor m_Vendor; @@ -299,6 +306,7 @@ private: bool qualcommDrefNon2DCompileCrash = false; bool qualcommLineWidthCrash = false; bool intelBrokenOcclusionQueries = false; + bool nvidiaStaticPipelineRebindStates = false; }; enum diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index 249ccea16..9f4b031ac 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -98,7 +98,8 @@ static void create(WrappedVulkan *driver, const char *objName, const int line, } pipeLayoutInfo.pSetLayouts = &setLayout; - pipeLayoutInfo.setLayoutCount = 1; + if(setLayout != VK_NULL_HANDLE) + pipeLayoutInfo.setLayoutCount = 1; VkResult vkr = driver->vkCreatePipelineLayout(driver->GetDev(), &pipeLayoutInfo, NULL, pipeLayout); if(vkr != VK_SUCCESS) @@ -708,6 +709,28 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver) if(RenderDoc::Inst().IsReplayApp()) { + CREATE_OBJECT(m_DummyPipelineLayout, VK_NULL_HANDLE, 0); + + ConciseGraphicsPipeline dummyPipeInfo = { + VK_NULL_HANDLE, + m_DummyPipelineLayout, + shaderCache->GetBuiltinModule(BuiltinShader::BlitVS), + shaderCache->GetBuiltinModule(BuiltinShader::FixedColFS), + {}, + VK_SAMPLE_COUNT_1_BIT, + false, // sampleRateShading + false, // depthEnable + false, // stencilEnable + VK_STENCIL_OP_REPLACE, + false, // colourOutput + false, // blendEnable + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + 0x0, // writeMask + }; + + CREATE_OBJECT(m_DummyPipeline, dummyPipeInfo); + VkDescriptorPoolSize descPoolTypes[] = { {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, ARRAY_COUNT(m_DiscardSet)}, }; @@ -803,6 +826,9 @@ VulkanDebugManager::~VulkanDebugManager() m_pDriver->vkDestroyPipeline(dev, m_MS2BufferPipe, NULL); m_pDriver->vkDestroyPipeline(dev, m_DepthMS2BufferPipe, NULL); + m_pDriver->vkDestroyPipelineLayout(dev, m_DummyPipelineLayout, NULL); + m_pDriver->vkDestroyPipeline(dev, m_DummyPipeline, NULL); + m_pDriver->vkDestroyDescriptorPool(dev, m_DiscardPool, NULL); m_pDriver->vkDestroyPipelineLayout(dev, m_DiscardLayout, NULL); m_pDriver->vkDestroyDescriptorSetLayout(dev, m_DiscardSetLayout, NULL); diff --git a/renderdoc/driver/vulkan/vk_debug.h b/renderdoc/driver/vulkan/vk_debug.h index a92fb4cc0..0832acf38 100644 --- a/renderdoc/driver/vulkan/vk_debug.h +++ b/renderdoc/driver/vulkan/vk_debug.h @@ -80,6 +80,7 @@ public: VkDeviceMemory GetReadbackMemory() { return m_ReadbackWindow.mem; } VkPipelineCache GetPipelineCache() { return m_PipelineCache; } VkPipeline GetCustomPipeline() { return m_Custom.TexPipeline; } + VkPipeline GetDummyPipeline() { return m_DummyPipeline; } VkImage GetCustomTexture() { return m_Custom.TexImg; } VkFramebuffer GetCustomFramebuffer() { return m_Custom.TexFB; } VkRenderPass GetCustomRenderpass() { return m_Custom.TexRP; } @@ -145,6 +146,10 @@ private: VkImage m_DummyStencilImage = {VK_NULL_HANDLE}; VkImageView m_DummyStencilView = {VK_NULL_HANDLE}; + // dummy pipeline + VkPipelineLayout m_DummyPipelineLayout; + VkPipeline m_DummyPipeline; + // one per depth/stencil output format, per sample count VkPipeline m_DepthArray2MSPipe[7][4] = {{VK_NULL_HANDLE}}; diff --git a/renderdoc/driver/vulkan/vk_state.cpp b/renderdoc/driver/vulkan/vk_state.cpp index c3935aec5..c12f8faea 100644 --- a/renderdoc/driver/vulkan/vk_state.cpp +++ b/renderdoc/driver/vulkan/vk_state.cpp @@ -326,6 +326,10 @@ void VulkanRenderState::BindPipeline(WrappedVulkan *vk, VkCommandBuffer cmd, } else if(binding == BindInitial) { + if(vk->GetDriverInfo().NVStaticPipelineRebindStates()) + ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, + Unwrap(vk->GetDebugManager()->GetDummyPipeline())); + // if we're setting up a partial command buffer, bind all dynamic state we have. This will // then get overridden, but we need it in case a pipeline expects to inherit some dynamic // state from earlier in the command buffer but there's no pipeline bound yet.