From 22e709f3b373925343938d600a9f12c88517f1a1 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 14 Mar 2025 23:59:12 +0100 Subject: [PATCH] [dxvk] Consider point-mode tessellation for GS topology --- src/dxvk/dxvk_graphics.cpp | 58 ++++++++++++++++++++++---------------- src/dxvk/dxvk_graphics.h | 19 ++++++------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 72ed345f2..bcb144aa3 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -44,6 +44,19 @@ namespace dxvk { } + VkPrimitiveTopology determinePreGsTopology( + const DxvkGraphicsPipelineShaders& shaders, + const DxvkGraphicsPipelineStateInfo& state) { + if (shaders.tcs && shaders.tcs->flags().test(DxvkShaderFlag::TessellationPoints)) + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + + if (shaders.tes) + return shaders.tes->info().outputTopology; + + return state.ia.primitiveTopology(); + } + + DxvkGraphicsPipelineVertexInputState::DxvkGraphicsPipelineVertexInputState() { } @@ -52,13 +65,13 @@ namespace dxvk { DxvkGraphicsPipelineVertexInputState::DxvkGraphicsPipelineVertexInputState( const DxvkDevice* device, const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* vs) { + const DxvkGraphicsPipelineShaders& shaders) { std::array viBindingMap = { }; iaInfo.topology = state.ia.primitiveTopology(); iaInfo.primitiveRestartEnable = state.ia.primitiveRestart(); - uint32_t attrMask = vs->info().inputMask; + uint32_t attrMask = shaders.vs->info().inputMask; uint32_t bindingMask = 0; // Find out which bindings are used based on the attribute mask @@ -244,10 +257,10 @@ namespace dxvk { DxvkGraphicsPipelineFragmentOutputState::DxvkGraphicsPipelineFragmentOutputState( const DxvkDevice* device, const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* fs) { + const DxvkGraphicsPipelineShaders& shaders) { // Set up color formats and attachment blend states. Disable the write // mask for any attachment that the fragment shader does not write to. - uint32_t fsOutputMask = fs ? fs->info().outputMask : 0u; + uint32_t fsOutputMask = shaders.fs ? shaders.fs->info().outputMask : 0u; // Dual-source blending can only write to one render target if (state.useDualSourceBlending()) @@ -331,13 +344,13 @@ namespace dxvk { : VK_SAMPLE_COUNT_1_BIT; } - if (fs && fs->flags().test(DxvkShaderFlag::HasSampleRateShading)) { + if (shaders.fs && shaders.fs->flags().test(DxvkShaderFlag::HasSampleRateShading)) { msInfo.sampleShadingEnable = VK_TRUE; msInfo.minSampleShading = 1.0f; } // Alpha to coverage is not supported with sample mask exports. - cbUseDynamicAlphaToCoverage = !fs || !fs->flags().test(DxvkShaderFlag::ExportsSampleMask); + cbUseDynamicAlphaToCoverage = !shaders.fs || !shaders.fs->flags().test(DxvkShaderFlag::ExportsSampleMask); msSampleMask = state.ms.sampleMask() & ((1u << msInfo.rasterizationSamples) - 1); msInfo.pSampleMask = &msSampleMask; @@ -518,9 +531,7 @@ namespace dxvk { DxvkGraphicsPipelinePreRasterizationState::DxvkGraphicsPipelinePreRasterizationState( const DxvkDevice* device, const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* tes, - const DxvkShader* gs, - const DxvkShader* fs) { + const DxvkGraphicsPipelineShaders& shaders) { // Set up tessellation state tsInfo.patchControlPoints = state.ia.patchVertexCount(); @@ -532,7 +543,7 @@ namespace dxvk { // Set up rasterized stream depending on geometry shader state. // Rasterizing stream 0 is default behaviour in all situations. - int32_t streamIndex = gs ? gs->info().xfbRasterizedStream : 0; + int32_t streamIndex = shaders.gs ? shaders.gs->info().xfbRasterizedStream : 0; if (streamIndex > 0) { rsXfbStreamInfo.pNext = std::exchange(rsInfo.pNext, &rsXfbStreamInfo); @@ -558,7 +569,7 @@ namespace dxvk { } // Set up line rasterization mode as requested by the application. - if (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering(state, tes, gs)) { + if (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering(shaders, state)) { rsLineInfo.pNext = std::exchange(rsInfo.pNext, &rsLineInfo); rsLineInfo.lineRasterizationMode = state.rs.lineMode(); @@ -571,7 +582,7 @@ namespace dxvk { // in combination with smooth lines. Override the line mode to // rectangular to fix this, but keep the width fixed at 1.0. bool needsOverride = state.ms.enableAlphaToCoverage() - || (fs && fs->flags().test(DxvkShaderFlag::HasSampleRateShading)); + || (shaders.fs && shaders.fs->flags().test(DxvkShaderFlag::HasSampleRateShading)); if (needsOverride) rsLineInfo.lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; @@ -633,15 +644,14 @@ namespace dxvk { bool DxvkGraphicsPipelinePreRasterizationState::isLineRendering( - const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* tes, - const DxvkShader* gs) { + const DxvkGraphicsPipelineShaders& shaders, + const DxvkGraphicsPipelineStateInfo& state) { bool isLineRendering = state.rs.polygonMode() == VK_POLYGON_MODE_LINE; - if (gs) { - isLineRendering |= gs->info().outputTopology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - } else if (tes) { - isLineRendering |= tes->info().outputTopology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + if (shaders.gs) { + isLineRendering |= shaders.gs->info().outputTopology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + } else if (shaders.tes) { + isLineRendering |= shaders.tes->info().outputTopology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST; } else { VkPrimitiveTopology topology = state.ia.primitiveTopology(); @@ -874,7 +884,7 @@ namespace dxvk { // Fix up input topology for geometry shaders as necessary if (shaderInfo.stage == VK_SHADER_STAGE_GEOMETRY_BIT) { - VkPrimitiveTopology iaTopology = shaders.tes ? shaders.tes->info().outputTopology : state.ia.primitiveTopology(); + VkPrimitiveTopology iaTopology = determinePreGsTopology(shaders, state); info.inputTopology = determineGsInputTopology(shaderInfo.inputTopology, iaTopology); } @@ -1207,7 +1217,7 @@ namespace dxvk { // We do not implement setting certain rarely used render // states dynamically since they are generally not used - bool isLineRendering = DxvkGraphicsPipelinePreRasterizationState::isLineRendering(state, m_shaders.tes.ptr(), m_shaders.gs.ptr()); + bool isLineRendering = DxvkGraphicsPipelinePreRasterizationState::isLineRendering(m_shaders, state); if (state.rs.polygonMode() != VK_POLYGON_MODE_FILL || state.rs.conservativeMode() != VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT @@ -1235,7 +1245,7 @@ namespace dxvk { if (m_shaders.gs != nullptr) { // If the geometry shader's input topology is not compatible with // the topology set to the pipeline, we need to patch the GS. - VkPrimitiveTopology iaTopology = m_shaders.tes ? m_shaders.tes->info().outputTopology : state.ia.primitiveTopology(); + VkPrimitiveTopology iaTopology = determinePreGsTopology(m_shaders, state); VkPrimitiveTopology gsTopology = m_shaders.gs->info().inputTopology; if (determineGsInputTopology(gsTopology, iaTopology) != gsTopology) @@ -1306,8 +1316,8 @@ namespace dxvk { VkPipeline DxvkGraphicsPipeline::getBasePipeline( const DxvkGraphicsPipelineStateInfo& state) { - DxvkGraphicsPipelineVertexInputState viState(m_device, state, m_shaders.vs.ptr()); - DxvkGraphicsPipelineFragmentOutputState foState(m_device, state, m_shaders.fs.ptr()); + DxvkGraphicsPipelineVertexInputState viState(m_device, state, m_shaders); + DxvkGraphicsPipelineFragmentOutputState foState(m_device, state, m_shaders); DxvkGraphicsPipelineBaseInstanceKey key; key.viLibrary = m_manager->createVertexInputLibrary(viState); diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 2dde3e162..e60e4fd44 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -50,7 +50,7 @@ namespace dxvk { DxvkGraphicsPipelineVertexInputState( const DxvkDevice* device, const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* vs); + const DxvkGraphicsPipelineShaders& shaders); VkPipelineInputAssemblyStateCreateInfo iaInfo = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; VkPipelineVertexInputStateCreateInfo viInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; @@ -109,7 +109,7 @@ namespace dxvk { DxvkGraphicsPipelineFragmentOutputState( const DxvkDevice* device, const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* fs); + const DxvkGraphicsPipelineShaders& shaders); VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; VkPipelineColorBlendStateCreateInfo cbInfo = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; @@ -170,9 +170,7 @@ namespace dxvk { DxvkGraphicsPipelinePreRasterizationState( const DxvkDevice* device, const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* tes, - const DxvkShader* gs, - const DxvkShader* fs); + const DxvkGraphicsPipelineShaders& shaders); VkPipelineViewportStateCreateInfo vpInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; VkPipelineTessellationStateCreateInfo tsInfo = { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO }; @@ -187,9 +185,8 @@ namespace dxvk { size_t hash() const; static bool isLineRendering( - const DxvkGraphicsPipelineStateInfo& state, - const DxvkShader* tes, - const DxvkShader* gs); + const DxvkGraphicsPipelineShaders& shaders, + const DxvkGraphicsPipelineStateInfo& state); }; @@ -413,10 +410,10 @@ namespace dxvk { uint32_t specConstantMask) : shState(shaders, state), dyState(device, state, flags), - viState(device, state, shaders.vs.ptr()), - prState(device, state, shaders.tes.ptr(), shaders.gs.ptr(), shaders.fs.ptr()), + viState(device, state, shaders), + prState(device, state, shaders), fsState(device, state), - foState(device, state, shaders.fs.ptr()), + foState(device, state, shaders), scState(specConstantMask, state.sc) { } DxvkGraphicsPipelineShaderState shState;