From 2970645f335f76433f62317b6fd38d30ccf7742b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 26 Apr 2024 19:06:51 +0200 Subject: [PATCH] [dxvk] Fix push constant compatibility for pipeline libraries When linking pipelines, all pipeline libraries are required to declare the exact same set of push constants, even for stages not part of the respective libraries. This invalidates all fossilize databases. --- src/d3d9/d3d9_fixed_function.cpp | 28 +++++----------------------- src/d3d9/d3d9_fixed_function.h | 2 +- src/d3d9/d3d9_format_helpers.cpp | 2 +- src/dxbc/dxbc_compiler.cpp | 5 ++--- src/dxso/dxso_compiler.cpp | 29 +++-------------------------- src/dxso/dxso_compiler.h | 2 -- src/dxvk/dxvk_context.cpp | 17 ++++++++++------- src/dxvk/dxvk_pipelayout.cpp | 24 ++++++++++++++++++++---- src/dxvk/dxvk_pipelayout.h | 22 ++++++++++++++++++++-- src/dxvk/dxvk_shader.cpp | 14 ++++++++++++-- src/dxvk/dxvk_shader.h | 2 +- src/dxvk/dxvk_swapchain_blitter.cpp | 3 +++ src/dxvk/hud/dxvk_hud_renderer.cpp | 7 ++++++- 13 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 099985f0..f9a2a1ce 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -336,7 +336,7 @@ namespace dxvk { } - uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) { + uint32_t SetupRenderStateBlock(SpirvModule& spvModule) { uint32_t floatType = spvModule.defFloatType(32); uint32_t uintType = spvModule.defIntType(32, 0); uint32_t vec3Type = spvModule.defVectorType(floatType, 3); @@ -357,7 +357,7 @@ namespace dxvk { floatType, }}; - uint32_t rsStruct = spvModule.defStructTypeUnique(count, rsMembers.data()); + uint32_t rsStruct = spvModule.defStructTypeUnique(rsMembers.size(), rsMembers.data()); uint32_t rsBlock = spvModule.newVar( spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant), spv::StorageClassPushConstant); @@ -369,9 +369,6 @@ namespace dxvk { uint32_t memberIdx = 0; auto SetMemberName = [&](const char* name, uint32_t offset) { - if (memberIdx >= count) - return; - spvModule.setDebugMemberName (rsStruct, memberIdx, name); spvModule.memberDecorateOffset (rsStruct, memberIdx, offset); memberIdx++; @@ -781,8 +778,6 @@ namespace dxvk { uint32_t m_inputMask = 0u; uint32_t m_outputMask = 0u; uint32_t m_flatShadingMask = 0u; - uint32_t m_pushConstOffset = 0u; - uint32_t m_pushConstSize = 0u; DxsoProgramType m_programType; D3D9FFShaderKeyVS m_vsKey; @@ -892,8 +887,8 @@ namespace dxvk { info.inputMask = m_inputMask; info.outputMask = m_outputMask; info.flatShadingInputs = m_flatShadingMask; - info.pushConstOffset = m_pushConstOffset; - info.pushConstSize = m_pushConstSize; + info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + info.pushConstSize = sizeof(D3D9RenderStateInfo); return new DxvkShader(info, m_module.compile()); } @@ -1384,20 +1379,7 @@ namespace dxvk { void D3D9FFShaderCompiler::setupRenderStateInfo() { - uint32_t count; - - if (m_programType == DxsoProgramType::PixelShader) { - m_pushConstOffset = 0; - m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize); - count = 5; - } - else { - m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize); - m_pushConstSize = sizeof(float) * 6; - count = 11; - } - - m_rsBlock = SetupRenderStateBlock(m_module, count); + m_rsBlock = SetupRenderStateBlock(m_module); } diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index 104f044c..0244755d 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -59,7 +59,7 @@ namespace dxvk { void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx); // Returns a render state block - uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count); + uint32_t SetupRenderStateBlock(SpirvModule& spvModule); struct D3D9PointSizeInfoVS { uint32_t defaultValue; diff --git a/src/d3d9/d3d9_format_helpers.cpp b/src/d3d9/d3d9_format_helpers.cpp index f0cdd2b6..7f284870 100644 --- a/src/d3d9/d3d9_format_helpers.cpp +++ b/src/d3d9/d3d9_format_helpers.cpp @@ -134,7 +134,7 @@ namespace dxvk { info.stage = VK_SHADER_STAGE_COMPUTE_BIT; info.bindingCount = bindings.size(); info.bindings = bindings.data(); - info.pushConstOffset = 0; + info.pushConstStages = VK_SHADER_STAGE_COMPUTE_BIT; info.pushConstSize = sizeof(VkExtent2D); return new DxvkShader(info, std::move(code)); diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 5b65c8a7..b679c323 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -257,14 +257,13 @@ namespace dxvk { info.outputMask = m_outputMask; info.uniformSize = m_immConstData.size(); info.uniformData = m_immConstData.data(); + info.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT; + info.pushConstSize = sizeof(DxbcPushConstants); info.outputTopology = m_outputTopology; if (m_programInfo.type() == DxbcProgramType::HullShader) info.patchVertexCount = m_hs.vertexCountIn; - if (m_programInfo.type() == DxbcProgramType::PixelShader && m_ps.pushConstantId) - info.pushConstSize = sizeof(DxbcPushConstants); - if (m_moduleInfo.xfb) { info.xfbRasterizedStream = m_moduleInfo.xfb->rasterizedStream; diff --git a/src/dxso/dxso_compiler.cpp b/src/dxso/dxso_compiler.cpp index abc55de8..97bece7d 100644 --- a/src/dxso/dxso_compiler.cpp +++ b/src/dxso/dxso_compiler.cpp @@ -228,8 +228,8 @@ namespace dxvk { info.bindings = m_bindings.data(); info.inputMask = m_inputMask; info.outputMask = m_outputMask; - info.pushConstOffset = m_pushConstOffset; - info.pushConstSize = m_pushConstSize; + info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + info.pushConstSize = sizeof(D3D9RenderStateInfo); if (m_programInfo.type() == DxsoProgramTypes::PixelShader) info.flatShadingInputs = m_ps.flatShadingMask; @@ -3561,30 +3561,7 @@ void DxsoCompiler::emitControlFlowGenericLoop( void DxsoCompiler::setupRenderStateInfo() { - uint32_t count; - - // Only need alpha ref for PS 3. - // No FF fog component. - if (m_programInfo.type() == DxsoProgramType::PixelShader) { - if (m_programInfo.majorVersion() == 3) { - m_pushConstOffset = offsetof(D3D9RenderStateInfo, alphaRef); - m_pushConstSize = sizeof(float); - } - else { - m_pushConstOffset = 0; - m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize); - } - - count = 5; - } - else { - m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize); - // Point scale never triggers on programmable - m_pushConstSize = sizeof(float) * 3; - count = 8; - } - - m_rsBlock = SetupRenderStateBlock(m_module, count); + m_rsBlock = SetupRenderStateBlock(m_module); } diff --git a/src/dxso/dxso_compiler.h b/src/dxso/dxso_compiler.h index 05daca70..6b58a84d 100644 --- a/src/dxso/dxso_compiler.h +++ b/src/dxso/dxso_compiler.h @@ -344,8 +344,6 @@ namespace dxvk { // covers vertex input and fragment output. uint32_t m_inputMask = 0u; uint32_t m_outputMask = 0u; - uint32_t m_pushConstOffset = 0u; - uint32_t m_pushConstSize = 0u; /////////////////////////////////// // Shader-specific data structures diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index e12553df..c63dce53 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -4984,7 +4984,7 @@ namespace dxvk { // Mark compute resources and push constants as dirty m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT); - if (newPipeline->getBindings()->layout().getPushConstantRange().size) + if (newPipeline->getBindings()->layout().getPushConstantRange(true).size) m_flags.set(DxvkContextFlag::DirtyPushConstants); m_flags.clr(DxvkContextFlag::CpDirtyPipelineState); @@ -5058,7 +5058,7 @@ namespace dxvk { m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS); - if (newPipeline->getBindings()->layout().getPushConstantRange().size) + if (newPipeline->getBindings()->layout().getPushConstantRange(true).size) m_flags.set(DxvkContextFlag::DirtyPushConstants); m_flags.clr(DxvkContextFlag::GpDirtyPipeline); @@ -5925,16 +5925,19 @@ namespace dxvk { auto bindings = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.pipeline->getBindings() : m_state.cp.pipeline->getBindings(); - - VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange(); + + // Optimized pipelines may have push constants trimmed, so look up + // the exact layout used for the currently bound pipeline. + bool independentSets = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS + && m_flags.test(DxvkContextFlag::GpIndependentSets); + + VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange(independentSets); if (!pushConstRange.size) return; - // Push constants should be compatible between complete and - // independent layouts, so always ask for the complete one m_cmd->cmdPushConstants( - bindings->getPipelineLayout(false), + bindings->getPipelineLayout(independentSets), pushConstRange.stageFlags, pushConstRange.offset, pushConstRange.size, diff --git a/src/dxvk/dxvk_pipelayout.cpp b/src/dxvk/dxvk_pipelayout.cpp index c16992cf..9132d025 100644 --- a/src/dxvk/dxvk_pipelayout.cpp +++ b/src/dxvk/dxvk_pipelayout.cpp @@ -205,7 +205,7 @@ namespace dxvk { DxvkBindingLayout::DxvkBindingLayout(VkShaderStageFlags stages) - : m_pushConst { 0, 0, 0 }, m_stages(stages) { + : m_pushConst { 0, 0, 0 }, m_pushConstStages(0), m_stages(stages) { } @@ -249,11 +249,17 @@ namespace dxvk { } + void DxvkBindingLayout::addPushConstantStage(VkShaderStageFlagBits stage) { + m_pushConstStages |= stage; + } + + void DxvkBindingLayout::merge(const DxvkBindingLayout& layout) { for (uint32_t i = 0; i < layout.m_bindings.size(); i++) m_bindings[i].merge(layout.m_bindings[i]); addPushConstantRange(layout.m_pushConst); + m_pushConstStages |= layout.m_pushConstStages; } @@ -266,6 +272,9 @@ namespace dxvk { return false; } + if (m_pushConstStages != other.m_pushConstStages) + return false; + if (m_pushConst.stageFlags != other.m_pushConst.stageFlags || m_pushConst.offset != other.m_pushConst.offset || m_pushConst.size != other.m_pushConst.size) @@ -282,6 +291,7 @@ namespace dxvk { for (uint32_t i = 0; i < m_bindings.size(); i++) hash.add(m_bindings[i].hash()); + hash.add(m_pushConstStages); hash.add(m_pushConst.stageFlags); hash.add(m_pushConst.offset); hash.add(m_pushConst.size); @@ -334,15 +344,16 @@ namespace dxvk { } // Create pipeline layout objects - VkPushConstantRange pushConst = m_layout.getPushConstantRange(); + VkPushConstantRange pushConstComplete = m_layout.getPushConstantRange(false); + VkPushConstantRange pushConstIndependent = m_layout.getPushConstantRange(true); VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipelineLayoutInfo.setLayoutCount = setCount; pipelineLayoutInfo.pSetLayouts = setLayouts.data(); - if (pushConst.stageFlags && pushConst.size) { + if (pushConstComplete.stageFlags && pushConstComplete.size) { pipelineLayoutInfo.pushConstantRangeCount = 1; - pipelineLayoutInfo.pPushConstantRanges = &pushConst; + pipelineLayoutInfo.pPushConstantRanges = &pushConstComplete; } // If the full set is defined, create a layout without INDEPENDENT_SET_BITS @@ -356,6 +367,11 @@ namespace dxvk { if (m_device->canUseGraphicsPipelineLibrary() && (m_layout.getStages() & VK_SHADER_STAGE_ALL_GRAPHICS)) { pipelineLayoutInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT; + if (pushConstIndependent.stageFlags && pushConstIndependent.size) { + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = &pushConstIndependent; + } + if (vk->vkCreatePipelineLayout(vk->device(), &pipelineLayoutInfo, nullptr, &m_independentLayout)) throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout"); } diff --git a/src/dxvk/dxvk_pipelayout.h b/src/dxvk/dxvk_pipelayout.h index 1e9b6b1e..cccfb336 100644 --- a/src/dxvk/dxvk_pipelayout.h +++ b/src/dxvk/dxvk_pipelayout.h @@ -292,8 +292,19 @@ namespace dxvk { * \brief Retrieves push constant range * \returns Push constant range */ - VkPushConstantRange getPushConstantRange() const { - return m_pushConst; + VkPushConstantRange getPushConstantRange(bool independent) const { + VkPushConstantRange result = m_pushConst; + + if (!independent) { + result.stageFlags &= m_pushConstStages; + + if (!result.stageFlags) { + result.offset = 0; + result.size = 0; + } + } + + return result; } /** @@ -324,6 +335,12 @@ namespace dxvk { */ void addPushConstantRange(VkPushConstantRange range); + /** + * \brief Adds a stage that actively uses push constants + * \param [in] stage Shader stage + */ + void addPushConstantStage(VkShaderStageFlagBits stage); + /** * \brief Merges binding layouts * @@ -353,6 +370,7 @@ namespace dxvk { std::array m_bindings; VkPushConstantRange m_pushConst; + VkShaderStageFlags m_pushConstStages; VkShaderStageFlags m_stages; }; diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index dbebf715..2748bc17 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -59,8 +59,8 @@ namespace dxvk { if (info.pushConstSize) { VkPushConstantRange pushConst; - pushConst.stageFlags = info.stage; - pushConst.offset = info.pushConstOffset; + pushConst.stageFlags = info.pushConstStages; + pushConst.offset = 0; pushConst.size = info.pushConstSize; m_bindings.addPushConstantRange(pushConst); @@ -75,6 +75,8 @@ namespace dxvk { // Run an analysis pass over the SPIR-V code to gather some // info that we may need during pipeline compilation. + bool usesPushConstants = false; + std::vector bindingOffsets; std::vector varIds; std::vector sampleMaskIds; @@ -154,6 +156,9 @@ namespace dxvk { if (std::find(sampleMaskIds.begin(), sampleMaskIds.end(), ins.arg(2)) != sampleMaskIds.end()) m_flags.set(DxvkShaderFlag::ExportsSampleMask); } + + if (ins.arg(3) == spv::StorageClassPushConstant) + usesPushConstants = true; } // Ignore the actual shader code, there's nothing interesting for us in there. @@ -169,6 +174,11 @@ namespace dxvk { m_bindingOffsets.push_back(info); } + // Set flag for stages that actually use push constants + // so that they can be trimmed for optimized pipelines. + if (usesPushConstants) + m_bindings.addPushConstantStage(info.stage); + // Don't set pipeline library flag if the shader // doesn't actually support pipeline libraries m_needsLibraryCompile = canUsePipelineLibrary(true); diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index e3c0d8b7..bc138f27 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -52,7 +52,7 @@ namespace dxvk { /// Flat shading input mask uint32_t flatShadingInputs = 0; /// Push constant range - uint32_t pushConstOffset = 0; + VkShaderStageFlags pushConstStages = 0; uint32_t pushConstSize = 0; /// Uniform buffer data uint32_t uniformSize = 0; diff --git a/src/dxvk/dxvk_swapchain_blitter.cpp b/src/dxvk/dxvk_swapchain_blitter.cpp index c19188cb..beaf6e46 100644 --- a/src/dxvk/dxvk_swapchain_blitter.cpp +++ b/src/dxvk/dxvk_swapchain_blitter.cpp @@ -329,6 +329,8 @@ namespace dxvk { DxvkShaderCreateInfo vsInfo; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT; + vsInfo.pushConstSize = sizeof(PresenterArgs); vsInfo.outputMask = 0x1; m_vs = new DxvkShader(vsInfo, std::move(vsCode)); @@ -336,6 +338,7 @@ namespace dxvk { fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.bindingCount = fsBindings.size(); fsInfo.bindings = fsBindings.data(); + fsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.pushConstSize = sizeof(PresenterArgs); fsInfo.inputMask = 0x1; fsInfo.outputMask = 0x1; diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp index 630706e0..6e1dfeaf 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.cpp +++ b/src/dxvk/hud/dxvk_hud_renderer.cpp @@ -183,14 +183,17 @@ namespace dxvk::hud { vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.bindingCount = vsBindings.size(); vsInfo.bindings = vsBindings.data(); - vsInfo.outputMask = 0x3; + vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.pushConstSize = sizeof(HudTextPushConstants); + vsInfo.outputMask = 0x3; result.vert = new DxvkShader(vsInfo, std::move(vsCode)); DxvkShaderCreateInfo fsInfo; fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.bindingCount = fsBindings.size(); fsInfo.bindings = fsBindings.data(); + fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT; + fsInfo.pushConstSize = sizeof(HudTextPushConstants); fsInfo.inputMask = 0x3; fsInfo.outputMask = 0x1; result.frag = new DxvkShader(fsInfo, std::move(fsCode)); @@ -212,6 +215,7 @@ namespace dxvk::hud { DxvkShaderCreateInfo vsInfo; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.outputMask = 0x1; + vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; vsInfo.pushConstSize = sizeof(HudGraphPushConstants); result.vert = new DxvkShader(vsInfo, std::move(vsCode)); @@ -221,6 +225,7 @@ namespace dxvk::hud { fsInfo.bindings = fsBindings.data(); fsInfo.inputMask = 0x1; fsInfo.outputMask = 0x1; + fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.pushConstSize = sizeof(HudGraphPushConstants); result.frag = new DxvkShader(fsInfo, std::move(fsCode));