From 219853aa9f62da7f9df5630c350e65ba624cde63 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 16 Jun 2022 13:16:35 +0200 Subject: [PATCH] [dxvk] Rework dirty descriptor state tracking --- src/dxvk/dxvk_context.cpp | 123 ++++++++++++++++++---------------- src/dxvk/dxvk_context.h | 3 +- src/dxvk/dxvk_context_state.h | 2 - src/dxvk/dxvk_pipelayout.h | 78 +++++++++++++++++++++ 4 files changed, 147 insertions(+), 59 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index d01add70e..b80637854 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -53,7 +53,6 @@ namespace dxvk { DxvkContextFlag::GpDirtyFramebuffer, DxvkContextFlag::GpDirtyPipeline, DxvkContextFlag::GpDirtyPipelineState, - DxvkContextFlag::GpDirtyResources, DxvkContextFlag::GpDirtyVertexBuffers, DxvkContextFlag::GpDirtyIndexBuffer, DxvkContextFlag::GpDirtyXfbBuffers, @@ -64,8 +63,13 @@ namespace dxvk { DxvkContextFlag::GpDirtyDepthBounds, DxvkContextFlag::CpDirtyPipeline, DxvkContextFlag::CpDirtyPipelineState, - DxvkContextFlag::CpDirtyResources, DxvkContextFlag::DirtyDrawBuffer); + + m_descriptorState.dirtyStages( + VK_SHADER_STAGE_ALL_GRAPHICS | + VK_SHADER_STAGE_COMPUTE_BIT); + + m_descriptorState.clearSets(); } @@ -155,14 +159,10 @@ namespace dxvk { if (likely(needsUpdate)) m_rcTracked.clr(slot); - else - needsUpdate = m_rc[slot].bufferSlice.length() != buffer.length(); - - m_flags.set( - DxvkContextFlag::CpDirtyResources, - DxvkContextFlag::GpDirtyResources); m_rc[slot].bufferSlice = buffer; + + m_descriptorState.dirtyBuffers(stages); } @@ -178,9 +178,7 @@ namespace dxvk { : DxvkBufferSlice(); m_rcTracked.clr(slot); - m_flags.set( - DxvkContextFlag::CpDirtyResources, - DxvkContextFlag::GpDirtyResources); + m_descriptorState.dirtyViews(stages); } @@ -191,9 +189,7 @@ namespace dxvk { m_rc[slot].sampler = sampler; m_rcTracked.clr(slot); - m_flags.set( - DxvkContextFlag::CpDirtyResources, - DxvkContextFlag::GpDirtyResources); + m_descriptorState.dirtyViews(stages); } @@ -217,13 +213,11 @@ namespace dxvk { if (stage == VK_SHADER_STAGE_COMPUTE_BIT) { m_flags.set( DxvkContextFlag::CpDirtyPipeline, - DxvkContextFlag::CpDirtyPipelineState, - DxvkContextFlag::CpDirtyResources); + DxvkContextFlag::CpDirtyPipelineState); } else { m_flags.set( DxvkContextFlag::GpDirtyPipeline, - DxvkContextFlag::GpDirtyPipelineState, - DxvkContextFlag::GpDirtyResources); + DxvkContextFlag::GpDirtyPipelineState); } } @@ -1753,21 +1747,16 @@ namespace dxvk { ~(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - VkBufferUsageFlags resourceMask = - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | - VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; + if (usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)) + m_descriptorState.dirtyBuffers(buffer->getShaderStages()); - if (usage & resourceMask) { - m_flags.set(DxvkContextFlag::GpDirtyResources, - DxvkContextFlag::CpDirtyResources); - } - - // Fast early-out for resource buffers, very common - if (likely(!(usage & ~resourceMask))) + // Fast early-out for plain buffers, very common + if (likely(!(usage & ~(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)))) return; + if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) + m_descriptorState.dirtyViews(buffer->getShaderStages()); + if (usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT) m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer); @@ -4014,19 +4003,26 @@ namespace dxvk { void DxvkContext::unbindComputePipeline() { m_flags.set( DxvkContextFlag::CpDirtyPipeline, - DxvkContextFlag::CpDirtyPipelineState, - DxvkContextFlag::CpDirtyResources); + DxvkContextFlag::CpDirtyPipelineState); + m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT); + + m_state.cp.state.bsBindingMask.clear(); m_cpActivePipeline = VK_NULL_HANDLE; } bool DxvkContext::updateComputePipeline() { - m_state.cp.pipeline = lookupComputePipeline(m_state.cp.shaders); + auto newPipeline = lookupComputePipeline(m_state.cp.shaders); - if (unlikely(m_state.cp.pipeline == nullptr)) + m_state.cp.pipeline = newPipeline; + + if (unlikely(!newPipeline)) return false; - + + m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT); + m_state.cp.state.bsBindingMask.clear(); + if (m_state.cp.pipeline->getBindings()->layout().getPushConstantRange().size) m_flags.set(DxvkContextFlag::DirtyPushConstants); @@ -4054,7 +4050,6 @@ namespace dxvk { m_flags.set( DxvkContextFlag::GpDirtyPipeline, DxvkContextFlag::GpDirtyPipelineState, - DxvkContextFlag::GpDirtyResources, DxvkContextFlag::GpDirtyVertexBuffers, DxvkContextFlag::GpDirtyIndexBuffer, DxvkContextFlag::GpDirtyXfbBuffers, @@ -4064,20 +4059,25 @@ namespace dxvk { DxvkContextFlag::GpDirtyDepthBias, DxvkContextFlag::GpDirtyDepthBounds); + m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS); + + m_state.gp.state.bsBindingMask.clear(); m_gpActivePipeline = VK_NULL_HANDLE; } bool DxvkContext::updateGraphicsPipeline() { - m_state.gp.pipeline = lookupGraphicsPipeline(m_state.gp.shaders); + auto newPipeline = lookupGraphicsPipeline(m_state.gp.shaders); - if (unlikely(m_state.gp.pipeline == nullptr)) { + m_state.gp.pipeline = newPipeline; + + if (unlikely(!newPipeline)) { m_state.gp.flags = DxvkGraphicsPipelineFlags(); return false; } - if (m_state.gp.flags != m_state.gp.pipeline->flags()) { - m_state.gp.flags = m_state.gp.pipeline->flags(); + if (m_state.gp.flags != newPipeline->flags()) { + m_state.gp.flags = newPipeline->flags(); // Force-update vertex/index buffers for hazard checks m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer, @@ -4091,7 +4091,10 @@ namespace dxvk { this->spillRenderPass(true); } - if (m_state.gp.pipeline->getBindings()->layout().getPushConstantRange().size) + m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS); + m_state.gp.state.bsBindingMask.clear(); + + if (newPipeline->getBindings()->layout().getPushConstantRange().size) m_flags.set(DxvkContextFlag::DirtyPushConstants); m_flags.clr(DxvkContextFlag::GpDirtyPipeline); @@ -4147,7 +4150,6 @@ namespace dxvk { template void DxvkContext::updateResourceBindings(const DxvkBindingLayoutObjects* layout) { - std::array sets = { VK_NULL_HANDLE, VK_NULL_HANDLE }; std::array descriptors; const auto& bindings = layout->layout(); @@ -4159,15 +4161,20 @@ namespace dxvk { DxvkBindingMask newBindMask = refBindMask; + uint32_t dirtySetMask = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS + ? m_descriptorState.getDirtyGraphicsSets() + : m_descriptorState.getDirtyComputeSets(); + uint32_t bindingIndex = 0; + uint32_t firstUpdated = DxvkDescriptorSets::SetCount; for (uint32_t i = 0; i < DxvkDescriptorSets::SetCount; i++) { // Initialize binding mask for the current set, only // clear bits if certain resources are actually unbound. uint32_t bindingCount = bindings.getBindingCount(i); - // TODO skip if set is unmodified - if (true) { + if ((dirtySetMask & (1u << i)) || !m_descriptorState.getSet(i)) { + firstUpdated = std::min(firstUpdated, i); newBindMask.setRange(bindingIndex, bindingCount); for (uint32_t j = 0; j < bindingCount; j++) { @@ -4310,23 +4317,27 @@ namespace dxvk { Logger::err(str::format("DxvkContext: Unhandled descriptor type: ", binding.descriptorType)); } } - } - // Create and populate descriptor set with the given descriptors - sets[i] = allocateDescriptorSet(layout->getSetLayout(i)); + // Create and populate descriptor set with the given descriptors + VkDescriptorSet& set = m_descriptorState.getSet(i); + set = allocateDescriptorSet(layout->getSetLayout(i)); - if (bindingCount) { - m_cmd->updateDescriptorSetWithTemplate(sets[i], - layout->getSetUpdateTemplate(i), descriptors.data()); + if (bindingCount) { + m_cmd->updateDescriptorSetWithTemplate(set, + layout->getSetUpdateTemplate(i), descriptors.data()); + } } bindingIndex += bindingCount; } - // Bind all required descriptor sets + // Bind all updated descriptor sets + uint32_t setCount = DxvkDescriptorSets::SetCount - firstUpdated; + const VkDescriptorSet* setData = &m_descriptorState.getSet(firstUpdated); + m_cmd->cmdBindDescriptorSets(BindPoint, layout->getPipelineLayout(), - 0, sets.size(), sets.data(), + firstUpdated, setCount, setData, 0, nullptr); // Update pipeline if there are unbound resources @@ -4343,14 +4354,14 @@ namespace dxvk { void DxvkContext::updateComputeShaderResources() { this->updateResourceBindings(m_state.cp.pipeline->getBindings()); - m_flags.clr(DxvkContextFlag::CpDirtyResources); + m_descriptorState.clearStages(VK_SHADER_STAGE_COMPUTE_BIT); } void DxvkContext::updateGraphicsShaderResources() { this->updateResourceBindings(m_state.gp.pipeline->getBindings()); - m_flags.clr(DxvkContextFlag::GpDirtyResources); + m_descriptorState.clearStages(VK_SHADER_STAGE_ALL_GRAPHICS); } @@ -4948,7 +4959,7 @@ namespace dxvk { return false; } - if (m_flags.test(DxvkContextFlag::CpDirtyResources)) + if (m_descriptorState.hasDirtyComputeSets()) this->updateComputeShaderResources(); if (m_flags.test(DxvkContextFlag::CpDirtyPipelineState)) { @@ -4990,7 +5001,7 @@ namespace dxvk { if (m_flags.test(DxvkContextFlag::GpDirtyVertexBuffers)) this->updateVertexBufferBindings(); - if (m_flags.test(DxvkContextFlag::GpDirtyResources)) + if (m_descriptorState.hasDirtyGraphicsSets()) this->updateGraphicsShaderResources(); if (m_flags.test(DxvkContextFlag::GpDirtyPipelineState)) { diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 727ba544f..d5e41db6d 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1047,6 +1047,7 @@ namespace dxvk { DxvkContextFlags m_flags; DxvkContextState m_state; DxvkContextFeatures m_features; + DxvkDescriptorState m_descriptorState; DxvkBarrierSet m_sdmaAcquires; DxvkBarrierSet m_sdmaBarriers; @@ -1055,7 +1056,7 @@ namespace dxvk { DxvkBarrierSet m_execBarriers; DxvkBarrierSet m_gfxBarriers; DxvkBarrierControlFlags m_barrierControl; - + DxvkGpuQueryManager m_queryManager; DxvkStagingBuffer m_staging; diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index b8bb46b8b..992e5dd13 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -27,7 +27,6 @@ namespace dxvk { GpDirtyFramebuffer, ///< Framebuffer binding is out of date GpDirtyPipeline, ///< Graphics pipeline binding is out of date GpDirtyPipelineState, ///< Graphics pipeline needs to be recompiled - GpDirtyResources, ///< Graphics pipeline resource bindings are out of date GpDirtyVertexBuffers, ///< Vertex buffer bindings are out of date GpDirtyIndexBuffer, ///< Index buffer binding are out of date GpDirtyXfbBuffers, ///< Transform feedback buffer bindings are out of date @@ -43,7 +42,6 @@ namespace dxvk { CpDirtyPipeline, ///< Compute pipeline binding are out of date CpDirtyPipelineState, ///< Compute pipeline needs to be recompiled - CpDirtyResources, ///< Compute pipeline resource bindings are out of date DirtyDrawBuffer, ///< Indirect argument buffer is dirty DirtyPushConstants, ///< Push constant data has changed diff --git a/src/dxvk/dxvk_pipelayout.h b/src/dxvk/dxvk_pipelayout.h index ea43eb4cb..c2acbb5f4 100644 --- a/src/dxvk/dxvk_pipelayout.h +++ b/src/dxvk/dxvk_pipelayout.h @@ -470,6 +470,84 @@ namespace dxvk { }; + /** + * \brief Dirty descriptor set state + */ + class DxvkDescriptorState { + + public: + + void dirtyBuffers(VkShaderStageFlags stages) { + m_dirtyBuffers |= stages; + } + + void dirtyViews(VkShaderStageFlags stages) { + m_dirtyViews |= stages; + } + + void dirtyStages(VkShaderStageFlags stages) { + m_dirtyBuffers |= stages; + m_dirtyViews |= stages; + } + + void clearStages(VkShaderStageFlags stages) { + m_dirtyBuffers &= ~stages; + m_dirtyViews &= ~stages; + } + + bool hasDirtyGraphicsSets() const { + return (m_dirtyBuffers | m_dirtyViews) & (VK_SHADER_STAGE_ALL_GRAPHICS); + } + + bool hasDirtyComputeSets() const { + return (m_dirtyBuffers | m_dirtyViews) & (VK_SHADER_STAGE_COMPUTE_BIT); + } + + uint32_t getDirtyGraphicsSets() const { + uint32_t result = 0; + if (m_dirtyBuffers & VK_SHADER_STAGE_FRAGMENT_BIT) + result |= (1u << DxvkDescriptorSets::FsBuffers); + if (m_dirtyViews & VK_SHADER_STAGE_FRAGMENT_BIT) + result |= (1u << DxvkDescriptorSets::FsViews) | (1u << DxvkDescriptorSets::FsBuffers); + if ((m_dirtyBuffers | m_dirtyViews) & (VK_SHADER_STAGE_ALL_GRAPHICS & ~VK_SHADER_STAGE_FRAGMENT_BIT)) + result |= (1u << DxvkDescriptorSets::VsAll); + return result; + } + + uint32_t getDirtyComputeSets() const { + uint32_t result = 0; + if (m_dirtyBuffers & VK_SHADER_STAGE_COMPUTE_BIT) + result |= (1u << DxvkDescriptorSets::FsBuffers); + if (m_dirtyViews & VK_SHADER_STAGE_COMPUTE_BIT) + result |= (1u << DxvkDescriptorSets::FsViews) | (1u << DxvkDescriptorSets::FsBuffers); + return result; + } + + void clearSets() { + for (size_t i = 0; i < m_sets.size(); i++) + m_sets[i] = VK_NULL_HANDLE; + } + + template + VkDescriptorSet& getSet(uint32_t index) { + return m_sets[BindPoint * DxvkDescriptorSets::SetCount + index]; + } + + template + const VkDescriptorSet& getSet(uint32_t index) const { + return m_sets[BindPoint * DxvkDescriptorSets::SetCount + index]; + } + + private: + + VkShaderStageFlags m_dirtyBuffers = 0; + VkShaderStageFlags m_dirtyViews = 0; + + std::array m_sets; + + }; + + /** * \brief Resource slot *