From 94ca65d5873dd120d3dc4fb6ff08d6bd59ed4776 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 30 Jul 2022 17:42:46 +0200 Subject: [PATCH] [dxvk] Ignore spec constants that are not used by the current pipeline May reduce the number of pipeline permutations. --- src/dxvk/dxvk_compute.h | 11 +++++ src/dxvk/dxvk_context.cpp | 77 ++++++++++++++++++++++++++--------- src/dxvk/dxvk_context.h | 24 ++++++++++- src/dxvk/dxvk_context_state.h | 10 +++++ src/dxvk/dxvk_graphics.h | 11 +++++ 5 files changed, 112 insertions(+), 21 deletions(-) diff --git a/src/dxvk/dxvk_compute.h b/src/dxvk/dxvk_compute.h index a921e7d63..560e59ab5 100644 --- a/src/dxvk/dxvk_compute.h +++ b/src/dxvk/dxvk_compute.h @@ -90,6 +90,17 @@ namespace dxvk { DxvkBindingLayoutObjects* getBindings() const { return m_bindings; } + + /** + * \brief Queries spec constant mask + * + * This only includes user spec constants. + * \returns Bit mask of used spec constants + */ + uint32_t getSpecConstantMask() const { + constexpr uint32_t globalMask = (1u << MaxNumSpecConstants) - 1; + return m_shaders.cs->getSpecConstantMask() & globalMask; + } /** * \brief Retrieves pipeline handle diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index b1716c326..f568987e5 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -2524,24 +2524,6 @@ namespace dxvk { } - void DxvkContext::setSpecConstant( - VkPipelineBindPoint pipeline, - uint32_t index, - uint32_t value) { - auto& specConst = pipeline == VK_PIPELINE_BIND_POINT_GRAPHICS - ? m_state.gp.state.sc.specConstants[index] - : m_state.cp.state.sc.specConstants[index]; - - if (specConst != value) { - specConst = value; - - m_flags.set(pipeline == VK_PIPELINE_BIND_POINT_GRAPHICS - ? DxvkContextFlag::GpDirtyPipelineState - : DxvkContextFlag::CpDirtyPipelineState); - } - } - - void DxvkContext::setBarrierControl(DxvkBarrierControlFlags control) { m_barrierControl = control; } @@ -4493,6 +4475,12 @@ namespace dxvk { if (unlikely(!newPipeline)) return false; + if (unlikely(newPipeline->getSpecConstantMask() != m_state.cp.constants.mask)) + this->resetSpecConstants(newPipeline->getSpecConstantMask()); + + if (m_flags.test(DxvkContextFlag::CpDirtySpecConstants)) + this->updateSpecConstants(); + // Look up Vulkan pipeline handle for the given compute state auto pipelineHandle = newPipeline->getPipelineHandle(m_state.cp.state); @@ -4545,6 +4533,9 @@ namespace dxvk { return false; } + if (unlikely(newPipeline->getSpecConstantMask() != m_state.gp.constants.mask)) + this->resetSpecConstants(newPipeline->getSpecConstantMask()); + if (m_state.gp.flags != newPipeline->flags()) { m_state.gp.flags = newPipeline->flags(); @@ -4656,6 +4647,49 @@ namespace dxvk { } + template + void DxvkContext::resetSpecConstants( + uint32_t newMask) { + auto& scInfo = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.state.sc : m_state.cp.state.sc; + auto& scState = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.constants : m_state.cp.constants; + + // Set all constants to 0 that were used by the previous pipeline + // but are not used by the old one. Any stale data could otherwise + // lead to unnecessary pipeline variants being created. + for (auto i : bit::BitMask(scState.mask & ~newMask)) + scInfo.specConstants[i] = 0; + + scState.mask = newMask; + + auto flag = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS + ? DxvkContextFlag::GpDirtySpecConstants + : DxvkContextFlag::CpDirtySpecConstants; + + if (newMask) + m_flags.set(flag); + else + m_flags.clr(flag); + } + + + template + void DxvkContext::updateSpecConstants() { + auto& scInfo = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.state.sc : m_state.cp.state.sc; + auto& scState = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.constants : m_state.cp.constants; + + for (auto i : bit::BitMask(scState.mask)) + scInfo.specConstants[i] = scState.data[i]; + + if (BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS) { + m_flags.clr(DxvkContextFlag::GpDirtySpecConstants); + m_flags.set(DxvkContextFlag::GpDirtyPipelineState); + } else { + m_flags.clr(DxvkContextFlag::CpDirtySpecConstants); + m_flags.set(DxvkContextFlag::CpDirtyPipelineState); + } + } + + void DxvkContext::invalidateState() { this->unbindComputePipeline(); this->unbindGraphicsPipeline(); @@ -5348,7 +5382,9 @@ namespace dxvk { bool DxvkContext::commitComputeState() { this->spillRenderPass(false); - if (m_flags.test(DxvkContextFlag::CpDirtyPipelineState)) { + if (m_flags.any( + DxvkContextFlag::CpDirtyPipelineState, + DxvkContextFlag::CpDirtySpecConstants)) { if (unlikely(!this->updateComputePipelineState())) return false; } @@ -5397,6 +5433,9 @@ namespace dxvk { if (m_flags.test(DxvkContextFlag::GpDirtyVertexBuffers)) this->updateVertexBufferBindings(); + if (m_flags.test(DxvkContextFlag::GpDirtySpecConstants)) + this->updateSpecConstants(); + if (m_flags.test(DxvkContextFlag::GpDirtyPipelineState)) { DxvkGlobalPipelineBarrier barrier = { }; diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 3e9eff4e3..46b0dd9ba 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1033,7 +1033,20 @@ namespace dxvk { void setSpecConstant( VkPipelineBindPoint pipeline, uint32_t index, - uint32_t value); + uint32_t value) { + auto& scState = pipeline == VK_PIPELINE_BIND_POINT_GRAPHICS + ? m_state.gp.constants : m_state.cp.constants; + + if (scState.data[index] != value) { + scState.data[index] = value; + + if (scState.mask & (1u << index)) { + m_flags.set(pipeline == VK_PIPELINE_BIND_POINT_GRAPHICS + ? DxvkContextFlag::GpDirtySpecConstants + : DxvkContextFlag::CpDirtySpecConstants); + } + } + } /** * \brief Sets barrier control flags @@ -1347,7 +1360,14 @@ namespace dxvk { void unbindGraphicsPipeline(); bool updateGraphicsPipeline(); bool updateGraphicsPipelineState(DxvkGlobalPipelineBarrier srcBarrier); - + + template + void resetSpecConstants( + uint32_t newMask); + + template + void updateSpecConstants(); + void invalidateState(); template diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 859f29ca2..d0b4d5630 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -37,6 +37,7 @@ namespace dxvk { GpDirtyStencilRef, ///< Stencil reference has changed GpDirtyRasterizerState, ///< Cull mode and front face have changed GpDirtyViewport, ///< Viewport state has changed + GpDirtySpecConstants, ///< Graphics spec constants are out of date GpDynamicBlendConstants, ///< Blend constants are dynamic GpDynamicDepthStencilState, ///< Depth-stencil state is dynamic GpDynamicDepthBias, ///< Depth bias is dynamic @@ -47,6 +48,7 @@ namespace dxvk { GpIndependentSets, ///< Graphics pipeline layout was created with independent sets CpDirtyPipelineState, ///< Compute pipeline is out of date + CpDirtySpecConstants, ///< Compute spec constants are out of date DirtyDrawBuffer, ///< Indirect argument buffer is dirty DirtyPushConstants, ///< Push constant data has changed @@ -120,11 +122,18 @@ namespace dxvk { }; + struct DxvkSpecConstantState { + uint32_t mask = 0; + std::array data = { }; + }; + + struct DxvkGraphicsPipelineState { DxvkGraphicsPipelineShaders shaders; DxvkGraphicsPipelineStateInfo state; DxvkGraphicsPipelineFlags flags; DxvkGraphicsPipeline* pipeline = nullptr; + DxvkSpecConstantState constants; }; @@ -132,6 +141,7 @@ namespace dxvk { DxvkComputePipelineShaders shaders; DxvkComputePipelineStateInfo state; DxvkComputePipeline* pipeline = nullptr; + DxvkSpecConstantState constants; }; diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 71e05642e..d3593b41a 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -343,6 +343,17 @@ namespace dxvk { return m_bindings; } + /** + * \brief Queries spec constant mask + * + * This only includes user spec constants. + * \returns Bit mask of used spec constants + */ + uint32_t getSpecConstantMask() const { + constexpr uint32_t globalMask = (1u << MaxNumSpecConstants) - 1; + return m_specConstantMask & globalMask; + } + /** * \brief Queries global resource barrier *