diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index e4a5f445ef..c3d5098af0 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -1529,6 +1529,15 @@ static void AddUtilityVertexAttributes(Vulkan::GraphicsPipelineBuilder& gpb) gpb.SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); } +static void SetPipelineProvokingVertex(const GSDevice::FeatureSupport& features, Vulkan::GraphicsPipelineBuilder& gpb) +{ + // We enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass. + // Normally we wouldn't enable it on the present/swap chain, but apparently the rule is it applies to the last + // pipeline bound before the render pass begun, and in this case, we can't bind null. + if (features.provoking_vertex_last) + gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); +} + VkShaderModule GSDeviceVK::GetUtilityVertexShader(const std::string& source, const char* replace_main = nullptr) { std::stringstream ss; @@ -1769,6 +1778,7 @@ bool GSDeviceVK::CompileConvertPipelines() ScopedGuard vs_guard([&vs]() { Vulkan::Util::SafeDestroyShaderModule(vs); }); Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); AddUtilityVertexAttributes(gpb); gpb.SetPipelineLayout(m_utility_pipeline_layout); gpb.SetDynamicViewportAndScissorState(); @@ -1777,10 +1787,6 @@ bool GSDeviceVK::CompileConvertPipelines() gpb.SetNoBlendingState(); gpb.SetVertexShader(vs); - // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass - if (m_features.provoking_vertex_last) - gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); - for (ShaderConvert i = ShaderConvert::COPY; static_cast(i) < static_cast(ShaderConvert::Count); i = static_cast(static_cast(i) + 1)) { @@ -1964,6 +1970,7 @@ bool GSDeviceVK::CompilePresentPipelines() ScopedGuard vs_guard([&vs]() { Vulkan::Util::SafeDestroyShaderModule(vs); }); Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); AddUtilityVertexAttributes(gpb); gpb.SetPipelineLayout(m_utility_pipeline_layout); gpb.SetDynamicViewportAndScissorState(); @@ -1975,10 +1982,6 @@ bool GSDeviceVK::CompilePresentPipelines() gpb.SetNoStencilState(); gpb.SetRenderPass(m_swap_chain_render_pass, 0); - // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass - if (m_features.provoking_vertex_last) - gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); - for (PresentShader i = PresentShader::COPY; static_cast(i) < static_cast(PresentShader::Count); i = static_cast(static_cast(i) + 1)) { @@ -2023,6 +2026,7 @@ bool GSDeviceVK::CompileInterlacePipelines() ScopedGuard vs_guard([&vs]() { Vulkan::Util::SafeDestroyShaderModule(vs); }); Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); AddUtilityVertexAttributes(gpb); gpb.SetPipelineLayout(m_utility_pipeline_layout); gpb.SetDynamicViewportAndScissorState(); @@ -2033,10 +2037,6 @@ bool GSDeviceVK::CompileInterlacePipelines() gpb.SetRenderPass(rp, 0); gpb.SetVertexShader(vs); - // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass - if (m_features.provoking_vertex_last) - gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); - for (int i = 0; i < static_cast(m_interlace.size()); i++) { VkShaderModule ps = GetUtilityFragmentShader(*shader, StringUtil::StdStringFromFormat("ps_main%d", i).c_str()); @@ -2077,6 +2077,7 @@ bool GSDeviceVK::CompileMergePipelines() ScopedGuard vs_guard([&vs]() { Vulkan::Util::SafeDestroyShaderModule(vs); }); Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); AddUtilityVertexAttributes(gpb); gpb.SetPipelineLayout(m_utility_pipeline_layout); gpb.SetDynamicViewportAndScissorState(); @@ -2086,10 +2087,6 @@ bool GSDeviceVK::CompileMergePipelines() gpb.SetRenderPass(rp, 0); gpb.SetVertexShader(vs); - // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass - if (m_features.provoking_vertex_last) - gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); - for (int i = 0; i < static_cast(m_merge.size()); i++) { VkShaderModule ps = GetUtilityFragmentShader(*shader, StringUtil::StdStringFromFormat("ps_main%d", i).c_str()); @@ -2119,6 +2116,7 @@ bool GSDeviceVK::CompilePostProcessingPipelines() return false; Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); AddUtilityVertexAttributes(gpb); gpb.SetPipelineLayout(m_utility_pipeline_layout); gpb.SetDynamicViewportAndScissorState(); @@ -2128,10 +2126,6 @@ bool GSDeviceVK::CompilePostProcessingPipelines() gpb.SetNoBlendingState(); gpb.SetRenderPass(rp, 0); - // we enable provoking vertex here anyway, in case it doesn't support multiple modes in the same pass - if (m_features.provoking_vertex_last) - gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); - { std::optional vshader = Host::ReadResourceFileToString("shaders/vulkan/convert.glsl"); if (!vshader) @@ -2267,6 +2261,7 @@ bool GSDeviceVK::CompileImGuiPipeline() ScopedGuard ps_guard([&ps]() { Vulkan::Util::SafeDestroyShaderModule(ps); }); Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); gpb.SetPipelineLayout(m_utility_pipeline_layout); gpb.SetRenderPass(m_swap_chain_render_pass, 0); gpb.AddVertexBuffer(0, sizeof(ImDrawVert), VK_VERTEX_INPUT_RATE_VERTEX); @@ -2655,6 +2650,7 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p) return VK_NULL_HANDLE; Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); // Common state gpb.SetPipelineLayout(m_tfx_pipeline_layout); @@ -2739,9 +2735,6 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p) VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, p.cms.wrgba); } - if (m_features.provoking_vertex_last) - gpb.SetProvokingVertex(VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT); - // Tests have shown that it's faster to just enable rast order on the entire pass, rather than alternating // between turning it on and off for different draws, and adding the required barrier between non-rast-order // and rast-order draws.