From c64103c73f350264d8c7745448ab2a36fa21ee92 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 10 Jan 2018 11:44:40 +0100 Subject: [PATCH] [dxvk] Implementing unbound resource handling (1/4) In order to emulate D3D11 behaviour when a resource is not bound, we use specialization constants. This requires further changes in the shader compiler. --- src/dxvk/dxvk_binding.h | 83 ++++++++++++++++++++++++++++++++++- src/dxvk/dxvk_compute.cpp | 2 +- src/dxvk/dxvk_context.cpp | 34 +++++++++++++- src/dxvk/dxvk_context_state.h | 13 +++--- src/dxvk/dxvk_graphics.cpp | 24 +++++++--- src/dxvk/dxvk_graphics.h | 3 ++ src/dxvk/dxvk_limits.h | 1 + src/dxvk/dxvk_shader.cpp | 5 +-- src/dxvk/dxvk_shader.h | 13 +++++- 9 files changed, 161 insertions(+), 17 deletions(-) diff --git a/src/dxvk/dxvk_binding.h b/src/dxvk/dxvk_binding.h index be84ec99..c044d818 100644 --- a/src/dxvk/dxvk_binding.h +++ b/src/dxvk/dxvk_binding.h @@ -3,12 +3,93 @@ #include "dxvk_buffer.h" #include "dxvk_descriptor.h" #include "dxvk_image.h" +#include "dxvk_limits.h" #include "dxvk_sampler.h" namespace dxvk { /** - * \brief Bound shader resource + * \brief Binding state + * + * Used to track which resource slots have a compatible + * binding and which ones don't. This is used to set up + * binding-related specialization constants in shaders. + * \tparam N Number of binding slots + */ + class DxvkBindingState { + constexpr static uint32_t BitCount = 32; + constexpr static uint32_t IntCount = (MaxNumActiveBindings + BitCount - 1) / BitCount; + public: + + /** + * \brief Tests whether a binding is active + * + * \param [in] slot The binding ID + * \returns \c true if the binding is active + */ + bool isBound(uint32_t slot) const { + const uint32_t intId = slot / BitCount; + const uint32_t bitId = slot % BitCount; + const uint32_t bitMask = 1u << bitId; + return (m_slots[intId] & bitMask) != 0; + } + + /** + * \brief Marks a binding as active + * + * \param [in] slot The binding ID + * \returns \c true if the state has changed + */ + bool setBound(uint32_t slot) { + const uint32_t intId = slot / BitCount; + const uint32_t bitId = slot % BitCount; + const uint32_t bitMask = 1u << bitId; + + const uint32_t prev = m_slots[intId]; + m_slots[intId] = prev | bitMask; + return (prev & bitMask) == 0; + } + + /** + * \brief Marks a binding as inactive + * + * \param [in] slot The binding ID + * \returns \c true if the state has changed + */ + bool setUnbound(uint32_t slot) { + const uint32_t intId = slot / BitCount; + const uint32_t bitId = slot % BitCount; + const uint32_t bitMask = 1u << bitId; + + const uint32_t prev = m_slots[intId]; + m_slots[intId] = prev & ~bitMask; + return (prev & bitMask) != 0; + } + + /** + * \brief Clears binding state + * + * Useful to zero out any bindings + * that are not used by a pipeline. + */ + void clear() { + for (uint32_t i = 0; i < IntCount; i++) + m_slots[i] = 0; + } + + private: + + uint32_t m_slots[IntCount]; + + }; + + + /** + * \brief Bound shader resources + * + * Stores the resources bound to a binding + * slot in DXVK. These are used to create + * descriptor sets. */ struct DxvkShaderResourceSlot { Rc sampler; diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp index ea7253d2..af49a71b 100644 --- a/src/dxvk/dxvk_compute.cpp +++ b/src/dxvk/dxvk_compute.cpp @@ -32,7 +32,7 @@ namespace dxvk { info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; - info.stage = m_cs->stageInfo(); + info.stage = m_cs->stageInfo(nullptr); info.layout = m_layout->pipelineLayout(); info.basePipelineHandle = VK_NULL_HANDLE; info.basePipelineIndex = 0; diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 401cdc99..75fb83d0 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -926,12 +926,14 @@ namespace dxvk { if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) { m_flags.clr(DxvkContextFlag::GpDirtyPipeline); + m_state.gp.bs.clear(); m_state.gp.pipeline = m_device->createGraphicsPipeline( m_state.gp.vs.shader, m_state.gp.tcs.shader, m_state.gp.tes.shader, m_state.gp.gs.shader, m_state.gp.fs.shader); } DxvkGraphicsPipelineStateInfo gpState; + gpState.bsBindingState = m_state.gp.bs; gpState.iaPrimitiveTopology = m_state.ia.primitiveTopology; gpState.iaPrimitiveRestart = m_state.ia.primitiveRestart; @@ -1033,6 +1035,13 @@ namespace dxvk { void DxvkContext::updateShaderResources( VkPipelineBindPoint bindPoint, const Rc& layout) { + DxvkBindingState& bs = + bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS + ? m_state.gp.bs + : m_state.cp.bs; + + bool updatePipelineState = false; + // TODO recreate resource views if the underlying // resource was marked as dirty after invalidation for (uint32_t i = 0; i < layout->bindingCount(); i++) { @@ -1042,6 +1051,8 @@ namespace dxvk { switch (binding.type) { case VK_DESCRIPTOR_TYPE_SAMPLER: if (res.sampler != nullptr) { + updatePipelineState |= bs.setBound(i); + m_descriptors[i].image.sampler = res.sampler->handle(); m_descriptors[i].image.imageView = VK_NULL_HANDLE; m_descriptors[i].image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -1049,6 +1060,8 @@ namespace dxvk { m_cmd->trackResource(res.sampler); } else { Logger::err("DxvkContext: Unbound sampler descriptor"); + updatePipelineState |= bs.setUnbound(i); + m_descriptors[i].image.sampler = VK_NULL_HANDLE; m_descriptors[i].image.imageView = VK_NULL_HANDLE; m_descriptors[i].image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -1056,7 +1069,9 @@ namespace dxvk { case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - if (res.imageView != nullptr && res.imageView->type() != binding.view) { + if (res.imageView != nullptr/* && res.imageView->type() != binding.view*/) { + updatePipelineState |= bs.setBound(i); + m_descriptors[i].image.sampler = VK_NULL_HANDLE; m_descriptors[i].image.imageView = res.imageView->handle(); m_descriptors[i].image.imageLayout = res.imageView->imageInfo().layout; @@ -1065,6 +1080,8 @@ namespace dxvk { m_cmd->trackResource(res.imageView->image()); } else { Logger::err("DxvkContext: Unbound or incompatible image descriptor"); + updatePipelineState |= bs.setUnbound(i); + m_descriptors[i].image.sampler = VK_NULL_HANDLE; m_descriptors[i].image.imageView = VK_NULL_HANDLE; m_descriptors[i].image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -1073,22 +1090,30 @@ namespace dxvk { case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: if (res.bufferView != nullptr) { + updatePipelineState |= bs.setBound(i); + m_descriptors[i].texelBuffer = res.bufferView->handle(); m_cmd->trackResource(res.bufferView); m_cmd->trackResource(res.bufferView->buffer()->resource()); } else { Logger::err("DxvkContext: Unbound texel buffer"); + updatePipelineState |= bs.setUnbound(i); + m_descriptors[i].texelBuffer = VK_NULL_HANDLE; } break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: if (res.bufferSlice.handle() != VK_NULL_HANDLE) { + updatePipelineState |= bs.setBound(i); + m_descriptors[i].buffer = res.bufferSlice.descriptorInfo(); m_cmd->trackResource(res.bufferSlice.resource()); } else { Logger::err("DxvkContext: Unbound buffer"); + updatePipelineState |= bs.setUnbound(i); + m_descriptors[i].buffer.buffer = VK_NULL_HANDLE; m_descriptors[i].buffer.offset = 0; m_descriptors[i].buffer.range = 0; @@ -1106,6 +1131,12 @@ namespace dxvk { layout->bindingCount(), layout->bindings(), m_descriptors.data()); + + if (updatePipelineState) { + m_flags.set(bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS + ? DxvkContextFlag::GpDirtyPipelineState + : DxvkContextFlag::CpDirtyPipelineState); + } } @@ -1178,6 +1209,7 @@ namespace dxvk { void DxvkContext::commitComputeState() { + // TODO handle CpDirtyPipelineState this->renderPassEnd(); this->updateComputePipeline(); this->updateComputeShaderResources(); diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 4f790758..083ef45a 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -30,6 +30,7 @@ namespace dxvk { GpDirtyIndexBuffer, ///< Index buffer binding are out of date CpDirtyPipeline, ///< Compute pipeline binding are out of date + CpDirtyPipelineState, ///< Compute pipeline needs to be recompiled CpDirtyResources, ///< Compute pipeline resource bindings are out of date }; @@ -70,17 +71,19 @@ namespace dxvk { struct DxvkGraphicsPipelineState { - DxvkShaderStage vs; - DxvkShaderStage tcs; - DxvkShaderStage tes; - DxvkShaderStage gs; - DxvkShaderStage fs; + DxvkBindingState bs; + DxvkShaderStage vs; + DxvkShaderStage tcs; + DxvkShaderStage tes; + DxvkShaderStage gs; + DxvkShaderStage fs; Rc pipeline; }; struct DxvkComputePipelineState { + DxvkBindingState bs; DxvkShaderStage cs; Rc pipeline; }; diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index b4681fb3..729e3047 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -91,13 +91,27 @@ namespace dxvk { VK_DYNAMIC_STATE_STENCIL_REFERENCE, }; + std::array specData; + std::array specMap; + + for (uint32_t i = 0; i < MaxNumActiveBindings; i++) { + specData[i] = state.bsBindingState.isBound(i) ? VK_TRUE : VK_FALSE; + specMap [i] = { i, static_cast(sizeof(VkBool32)) * i, sizeof(VkBool32) }; + } + + VkSpecializationInfo specInfo; + specInfo.mapEntryCount = specMap.size(); + specInfo.pMapEntries = specMap.data(); + specInfo.dataSize = specData.size() * sizeof(VkBool32); + specInfo.pData = specData.data(); + std::vector stages; - if (m_vs != nullptr) stages.push_back(m_vs->stageInfo()); - if (m_tcs != nullptr) stages.push_back(m_tcs->stageInfo()); - if (m_tes != nullptr) stages.push_back(m_tes->stageInfo()); - if (m_gs != nullptr) stages.push_back(m_gs->stageInfo()); - if (m_fs != nullptr) stages.push_back(m_fs->stageInfo()); + if (m_vs != nullptr) stages.push_back(m_vs->stageInfo(&specInfo)); + if (m_tcs != nullptr) stages.push_back(m_tcs->stageInfo(&specInfo)); + if (m_tes != nullptr) stages.push_back(m_tes->stageInfo(&specInfo)); + if (m_gs != nullptr) stages.push_back(m_gs->stageInfo(&specInfo)); + if (m_fs != nullptr) stages.push_back(m_fs->stageInfo(&specInfo)); VkPipelineVertexInputStateCreateInfo viInfo; viInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index e77d0b0a..da9ce6ef 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -3,6 +3,7 @@ #include #include +#include "dxvk_binding.h" #include "dxvk_constant_state.h" #include "dxvk_hash.h" #include "dxvk_pipelayout.h" @@ -27,6 +28,8 @@ namespace dxvk { DxvkGraphicsPipelineStateInfo& operator = ( const DxvkGraphicsPipelineStateInfo& other); + DxvkBindingState bsBindingState; + VkPrimitiveTopology iaPrimitiveTopology; VkBool32 iaPrimitiveRestart; diff --git a/src/dxvk/dxvk_limits.h b/src/dxvk/dxvk_limits.h index 8a00fdbb..06364f69 100644 --- a/src/dxvk/dxvk_limits.h +++ b/src/dxvk/dxvk_limits.h @@ -11,6 +11,7 @@ namespace dxvk { MaxNumOutputStreams = 4, MaxNumViewports = 16, MaxNumResourceSlots = 1024, + MaxNumActiveBindings = 128, }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index 18e8ec17..efe5e609 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -26,16 +26,15 @@ namespace dxvk { } - VkPipelineShaderStageCreateInfo DxvkShaderModule::stageInfo() const { + VkPipelineShaderStageCreateInfo DxvkShaderModule::stageInfo(const VkSpecializationInfo* specInfo) const { VkPipelineShaderStageCreateInfo info; - info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; info.stage = m_stage; info.module = m_module; info.pName = "main"; - info.pSpecializationInfo = nullptr; + info.pSpecializationInfo = specInfo; return info; } diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index 3c8ecf55..ae882120 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -28,11 +28,22 @@ namespace dxvk { ~DxvkShaderModule(); + /** + * \brief Shader module handle + * \returns Shader module handle + */ VkShaderModule handle() const { return m_module; } - VkPipelineShaderStageCreateInfo stageInfo() const; + /** + * \brief Shader stage creation info + * + * \param [in] specInfo Specialization info + * \returns Shader stage create info + */ + VkPipelineShaderStageCreateInfo stageInfo( + const VkSpecializationInfo* specInfo) const; private: