From cf7b7505e93c96e3b63d9dc9a775b994a1572fa8 Mon Sep 17 00:00:00 2001 From: azhirnov Date: Thu, 21 Jan 2021 06:21:32 +0300 Subject: pipeline resource signature for vulkan --- Graphics/GraphicsEngineVulkan/CMakeLists.txt | 2 - .../include/DeviceContextVkImpl.hpp | 49 +- .../include/PipelineLayoutCacheVk.hpp | 82 -- .../include/PipelineLayoutVk.hpp | 93 +- .../include/PipelineResourceSignatureVkImpl.hpp | 152 +- .../include/PipelineStateVkImpl.hpp | 29 +- .../include/RenderDeviceVkImpl.hpp | 12 +- .../include/ShaderResourceCacheVk.hpp | 17 +- .../include/ShaderVariableVk.hpp | 85 +- .../src/DeviceContextVkImpl.cpp | 261 +++- .../src/PipelineLayoutCacheVk.cpp | 80 - .../GraphicsEngineVulkan/src/PipelineLayoutVk.cpp | 149 +- .../src/PipelineResourceSignatureVkImpl.cpp | 1526 +++++++++++++++++--- .../src/PipelineStateVkImpl.cpp | 238 ++- .../src/RenderDeviceVkImpl.cpp | 33 +- .../src/ShaderResourceBindingVkImpl.cpp | 26 +- .../src/ShaderResourceCacheVk.cpp | 116 +- .../GraphicsEngineVulkan/src/ShaderVariableVk.cpp | 590 +------- .../src/VulkanTypeConversions.cpp | 27 +- 19 files changed, 2135 insertions(+), 1432 deletions(-) delete mode 100644 Graphics/GraphicsEngineVulkan/include/PipelineLayoutCacheVk.hpp delete mode 100644 Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp (limited to 'Graphics/GraphicsEngineVulkan') diff --git a/Graphics/GraphicsEngineVulkan/CMakeLists.txt b/Graphics/GraphicsEngineVulkan/CMakeLists.txt index 40a0dd49..79e61acd 100644 --- a/Graphics/GraphicsEngineVulkan/CMakeLists.txt +++ b/Graphics/GraphicsEngineVulkan/CMakeLists.txt @@ -17,7 +17,6 @@ set(INCLUDE include/GenerateMipsVkHelper.hpp include/pch.h include/PipelineLayoutVk.hpp - include/PipelineLayoutCacheVk.hpp include/PipelineStateVkImpl.hpp include/QueryManagerVk.hpp include/QueryVkImpl.hpp @@ -93,7 +92,6 @@ set(SRC src/VulkanDynamicHeap.cpp src/FramebufferCache.cpp src/GenerateMipsVkHelper.cpp - src/PipelineLayoutCacheVk.cpp src/PipelineLayoutVk.cpp src/PipelineStateVkImpl.cpp src/QueryManagerVk.cpp diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index d91feae3..4e6d2b30 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -397,7 +397,6 @@ private: void CommitVkVertexBuffers(); void CommitViewports(); void CommitScissorRects(); - void BindShaderResources(PipelineStateVkImpl* pPipelineStateVk); __forceinline void TransitionOrVerifyBufferState(BufferVkImpl& Buffer, RESOURCE_STATE_TRANSITION_MODE TransitionMode, @@ -477,14 +476,54 @@ private: Uint32 NumCommands = 0; } m_State; + // graphics, compute, ray tracing + static constexpr Uint32 PIPELINE_BIND_POINTS = 3; + + // static and dynamic descriptor sets + static constexpr Uint32 MAX_DESCR_SET_PER_SIGNATURE = 2; + + struct DescriptorSetBindInfo + { + using ShaderResourceArray = std::array, MAX_RESOURCE_SIGNATURES>; + using VkDescSetArray = std::array; + using BoolArray = std::bitset; + + ShaderResourceArray Resources; + VkDescSetArray vkSets = {}; + BoolArray PendingVkSet = {0}; // 'true' if new descriptor set must be bound + BoolArray PendingDynamicDescriptors = {0}; // 'true' if dynamic descriptor set must be bound + BoolArray DynamicBuffersPresent = {0}; + + DescriptorSetBindInfo() + {} + + void Reset() + { + PendingVkSet.reset(); + DynamicBuffersPresent.reset(); + PendingDynamicDescriptors.reset(); + Resources.fill({}); + +#ifdef DILIGENT_DEBUG + vkSets.fill(VK_NULL_HANDLE); +#endif + } + + __forceinline bool RequireUpdate(bool Intact = false) const + { + return PendingVkSet.any() || PendingDynamicDescriptors.any() || (DynamicBuffersPresent.any() && !Intact); + } + }; + + void BindShaderResources(PipelineStateVkImpl* pPipelineStateVk); + void BindDescriptorSetsWithDynamicOffsets(DescriptorSetBindInfo& DescrSetBindInfo); + void ValidateShaderResources(); /// AZ TODO: comment - using DescSetBindingInfoArray = std::array; - DescSetBindingInfoArray m_DescrSetBindInfo; + std::array m_DescrSetBindInfo; /// AZ TODO: comment - using ShaderResourcePerPipelineType = std::array, MAX_RESOURCE_SIGNATURES>, 3>; - ShaderResourcePerPipelineType m_ShaderResources; + std::vector m_DynamicBufferOffsets; /// Render pass that matches currently bound render targets. /// This render pass may or may not be currently set in the command buffer diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutCacheVk.hpp deleted file mode 100644 index f34110f7..00000000 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutCacheVk.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -/// \file -/// Declaration of Diligent::PipelineLayoutCacheVk class - -#include -#include -#include "VulkanUtilities/VulkanObjectWrappers.hpp" -#include "PipelineLayoutVk.hpp" - -namespace Diligent -{ - -class PipelineLayoutCacheVk -{ -public: - PipelineLayoutCacheVk(RenderDeviceVkImpl& DeviceVKImpl) : - m_DeviceVk{DeviceVKImpl} - {} - - // clang-format off - PipelineLayoutCacheVk (const PipelineLayoutCacheVk&) = delete; - PipelineLayoutCacheVk (PipelineLayoutCacheVk&&) = delete; - PipelineLayoutCacheVk& operator = (const PipelineLayoutCacheVk&) = delete; - PipelineLayoutCacheVk& operator = (PipelineLayoutCacheVk&&) = delete; - // clang-format on - - ~PipelineLayoutCacheVk(); - - RefCntAutoPtr GetLayout(IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount); - - void OnDestroyLayout(PipelineLayoutVk* pLayout); - -private: - struct PipelineLayoutHash - { - std::size_t operator()(const PipelineLayoutVk* Key) const noexcept - { - return Key->GetHash(); - } - }; - - struct PipelineLayoutCompare - { - bool operator()(const PipelineLayoutVk* lhs, const PipelineLayoutVk* rhs) const noexcept; - }; - - RenderDeviceVkImpl& m_DeviceVk; - - std::mutex m_Mutex; - - std::unordered_set m_Cache; -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp index fd27d0a3..cbc654b7 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp @@ -43,43 +43,34 @@ class DeviceContextVkImpl; class ShaderResourceCacheVk; /// Implementation of the Diligent::PipelineLayoutVk class -class PipelineLayoutVk final : public ObjectBase +class PipelineLayoutVk { public: - PipelineLayoutVk(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount); - ~PipelineLayoutVk(); + PipelineLayoutVk(); - using ObjectBase::Release; - - void Finalize(); + void Create(RenderDeviceVkImpl* pDeviceVk, IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount); + void Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask); size_t GetHash() const; VkPipelineLayout GetVkPipelineLayout() const { return m_VkPipelineLayout; } - PIPELINE_TYPE GetPipelineType() const { return m_PipelineType; } Uint32 GetSignatureCount() const { return m_SignatureCount; } Uint32 GetDescriptorSetCount() const { return m_DescrSetCount; } Uint32 GetDynamicOffsetCount() const { return m_DynamicOffsetCount; } - IPipelineResourceSignature* GetSignature(Uint32 index) const + PipelineResourceSignatureVkImpl* GetSignature(Uint32 index) const { VERIFY_EXPR(index < m_SignatureCount); - return m_Signatures[index].RawPtr(); - } - - bool HasSignature(IPipelineResourceSignature* pPRS) const - { - Uint32 Index = pPRS->GetDesc().BindingIndex; - return Index < m_SignatureCount && m_Signatures[Index].RawPtr() == pPRS; + return m_Signatures[index].RawPtr(); } - Uint32 GetDescrSetIndex(IPipelineResourceSignature* pPRS) const + Uint32 GetDescrSetIndex(const IPipelineResourceSignature* pPRS) const { Uint32 Index = pPRS->GetDesc().BindingIndex; return Index < m_SignatureCount ? m_DescSetOffset[Index] : ~0u; } - Uint32 GetDynamicBufferOffset(IPipelineResourceSignature* pPRS) const + Uint32 GetDynamicBufferOffset(const IPipelineResourceSignature* pPRS) const { Uint32 Index = pPRS->GetDesc().BindingIndex; return Index < m_SignatureCount ? m_DynBufOffset[Index] : 0; @@ -93,25 +84,16 @@ public: }; bool GetResourceInfo(const char* Name, SHADER_TYPE Stage, ResourceInfo& Info) const; - using DescriptorSetBindInfo = PipelineResourceSignatureVkImpl::DescriptorSetBindInfo; - - // Computes dynamic offsets and binds descriptor sets - __forceinline void BindDescriptorSetsWithDynamicOffsets(VulkanUtilities::VulkanCommandBuffer& CmdBuffer, - Uint32 CtxId, - DeviceContextVkImpl* pCtxVkImpl, - DescriptorSetBindInfo& BindInfo) const; - private: VulkanUtilities::PipelineLayoutWrapper m_VkPipelineLayout; - RenderDeviceVkImpl* m_pDeviceVk; + Uint32 m_DynamicOffsetCount : 25; - // AZ TODO: pack bits - Uint16 m_DynamicOffsetCount = 0; - Uint8 m_SignatureCount = 0; - Uint8 m_DescrSetCount = 0; + Uint32 m_SignatureCount : 3; + static_assert(MAX_RESOURCE_SIGNATURES == (1 << 3), "update m_SignatureCount bits count"); - PIPELINE_TYPE m_PipelineType = PIPELINE_TYPE(0xFF); + Uint32 m_DescrSetCount : 4; + static_assert(MAX_RESOURCE_SIGNATURES * 2 == (1 << 4), "update m_DescrSetCount bits count"); using SignatureArray = std::array, MAX_RESOURCE_SIGNATURES>; using DescSetOffsetArray = std::array; @@ -122,53 +104,4 @@ private: DynBufOffsetArray m_DynBufOffset = {}; }; - -__forceinline void PipelineLayoutVk::BindDescriptorSetsWithDynamicOffsets(VulkanUtilities::VulkanCommandBuffer& CmdBuffer, - Uint32 CtxId, - DeviceContextVkImpl* pCtxVkImpl, - DescriptorSetBindInfo& BindInfo) const -{ - /* - VERIFY(BindInfo.pDbgPipelineLayout != nullptr, "Pipeline layout is not initialized, which most likely means that CommitShaderResources() has never been called"); - VERIFY(BindInfo.pDbgPipelineLayout->IsSameAs(*this), "Inconsistent pipeline layout"); - VERIFY(BindInfo.DynamicOffsetCount > 0, "This function should only be called for pipelines that contain dynamic descriptors"); - VERIFY_EXPR(BindInfo.pResourceCache != nullptr); - -#ifdef DILIGENT_DEBUG - Uint32 TotalDynamicDescriptors = 0; - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE; - VarType <= SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC; - VarType = static_cast(VarType + 1)) - { - const auto& Set = m_LayoutMgr.GetDescriptorSet(VarType); - TotalDynamicDescriptors += Set.NumDynamicDescriptors; - } - VERIFY(BindInfo.DynamicOffsetCount == TotalDynamicDescriptors, "Incosistent dynamic buffer size"); - VERIFY_EXPR(BindInfo.DynamicOffsets.size() >= BindInfo.DynamicOffsetCount); -#endif - - auto NumOffsetsWritten = BindInfo.pResourceCache->GetDynamicBufferOffsets(CtxId, pCtxVkImpl, BindInfo.DynamicOffsets); - VERIFY_EXPR(NumOffsetsWritten == BindInfo.DynamicOffsetCount); - (void)NumOffsetsWritten; - - // Note that there is one global dynamic buffer from which all dynamic resources are suballocated in Vulkan back-end, - // and this buffer is not resizable, so the buffer handle can never change. - - // vkCmdBindDescriptorSets causes the sets numbered [firstSet .. firstSet+descriptorSetCount-1] to use the - // bindings stored in pDescriptorSets[0 .. descriptorSetCount-1] for subsequent rendering commands - // (either compute or graphics, according to the pipelineBindPoint). Any bindings that were previously - // applied via these sets are no longer valid (13.2.5) - CmdBuffer.BindDescriptorSets(BindInfo.BindPoint, - m_LayoutMgr.GetVkPipelineLayout(), - 0, // First set - BindInfo.SetCout, - BindInfo.vkSets.data(), // BindInfo.vkSets is never empty - // dynamicOffsetCount must equal the total number of dynamic descriptors in the sets being bound (13.2.5) - BindInfo.DynamicOffsetCount, - BindInfo.DynamicOffsets.data()); - - BindInfo.DynamicDescriptorsBound = true; -*/ -} - } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index 6b01b4cd..c5897f86 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -30,6 +30,7 @@ /// \file /// Declaration of Diligent::PipelineResourceSignatureVkImpl class #include +#include #include "PipelineResourceSignatureBase.hpp" #include "VulkanUtilities/VulkanObjectWrappers.hpp" @@ -44,6 +45,31 @@ class ShaderResourceCacheVk; class ShaderVariableManagerVk; class DeviceContextVkImpl; +enum class DescriptorType : Uint8 +{ + Sampler, + CombinedImageSampler, + SeparateImage, + StorageImage, + StorageImage_ReadOnly, + UniformTexelBuffer, + StorageTexelBuffer, + StorageTexelBuffer_ReadOnly, + UniformBuffer, + StorageBuffer, + StorageBuffer_ReadOnly, + UniformBufferDynamic, + StorageBufferDynamic, + StorageBufferDynamic_ReadOnly, + InputAttachment, + AccelerationStructure, + Count, + Unknown = 0xFF, +}; + +RESOURCE_STATE DescriptorTypeToResourceState(DescriptorType Type); + + /// Implementation of the Diligent::PipelineResourceSignatureVkImpl class class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBase { @@ -56,23 +82,32 @@ public: bool bIsDeviceInternal = false); ~PipelineResourceSignatureVkImpl(); - Uint32 GetDynamicBufferCount() const { return m_DynamicBufferCount; } - Uint8 GetNumShaderStages() const { return m_NumShaders; } - Uint32 GetTotalResourceCount() const { return m_Desc.NumResources; } + Uint32 GetDynamicBufferCount() const { return m_DynamicBufferCount; } + Uint8 GetNumShaderStages() const { return m_NumShaders; } + SHADER_TYPE GetShaderStageType(Uint32 StageIndex) const; + Uint32 GetTotalResourceCount() const { return m_Desc.NumResources; } + Uint32 GetNumDescriptorSets() const; - static constexpr Uint8 InvalidSamplerInd = 0xFF; + static constexpr Uint32 InvalidSamplerInd = ~0u; - struct PackedBindingIndex + struct ResourceAttribs { - Uint16 Binding; - Uint8 DescSet : 1; // 0 - static, 1 - dynamic - Uint8 SamplerInd = InvalidSamplerInd; + DescriptorType Type; + Uint32 CacheOffset; // for ShaderResourceCacheVk + Uint16 BindingIndex; + Uint8 DescrSet : 1; + Uint8 ImmutableSamplerAssigned : 1; + Uint32 SamplerInd; + + ResourceAttribs() : + CacheOffset{~0u}, BindingIndex{0xFFFF}, DescrSet{0}, SamplerInd{InvalidSamplerInd}, ImmutableSamplerAssigned{0} + {} }; - const PackedBindingIndex& GetBinding(Uint32 ResIndex) const + const ResourceAttribs& GetAttribs(Uint32 ResIndex) const { VERIFY_EXPR(ResIndex < m_Desc.NumResources); - return m_pBindingIndices[ResIndex]; + return m_pResourceAttribs[ResIndex]; } const PipelineResourceDesc& GetResource(Uint32 ResIndex) const @@ -84,6 +119,9 @@ public: VkDescriptorSetLayout GetStaticVkDescriptorSetLayout() const { return m_VkDescSetLayouts[0]; } VkDescriptorSetLayout GetDynamicVkDescriptorSetLayout() const { return m_VkDescSetLayouts[1]; } + bool HasStaticDescrSet() const { return GetStaticVkDescriptorSetLayout() != VK_NULL_HANDLE; } + bool HasDynamicDescrSet() const { return GetDynamicVkDescriptorSetLayout() != VK_NULL_HANDLE; } + virtual void DILIGENT_CALL_TYPE CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, bool InitStaticResources) override final; @@ -93,51 +131,14 @@ public: virtual Uint32 DILIGENT_CALL_TYPE GetStaticVariableCount(SHADER_TYPE ShaderType) const override final; - using VkDescSetArray = std::array; + virtual void DILIGENT_CALL_TYPE BindStaticResources(Uint32 ShaderFlags, + IResourceMapping* pResourceMapping, + Uint32 Flags) override final; - struct DescriptorSetBindInfo + virtual bool DILIGENT_CALL_TYPE IsCompatibleWith(const IPipelineResourceSignature* pPRS) const override final { - VkDescSetArray vkSets; - std::vector DynamicOffsets; - const ShaderResourceCacheVk* pResourceCache = nullptr; - Uint32 SetCout = 0; - Uint32 DynamicOffsetCount = 0; - bool DynamicBuffersPresent = false; - bool DynamicDescriptorsBound = false; -#ifdef DILIGENT_DEBUG - //const PipelineLayoutVk* pDbgPipelineLayout = nullptr; -#endif - DescriptorSetBindInfo() : - DynamicOffsets(64) - { - } - - void Reset() - { - pResourceCache = nullptr; - SetCout = 0; - DynamicOffsetCount = 0; - DynamicBuffersPresent = false; - DynamicDescriptorsBound = false; - -#ifdef DILIGENT_DEBUG - // In release mode, do not clear vectors as this causes unnecessary work - DynamicOffsets.clear(); - - //pDbgPipelineLayout = nullptr; -#endif - } - }; - - void PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, - VkPipelineBindPoint BindPoint, - const ShaderResourceCacheVk& ResourceCache, - DescriptorSetBindInfo& BindInfo, - VkDescriptorSet VkDynamicDescrSet) const; - - static VkDescriptorType GetVkDescriptorType(const PipelineResourceDesc& Res); - - SHADER_TYPE GetShaderStageType(Uint32 StageIndex) const; + return IsCompatibleWith(*ValidatedCast(pPRS)); + } SRBMemoryAllocator& GetSRBMemoryAllocator() { @@ -153,25 +154,60 @@ public: void InitializeStaticSRBResources(ShaderResourceCacheVk& ResourceCache) const; + static String GetPrintName(const PipelineResourceDesc& ResDesc, Uint32 ArrayInd); + + void BindResource(IDeviceObject* pObj, + Uint32 ArrayIndex, + Uint32 ResIndex, + ShaderResourceCacheVk& ResourceCache) const; + + void CommitDynamicResources(const ShaderResourceCacheVk& ResourceCache, + VkDescriptorSet vkDynamicDescriptorSet) const; + + bool IsCompatibleWith(const PipelineResourceSignatureVkImpl& Other) const; + + bool IsIncompatibleWith(const PipelineResourceSignatureVkImpl& Other) const + { + return GetHash() != Other.GetHash(); + } + private: void Destruct(); + void ReserveSpaceForStaticVarsMgrs(const PipelineResourceSignatureDesc& Desc, + FixedLinearAllocator& MemPool, + Int8& StaticVarStageCount, + Uint32& StaticVarCount, + Uint8 DSMapping[2]); + + void CreateLayout(Uint32 StaticVarCount, const Uint8 DSMapping[2]); + + Uint32 FindAssignedSampler(const PipelineResourceDesc& SepImg) const; + + // returns descriptor set index in resource cache + Uint32 GetStaticDescrSetIndex() const; + Uint32 GetDynamicDescrSetIndex() const; + + using ImmutableSamplerPtrType = RefCntAutoPtr; + +private: VulkanUtilities::DescriptorSetLayoutWrapper m_VkDescSetLayouts[2]; - PackedBindingIndex* m_pBindingIndices = nullptr; // [m_Desc.NumResources] + ResourceAttribs* m_pResourceAttribs = nullptr; // [m_Desc.NumResources] SHADER_TYPE m_ShaderStages = SHADER_TYPE_UNKNOWN; - Uint16 m_DynamicBufferCount = 0; - Uint8 m_NumShaders = 0; + Uint32 m_DynamicBufferCount : 29; // buffers with dynamic offsets + Uint32 m_NumShaders : 3; std::array m_StaticVarIndex = {-1, -1, -1, -1, -1, -1}; static_assert(MAX_SHADERS_IN_PIPELINE == 6, "Please update the initializer list above"); ShaderResourceCacheVk* m_pResourceCache = nullptr; - ShaderVariableManagerVk* m_StaticVarsMgrs = nullptr; // [0..MAX_SHADERS_IN_PIPELINE] + ShaderVariableManagerVk* m_StaticVarsMgrs = nullptr; // [m_NumShaders] - SRBMemoryAllocator m_SRBMemAllocator; + ImmutableSamplerPtrType* m_ImmutableSamplers = nullptr; // [m_Desc.NumImmutableSamplers] + SRBMemoryAllocator m_SRBMemAllocator; }; diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index 46d0fc93..0c0b07b8 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -72,24 +72,13 @@ public: /// Implementation of IPipelineStateVk::GetVkPipeline(). virtual VkPipeline DILIGENT_CALL_TYPE GetVkPipeline() const override final { return m_Pipeline; } - /// Implementation of IPipelineState::BindStaticResources() in Vulkan backend. - virtual void DILIGENT_CALL_TYPE BindStaticResources(Uint32 ShaderFlags, IResourceMapping* pResourceMapping, Uint32 Flags) override final; - /// Implementation of IPipelineState::GetResourceSignatureCount() in Vulkan backend. - virtual Uint32 DILIGENT_CALL_TYPE GetResourceSignatureCount() const override final { return m_PipelineLayout->GetSignatureCount(); } + virtual Uint32 DILIGENT_CALL_TYPE GetResourceSignatureCount() const override final { return m_PipelineLayout.GetSignatureCount(); } /// Implementation of IPipelineState::GetResourceSignature() in Vulkan backend. - virtual IPipelineResourceSignature* DILIGENT_CALL_TYPE GetResourceSignature(Uint32 Index) const override final { return m_PipelineLayout->GetSignature(Index); } - - __forceinline void BindDescriptorSetsWithDynamicOffsets(VulkanUtilities::VulkanCommandBuffer& CmdBuffer, - Uint32 CtxId, - DeviceContextVkImpl* pCtxVkImpl, - PipelineLayoutVk::DescriptorSetBindInfo& BindInfo) - { - m_PipelineLayout->BindDescriptorSetsWithDynamicOffsets(CmdBuffer, CtxId, pCtxVkImpl, BindInfo); - } + virtual IPipelineResourceSignature* DILIGENT_CALL_TYPE GetResourceSignature(Uint32 Index) const override final { return m_PipelineLayout.GetSignature(Index); } - const PipelineLayoutVk& GetPipelineLayout() const { return *m_PipelineLayout; } + const PipelineLayoutVk& GetPipelineLayout() const { return m_PipelineLayout; } static RenderPassDesc GetImplicitRenderPassDesc(Uint32 NumRenderTargets, const TEXTURE_FORMAT RTVFormats[], @@ -102,11 +91,6 @@ public: void InitializeStaticSRBResources(ShaderResourceCacheVk& ResourceCache) const; - bool IsIncompatibleWith(IPipelineResourceSignature* pPRS) const - { - return !m_PipelineLayout->HasSignature(pPRS); - } - struct ShaderStageInfo { ShaderStageInfo() {} @@ -137,13 +121,10 @@ private: void Destruct(); - void* m_pRawMem = nullptr; // AZ TODO: remove + void* m_pRawMem = nullptr; // AZ TODO: move to base class VulkanUtilities::PipelineWrapper m_Pipeline; - RefCntAutoPtr m_PipelineLayout; - - bool m_HasStaticResources = false; - bool m_HasNonStaticResources = false; + PipelineLayoutVk m_PipelineLayout; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp index 2547dbb0..d67e33d2 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp @@ -47,7 +47,6 @@ #include "VulkanUploadHeap.hpp" #include "FramebufferCache.hpp" #include "RenderPassCache.hpp" -#include "PipelineLayoutCacheVk.hpp" #include "CommandPoolManager.hpp" #include "DXCompiler.hpp" @@ -135,6 +134,10 @@ public: virtual void DILIGENT_CALL_TYPE CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, IPipelineResourceSignature** ppSignature) override final; + void CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature, + bool IsDeviceInternal); + /// Implementation of IRenderDeviceVk::GetVkDevice(). virtual VkDevice DILIGENT_CALL_TYPE GetVkDevice() override final { return m_LogicalVkDevice->GetVkDevice(); } @@ -195,8 +198,6 @@ public: FramebufferCache& GetFramebufferCache() { return m_FramebufferCache; } RenderPassCache& GetImplicitRenderPassCache() { return m_ImplicitRenderPassCache; } - PipelineLayoutCacheVk& GetPipelineLayoutCache() { return m_PipelineLayoutCache; } - VulkanUtilities::VulkanMemoryAllocation AllocateMemory(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProperties, VkMemoryAllocateFlags AllocateFlags = 0) { return m_MemoryMgr.Allocate(MemReqs, MemoryProperties, AllocateFlags); @@ -231,7 +232,7 @@ public: return m_Properties; } - void CreatePipelineLayout(IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount, PipelineLayoutVk** ppPipelineLayout); + IPipelineResourceSignature* GetEmptySignature() const { return m_pEmptySignature.RawPtr(); } private: template @@ -253,7 +254,6 @@ private: EngineVkCreateInfo m_EngineAttribs; FramebufferCache m_FramebufferCache; - PipelineLayoutCacheVk m_PipelineLayoutCache; RenderPassCache m_ImplicitRenderPassCache; DescriptorSetAllocator m_DescriptorSetAllocator; DescriptorPoolManager m_DynamicDescriptorPool; @@ -271,7 +271,7 @@ private: Properties m_Properties; - FixedBlockMemoryAllocator m_PipelineLayoutAllocator; + RefCntAutoPtr m_pEmptySignature; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 77ce5d8b..949bf29f 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -57,6 +57,7 @@ #include "DescriptorPoolManager.hpp" #include "SPIRVShaderResources.hpp" #include "BufferVkImpl.hpp" +#include "PipelineResourceSignatureVkImpl.hpp" namespace Diligent { @@ -93,13 +94,13 @@ public: static size_t GetRequiredMemorySize(Uint32 NumSets, const Uint32* SetSizes); void InitializeSets(IMemoryAllocator& MemAllocator, Uint32 NumSets, const Uint32* SetSizes); - void InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, VkDescriptorType Type); + void InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, DescriptorType Type); // sizeof(Resource) == 16 (x64, msvc, Release) struct Resource { // clang-format off - explicit Resource(VkDescriptorType _Type) noexcept : + explicit Resource(DescriptorType _Type) noexcept : Type{_Type} {} @@ -108,8 +109,8 @@ public: Resource& operator = (const Resource&) = delete; Resource& operator = (Resource&&) = delete; -/* 0 */ const VkDescriptorType Type; -/*4-7*/ // Unused +/* 0 */ const DescriptorType Type; +/*1-7*/ // Unused /* 8 */ RefCntAutoPtr pObject; VkDescriptorBufferInfo GetUniformBufferDescriptorWriteInfo () const; @@ -244,27 +245,25 @@ __forceinline Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 for (Uint32 set = 0; set < m_NumSets; ++set) { const auto& DescrSet = GetDescriptorSet(set); - Uint32 res = 0; // AZ TODO: optimize - while (res < DescrSet.GetSize()) + for (Uint32 res = 0; res < DescrSet.GetSize(); ++res) { const auto& Res = DescrSet.GetResource(res); - if (Res.Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + if (Res.Type == DescriptorType::UniformBufferDynamic) { const auto* pBufferVk = Res.pObject.RawPtr(); auto Offset = pBufferVk != nullptr ? pBufferVk->GetDynamicOffset(CtxId, pCtxVkImpl) : 0; Offsets[OffsetInd++] = Offset; } - if (Res.Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) + if (Res.Type == DescriptorType::StorageBufferDynamic || Res.Type == DescriptorType::StorageBufferDynamic_ReadOnly) { const auto* pBufferVkView = Res.pObject.RawPtr(); const auto* pBufferVk = pBufferVkView != nullptr ? pBufferVkView->GetBufferVk() : 0; auto Offset = pBufferVk != nullptr ? pBufferVk->GetDynamicOffset(CtxId, pCtxVkImpl) : 0; Offsets[OffsetInd++] = Offset; } - ++res; } } return OffsetInd; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp index 6eaaf35c..68dd7807 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp @@ -83,7 +83,8 @@ public: void Initialize(const PipelineResourceSignatureVkImpl& SrcLayout, IMemoryAllocator& Allocator, const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes); + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType); ~ShaderVariableManagerVk(); @@ -97,13 +98,14 @@ public: static size_t GetRequiredMemorySize(const PipelineResourceSignatureVkImpl& Layout, const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, Uint32 NumAllowedTypes, + SHADER_TYPE ShaderStages, Uint32& NumVariables); Uint32 GetVariableCount() const { return m_NumVariables; } private: friend ShaderVariableVkImpl; - using PackedBindingIndex = PipelineResourceSignatureVkImpl::PackedBindingIndex; + using ResourceAttribs = PipelineResourceSignatureVkImpl::ResourceAttribs; Uint32 GetVariableIndex(const ShaderVariableVkImpl& Variable); @@ -112,10 +114,10 @@ private: VERIFY_EXPR(m_pSignature); return m_pSignature->GetResource(Index); } - const PackedBindingIndex& GetBinding(Uint32 Index) const + const ResourceAttribs& GetAttribs(Uint32 Index) const { VERIFY_EXPR(m_pSignature); - return m_pSignature->GetBinding(Index); + return m_pSignature->GetAttribs(Index); } private: @@ -144,8 +146,10 @@ private: class ShaderVariableVkImpl final : public IShaderResourceVariable { public: - explicit ShaderVariableVkImpl(ShaderVariableManagerVk& ParentManager) : - m_ParentManager{ParentManager} + ShaderVariableVkImpl(ShaderVariableManagerVk& ParentManager, + Uint32 ResIndex) : + m_ParentManager{ParentManager}, + m_ResIndex{ResIndex} {} // clang-format off @@ -205,82 +209,23 @@ public: virtual Uint32 DILIGENT_CALL_TYPE GetIndex() const override final { - VERIFY_EXPR(m_ParentManager.m_pVariables); - return Uint32(size_t(this - m_ParentManager.m_pVariables)); + return m_ParentManager.GetVariableIndex(*this); } virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final; - String GetPrintName(Uint32 ArrayInd) const; - - RESOURCE_DIMENSION GetResourceDimension() const; - - bool IsMultisample() const; - private: friend ShaderVariableManagerVk; - using PackedBindingIndex = PipelineResourceSignatureVkImpl::PackedBindingIndex; + using ResourceAttribs = PipelineResourceSignatureVkImpl::ResourceAttribs; - const PipelineResourceDesc& GetDesc() const { return m_ParentManager.GetResource(GetIndex()); } - const PackedBindingIndex& GetBinding() const { return m_ParentManager.GetBinding(GetIndex()); } + const PipelineResourceDesc& GetDesc() const { return m_ParentManager.GetResource(m_ResIndex); } + const ResourceAttribs& GetAttribs() const { return m_ParentManager.GetAttribs(m_ResIndex); } void BindResource(IDeviceObject* pObj, Uint32 ArrayIndex) const; - struct UpdateInfo - { - ShaderResourceCacheVk::Resource& DstRes; - const VkDescriptorSet vkDescrSet; - const Uint32 ArrayIndex; - const SHADER_RESOURCE_VARIABLE_TYPE VarType; - const Uint16 Binding; - const Uint8 SamplerInd; - char const* const Name; - }; - - void CacheUniformBuffer(IDeviceObject* pBuffer, - UpdateInfo& Info, - Uint16& DynamicBuffersCounter) const; - - void CacheStorageBuffer(IDeviceObject* pBufferView, - UpdateInfo& Info, - Uint16& DynamicBuffersCounter) const; - - void CacheTexelBuffer(IDeviceObject* pBufferView, - UpdateInfo& Info, - Uint16& DynamicBuffersCounter) const; - - template - void CacheImage(IDeviceObject* pTexView, - UpdateInfo& Info, - TCacheSampler CacheSampler) const; - - void CacheSeparateSampler(IDeviceObject* pSampler, - UpdateInfo& Info) const; - - void CacheInputAttachment(IDeviceObject* pTexView, - UpdateInfo& Info) const; - - void CacheAccelerationStructure(IDeviceObject* pTLAS, - UpdateInfo& Info) const; - - template - bool UpdateCachedResource(UpdateInfo& Info, - RefCntAutoPtr&& pObject, - TPreUpdateObject PreUpdateObject) const; - - bool IsImmutableSamplerAssigned() const; - - // Updates resource descriptor in the descriptor set - inline void UpdateDescriptorHandle(UpdateInfo& Info, - const VkDescriptorImageInfo* pImageInfo, - const VkDescriptorBufferInfo* pBufferInfo, - const VkBufferView* pTexelBufferView, - const VkWriteDescriptorSetAccelerationStructureKHR* pAccelStructInfo = nullptr) const; - - static constexpr Uint8 InvalidSamplerInd = PipelineResourceSignatureVkImpl::InvalidSamplerInd; - private: ShaderVariableManagerVk& m_ParentManager; + const Uint32 m_ResIndex; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index bea9f29b..958a5e80 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -118,6 +118,8 @@ DeviceContextVkImpl::DeviceContextVkImpl(IReferenceCounters* p m_vkClearValues.reserve(16); + m_DynamicBufferOffsets.reserve(64); + CreateASCompactedSizeQueryPool(); } @@ -231,8 +233,6 @@ inline void DeviceContextVkImpl::DisposeCurrentCmdBuffer(Uint32 CmdQueue, Uint64 void DeviceContextVkImpl::SetPipelineState(IPipelineState* pPipelineState) { auto* pPipelineStateVk = ValidatedCast(pPipelineState); - BindShaderResources(pPipelineStateVk); - if (PipelineStateVkImpl::IsSameObject(m_pPipelineState, pPipelineStateVk)) return; @@ -308,6 +308,8 @@ void DeviceContextVkImpl::SetPipelineState(IPipelineState* pPipelineState) default: UNEXPECTED("unknown pipeline type"); } + + BindShaderResources(pPipelineStateVk); } static Uint32 PipelineTypeToBindPointIndex(PIPELINE_TYPE Type) @@ -338,32 +340,156 @@ void DeviceContextVkImpl::BindShaderResources(PipelineStateVkImpl* pPipelineStat { VERIFY_EXPR(pPipelineStateVk != nullptr); - const auto& Layout = pPipelineStateVk->GetPipelineLayout(); - auto& Resources = m_ShaderResources[PipelineTypeToBindPointIndex(Layout.GetPipelineType())]; - auto& DescrSetBindInfo = m_DescrSetBindInfo[PipelineTypeToBindPointIndex(Layout.GetPipelineType())]; + const auto& Layout = pPipelineStateVk->GetPipelineLayout(); + const auto BindIndex = PipelineTypeToBindPointIndex(pPipelineStateVk->GetDesc().PipelineType); + auto& BindInfo = m_DescrSetBindInfo[BindIndex]; + auto& Resources = BindInfo.Resources; + const auto BindPoint = PipelineTypeToBindPoint(pPipelineStateVk->GetDesc().PipelineType); + const auto SignCount = Layout.GetSignatureCount(); + Uint32 CompatSignCount = SignCount; -#ifdef DILIGENT_DEVELOPMENT - for (Uint32 i = 0, Count = Layout.GetSignatureCount(); i < Count; ++i) + // Layout compatibility means that descriptor sets can be bound to a command buffer + // for use by any pipeline created with a compatible pipeline layout, and without having bound + // a particular pipeline first. It also means that descriptor sets can remain valid across + // a pipeline change, and the same resources will be accessible to the newly bound pipeline. + // (14.2.2. Pipeline Layouts, clause 'Pipeline Layout Compatibility') + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#descriptorsets-compatibility + + // unbind incompatible descriptor sets + for (Uint32 i = 0; i < SignCount; ++i) { auto* LayoutSign = Layout.GetSignature(i); - auto* ResSign = Resources[i] ? Resources[i]->GetPipelineResourceSignature() : nullptr; + VERIFY_EXPR(LayoutSign != nullptr); - if (LayoutSign != ResSign) + if (Resources[i] != nullptr && LayoutSign->IsIncompatibleWith(*Resources[i]->GetSignature())) { - LOG_ERROR_MESSAGE("Shader resource binding '", (ResSign ? ResSign->GetDesc().Name : ""), - "' is not compatible with pipeline layout in current pipeline '", pPipelineStateVk->GetDesc().Name, "'."); - Resources[i] = nullptr; + Resources[i] = nullptr; + CompatSignCount = std::min(CompatSignCount, i + 1); } } + + // reset unused states + for (Uint32 i = CompatSignCount; i < MAX_RESOURCE_SIGNATURES; ++i) + { + Resources[i] = nullptr; + + BindInfo.PendingVkSet[i] = false; // AZ TODO: can be optimized + BindInfo.PendingDynamicDescriptors[i] = false; + BindInfo.DynamicBuffersPresent[i] = false; + +#ifdef DILIGENT_DEBUG + BindInfo.vkSets[i * MAX_DESCR_SET_PER_SIGNATURE + 0] = VK_NULL_HANDLE; + BindInfo.vkSets[i * MAX_DESCR_SET_PER_SIGNATURE + 1] = VK_NULL_HANDLE; +#endif + } +} + +void DeviceContextVkImpl::BindDescriptorSetsWithDynamicOffsets(DescriptorSetBindInfo& BindInfo) +{ + auto* pPipelineStateVk = ValidatedCast(m_pPipelineState.RawPtr()); + const auto& Layout = pPipelineStateVk->GetPipelineLayout(); + const auto BindIndex = PipelineTypeToBindPointIndex(pPipelineStateVk->GetDesc().PipelineType); + auto& Resources = BindInfo.Resources; + const auto BindPoint = PipelineTypeToBindPoint(pPipelineStateVk->GetDesc().PipelineType); + const auto SignCount = Layout.GetSignatureCount(); + auto& Offsets = m_DynamicBufferOffsets; + const auto VkLayout = Layout.GetVkPipelineLayout(); + + for (Uint32 i = 0; i < SignCount; ++i) + { + VERIFY_EXPR(Resources[i] != nullptr); + + if ((BindInfo.PendingVkSet[i] || BindInfo.PendingDynamicDescriptors[i]) && Resources[i] != nullptr) + { + const auto* pSignature = Resources[i]->GetSignature(); + const Uint32 DescrSetIdx = Layout.GetDescrSetIndex(pSignature); + const auto& ResourceCache = Resources[i]->GetResourceCache(); + const Uint32 DSCount = ResourceCache.GetNumDescriptorSets(); + const auto DSOffset = i * MAX_DESCR_SET_PER_SIGNATURE; + +#ifdef DILIGENT_DEBUG + for (Uint32 s = 0; s < DSCount; ++s) + VERIFY_EXPR(BindInfo.vkSets[DSOffset + s] != VK_NULL_HANDLE); #endif + if (pSignature->GetDynamicBufferCount() > 0) + { + Offsets.resize(pSignature->GetDynamicBufferCount()); + + auto NumOffsetsWritten = ResourceCache.GetDynamicBufferOffsets(m_ContextId, this, Offsets); + VERIFY_EXPR(NumOffsetsWritten == pSignature->GetDynamicBufferCount()); + + // Note that there is one global dynamic buffer from which all dynamic resources are suballocated in Vulkan back-end, + // and this buffer is not resizable, so the buffer handle can never change. + + // vkCmdBindDescriptorSets causes the sets numbered [firstSet .. firstSet+descriptorSetCount-1] to use the + // bindings stored in pDescriptorSets[0 .. descriptorSetCount-1] for subsequent rendering commands + // (either compute or graphics, according to the pipelineBindPoint). Any bindings that were previously + // applied via these sets are no longer valid (13.2.5) + m_CommandBuffer.BindDescriptorSets(BindPoint, VkLayout, DescrSetIdx, DSCount, &BindInfo.vkSets[DSOffset], static_cast(Offsets.size()), Offsets.data()); + BindInfo.PendingDynamicDescriptors[i] = false; + } + else + { + VERIFY_EXPR(!BindInfo.PendingDynamicDescriptors[i]); + m_CommandBuffer.BindDescriptorSets(BindPoint, VkLayout, DescrSetIdx, DSCount, &BindInfo.vkSets[DSOffset], 0, nullptr); + } - // the number of bound descriptor sets and dynamic offsets may be greater then actually used in pipeline layout, - // see https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#descriptorsets-compatibility - VERIFY_EXPR(Layout.GetDescriptorSetCount() <= DescrSetBindInfo.SetCout); - VERIFY_EXPR(Layout.GetDynamicOffsetCount() <= DescrSetBindInfo.DynamicOffsetCount); + BindInfo.PendingVkSet[i] = false; + } + } - // AZ TODO: optimize - bind DS that was changed - GetCommandBuffer().BindDescriptorSets(PipelineTypeToBindPoint(Layout.GetPipelineType()), Layout.GetVkPipelineLayout(), 0, Layout.GetDescriptorSetCount(), DescrSetBindInfo.vkSets.data(), Layout.GetDynamicOffsetCount(), DescrSetBindInfo.DynamicOffsets.data()); + VERIFY_EXPR(!BindInfo.PendingVkSet.any()); + VERIFY_EXPR(!BindInfo.PendingDynamicDescriptors.any()); +} + +void DeviceContextVkImpl::ValidateShaderResources() +{ +#ifdef DILIGENT_DEBUG + auto* pPipelineStateVk = ValidatedCast(m_pPipelineState.RawPtr()); + const auto& Layout = pPipelineStateVk->GetPipelineLayout(); + const auto BindIndex = PipelineTypeToBindPointIndex(pPipelineStateVk->GetDesc().PipelineType); + auto& BindInfo = m_DescrSetBindInfo[BindIndex]; + const auto SignCount = Layout.GetSignatureCount(); + + for (Uint32 i = 0; i < SignCount; ++i) + { + auto* LayoutSign = Layout.GetSignature(i); + if (LayoutSign == nullptr) + { + LOG_ERROR_MESSAGE("Pipeline resource signature for index (", i, ") is null, this may indicate a bug"); + return; + } + + if (BindInfo.Resources[i] == nullptr) + { + LOG_ERROR_MESSAGE("Shader resource binding is not bound to index (", i, ")."); + return; + } + + auto* ResSign = BindInfo.Resources[i]->GetSignature(); + VERIFY_EXPR(ResSign != nullptr); + + if (!LayoutSign->IsCompatibleWith(*ResSign)) + { + LOG_ERROR_MESSAGE("Shader resource binding with signature '", ResSign->GetDesc().Name, + "' is not compatible with pipeline layout in current pipeline '", pPipelineStateVk->GetDesc().Name, "'."); + } + + // You must call BindDescriptorSetsWithDynamicOffsets() before validation. + VERIFY_EXPR(!BindInfo.PendingVkSet[i]); + VERIFY_EXPR(!BindInfo.PendingDynamicDescriptors[i]); + + const auto DSCount = LayoutSign->GetNumDescriptorSets(); + const auto DSOffset = i * MAX_DESCR_SET_PER_SIGNATURE; + + for (Uint32 s = 0; s < DSCount; ++s) + { + VERIFY(BindInfo.vkSets[DSOffset + s] != VK_NULL_HANDLE, + "descriptor set with index (", s, ") is not bound for resource signature '", + LayoutSign->GetDesc().Name, "' binding index (", i, ")."); + } + } +#endif } void DeviceContextVkImpl::TransitionShaderResources(IPipelineState*, IShaderResourceBinding* pShaderResourceBinding) @@ -377,7 +503,7 @@ void DeviceContextVkImpl::TransitionShaderResources(IPipelineState*, IShaderReso if (pShaderResourceBinding == nullptr) { - LOG_ERROR_MESSAGE("TODO"); + LOG_ERROR_MESSAGE("AZ TODO"); return; } #endif @@ -411,14 +537,53 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR } #endif - Uint32 Index = PipelineTypeToBindPointIndex(pResBindingVkImpl->GetPipelineType()); - auto& DescrSetBindInfo = m_DescrSetBindInfo[Index]; - auto& ResourceBinding = m_ShaderResources[Index]; + const auto BindIndex = PipelineTypeToBindPointIndex(pResBindingVkImpl->GetPipelineType()); + const auto Index = pResBindingVkImpl->GetBindingIndex(); + auto& BindInfo = m_DescrSetBindInfo[BindIndex]; + const auto* pSignature = pResBindingVkImpl->GetSignature(); + const auto DSOffset = Index * MAX_DESCR_SET_PER_SIGNATURE; + const auto DSCount = ResourceCache.GetNumDescriptorSets(); + Uint32 DSIndex = 0; + + BindInfo.PendingVkSet[Index] = true; + BindInfo.PendingDynamicDescriptors[Index] = false; + BindInfo.DynamicBuffersPresent[Index] = ResourceCache.GetNumDynamicBuffers() > 0; - // AZ TODO: create dynamic descriptor set - (void)(DescrSetBindInfo); + if (pSignature->HasStaticDescrSet()) + { + VERIFY_EXPR(DSIndex < DSCount); + BindInfo.vkSets[DSOffset + DSIndex] = ResourceCache.GetDescriptorSet(DSIndex).GetVkDescriptorSet(); + ++DSIndex; + } - ResourceBinding[pResBindingVkImpl->GetBindingIndex()] = pResBindingVkImpl; + if (auto VkLayout = pSignature->GetDynamicVkDescriptorSetLayout()) + { + VERIFY_EXPR(DSIndex < DSCount); + VERIFY_EXPR(ResourceCache.GetDescriptorSet(DSIndex).GetVkDescriptorSet() == VK_NULL_HANDLE); + + VkDescriptorSet DynamicDescrSet = VK_NULL_HANDLE; + const char* DynamicDescrSetName = "Dynamic Descriptor Set"; +#ifdef DILIGENT_DEVELOPMENT + String _DynamicDescrSetName("AZ TODO"); + _DynamicDescrSetName.append(" - dynamic set"); + DynamicDescrSetName = _DynamicDescrSetName.c_str(); +#endif + // Allocate vulkan descriptor set for dynamic resources + DynamicDescrSet = AllocateDynamicDescriptorSet(VkLayout, DynamicDescrSetName); + + // Commit all dynamic resource descriptors + pSignature->CommitDynamicResources(ResourceCache, DynamicDescrSet); + + BindInfo.vkSets[DSOffset + DSIndex] = DynamicDescrSet; + ++DSIndex; + } + + if (pSignature->GetDynamicBufferCount() > 0) + BindInfo.PendingDynamicDescriptors[Index] = true; + + VERIFY_EXPR(DSIndex == DSCount); + + BindInfo.Resources[Index] = pResBindingVkImpl; } void DeviceContextVkImpl::SetStencilRef(Uint32 StencilRef) @@ -558,18 +723,14 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) } #endif - auto& DescrSetBindInfo = m_DescrSetBindInfo[0]; + auto& DescrSetBindInfo = m_DescrSetBindInfo[PipelineTypeToBindPointIndex(PIPELINE_TYPE_GRAPHICS)]; - if (DescrSetBindInfo.DynamicOffsetCount != 0) + // First time we must always bind descriptor sets with dynamic offsets. + // If there are no dynamic buffers bound in the resource cache, for all subsequent + // cals we do not need to bind the sets again. + if (DescrSetBindInfo.RequireUpdate(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT)) { - // First time we must always bind descriptor sets with dynamic offsets. - // If there are no dynamic buffers bound in the resource cache, for all subsequent - // cals we do not need to bind the sets again. - if (!DescrSetBindInfo.DynamicDescriptorsBound || - (DescrSetBindInfo.DynamicBuffersPresent && (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) == 0)) - { - m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, DescrSetBindInfo); - } + BindDescriptorSetsWithDynamicOffsets(DescrSetBindInfo); } #if 0 # ifdef DILIGENT_DEBUG @@ -594,6 +755,8 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) CommitRenderPassAndFramebuffer((Flags & DRAW_FLAG_VERIFY_STATES) != 0); } + + ValidateShaderResources(); } BufferVkImpl* DeviceContextVkImpl::PrepareIndirectDrawAttribsBuffer(IBuffer* pAttribsBuffer, RESOURCE_STATE_TRANSITION_MODE TransitonMode) @@ -713,14 +876,11 @@ void DeviceContextVkImpl::PrepareForDispatchCompute() if (m_CommandBuffer.GetState().RenderPass != VK_NULL_HANDLE) m_CommandBuffer.EndRenderPass(); - auto& DescrSetBindInfo = m_DescrSetBindInfo[1]; + auto& DescrSetBindInfo = m_DescrSetBindInfo[PipelineTypeToBindPointIndex(PIPELINE_TYPE_COMPUTE)]; - if (DescrSetBindInfo.DynamicOffsetCount != 0) + if (DescrSetBindInfo.RequireUpdate()) { - if (!DescrSetBindInfo.DynamicDescriptorsBound || DescrSetBindInfo.DynamicBuffersPresent) - { - m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, DescrSetBindInfo); - } + BindDescriptorSetsWithDynamicOffsets(DescrSetBindInfo); } #if 0 # ifdef DILIGENT_DEBUG @@ -731,21 +891,22 @@ void DeviceContextVkImpl::PrepareForDispatchCompute() } # endif #endif + + ValidateShaderResources(); } void DeviceContextVkImpl::PrepareForRayTracing() { EnsureVkCmdBuffer(); - auto& DescrSetBindInfo = m_DescrSetBindInfo[2]; + auto& DescrSetBindInfo = m_DescrSetBindInfo[PipelineTypeToBindPointIndex(PIPELINE_TYPE_RAY_TRACING)]; - if (DescrSetBindInfo.DynamicOffsetCount != 0) + if (DescrSetBindInfo.RequireUpdate()) { - if (!DescrSetBindInfo.DynamicDescriptorsBound || DescrSetBindInfo.DynamicBuffersPresent) - { - m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, DescrSetBindInfo); - } + BindDescriptorSetsWithDynamicOffsets(DescrSetBindInfo); } + + ValidateShaderResources(); } void DeviceContextVkImpl::DispatchCompute(const DispatchComputeAttribs& Attribs) @@ -1129,9 +1290,6 @@ void DeviceContextVkImpl::Flush() for (auto& BindInfo : m_DescrSetBindInfo) BindInfo.Reset(); - for (auto& ResourcesPerBindPoint : m_ShaderResources) - ResourcesPerBindPoint.fill({}); - m_State = ContextState{}; m_CommandBuffer.Reset(); m_pPipelineState = nullptr; @@ -1172,9 +1330,6 @@ void DeviceContextVkImpl::InvalidateState() for (auto& BindInfo : m_DescrSetBindInfo) BindInfo.Reset(); - for (auto& ResourcesPerBindPoint : m_ShaderResources) - ResourcesPerBindPoint.fill({}); - VERIFY(m_CommandBuffer.GetState().RenderPass == VK_NULL_HANDLE, "Invalidating context with unifinished render pass"); m_CommandBuffer.Reset(); } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp deleted file mode 100644 index 6ca53a44..00000000 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#include "PipelineLayoutCacheVk.hpp" -#include "RenderDeviceVkImpl.hpp" - -namespace Diligent -{ - -bool PipelineLayoutCacheVk::PipelineLayoutCompare::operator()(const PipelineLayoutVk* lhs, const PipelineLayoutVk* rhs) const noexcept -{ - if (lhs->GetSignatureCount() != rhs->GetSignatureCount()) - return false; - - for (Uint32 i = 0, Cnt = lhs->GetSignatureCount(); i < Cnt; ++i) - { - if (lhs->GetSignature(i) != rhs->GetSignature(i)) - return false; - } - return true; -} - -PipelineLayoutCacheVk::~PipelineLayoutCacheVk() -{ - std::lock_guard Lock{m_Mutex}; - VERIFY(m_Cache.empty(), "All pipeline layouts must be released"); -} - -RefCntAutoPtr PipelineLayoutCacheVk::GetLayout(IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount) -{ - RefCntAutoPtr pNewLayout; - m_DeviceVk.CreatePipelineLayout(ppSignatures, SignatureCount, &pNewLayout); - - if (pNewLayout == nullptr) - return {}; - - std::lock_guard Lock{m_Mutex}; - - PipelineLayoutVk* pResult = *m_Cache.insert(pNewLayout).first; - - if (pNewLayout == pResult) - { - pNewLayout->Finalize(); - void(pNewLayout.Detach()); - } - return RefCntAutoPtr{pResult}; -} - -void PipelineLayoutCacheVk::OnDestroyLayout(PipelineLayoutVk* pLayout) -{ - std::lock_guard Lock{m_Mutex}; - - m_Cache.erase(pLayout); -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp index 446c41bb..376417ce 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp @@ -39,69 +39,90 @@ namespace Diligent { -PipelineLayoutVk::PipelineLayoutVk(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount) : - ObjectBase{pRefCounters}, - m_pDeviceVk{pDeviceVk}, - m_SignatureCount{0} +PipelineLayoutVk::PipelineLayoutVk() : + m_DynamicOffsetCount{0}, + m_SignatureCount{0}, + m_DescrSetCount{0} { +} + +void PipelineLayoutVk::Release(RenderDeviceVkImpl* pDeviceVk, Uint64 CommandQueueMask) +{ + pDeviceVk->SafeReleaseDeviceObject(std::move(m_VkPipelineLayout), CommandQueueMask); +} + +void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDeviceVk, IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount) +{ + VERIFY(m_DynamicOffsetCount == 0 && m_SignatureCount == 0 && m_DescrSetCount == 0, + "pipeline layout is already initialized"); + for (Uint32 i = 0; i < SignatureCount; ++i) { - auto* pSign = ValidatedCast(ppSignatures[i]); - if (pSign) - { - const Uint8 Index = pSign->GetDesc().BindingIndex; + auto* pSignature = ValidatedCast(ppSignatures[i]); + if (!pSignature) + LOG_ERROR_AND_THROW("pipeline resource signature (", i, ") must not be null"); - if (Index >= m_Signatures.size()) - LOG_ERROR_AND_THROW("Pipeline resource signature '", pSign->GetDesc().Name, "' index (", Uint32{Index}, ") must be less than (", m_Signatures.size(), ")."); + const Uint32 Index = pSignature->GetDesc().BindingIndex; - if (m_Signatures[Index] != nullptr) - LOG_ERROR_AND_THROW("Pipeline resource signature '", pSign->GetDesc().Name, "' with index (", Uint32{Index}, ") overrides another resource signature '", m_Signatures[Index]->GetDesc().Name, "'."); + if (Index >= m_Signatures.size()) + LOG_ERROR_AND_THROW("Pipeline resource signature '", pSignature->GetDesc().Name, "' index (", Uint32{Index}, ") must be less than (", m_Signatures.size(), ")."); - m_SignatureCount = std::max(m_SignatureCount, Uint8(Index + 1)); - m_Signatures[Index] = pSign; + if (m_Signatures[Index] != nullptr) + LOG_ERROR_AND_THROW("Pipeline resource signature '", pSignature->GetDesc().Name, "' with index (", Uint32{Index}, ") overrides another resource signature '", m_Signatures[Index]->GetDesc().Name, "'."); - if (m_PipelineType <= PIPELINE_TYPE_LAST) - { - if (m_PipelineType != pSign->GetPipelineType()) - { - LOG_ERROR_AND_THROW("Pipeline resource signature '", pSign->GetDesc().Name, "' with index (", Uint32{Index}, ") has different pipeline type (", - GetPipelineTypeString(pSign->GetPipelineType()), ") than other (", GetPipelineTypeString(m_PipelineType), ")."); - } - } - else - m_PipelineType = pSign->GetPipelineType(); - } + m_SignatureCount = std::max(m_SignatureCount, Index + 1); + m_Signatures[Index] = pSignature; } -} -PipelineLayoutVk::~PipelineLayoutVk() -{ - m_pDeviceVk->GetPipelineLayoutCache().OnDestroyLayout(this); - m_pDeviceVk->SafeReleaseDeviceObject(std::move(m_VkPipelineLayout), ~0ull); -} + auto* pEmptySign = ValidatedCast(pDeviceVk->GetEmptySignature()); + + for (Uint32 i = 0; i < m_SignatureCount; ++i) + { + if (m_Signatures[i] == nullptr) + m_Signatures[i] = pEmptySign; + } -void PipelineLayoutVk::Finalize() -{ std::array DescSetLayouts; Uint32 DescSetLayoutCount = 0; - Uint32 DynamicBufferCount = 0; + Uint32 DynamicOffsetCount = 0; +#ifdef DILIGENT_DEBUG + Uint32 DynamicUniformBufferCount = 0; + Uint32 DynamicStorageBufferCount = 0; +#endif for (Uint32 i = 0; i < m_SignatureCount; ++i) { - if (m_Signatures[i] == nullptr) - continue; + const auto& pSignature = m_Signatures[i]; + VERIFY_EXPR(pSignature != nullptr); m_DescSetOffset[i] = static_cast(DescSetLayoutCount); - m_DynBufOffset[i] = static_cast(DynamicBufferCount); + m_DynBufOffset[i] = static_cast(DynamicOffsetCount); - auto StaticDSLayout = m_Signatures[i]->GetStaticVkDescriptorSetLayout(); - auto DynamicDSLayout = m_Signatures[i]->GetDynamicVkDescriptorSetLayout(); + auto StaticDSLayout = pSignature->GetStaticVkDescriptorSetLayout(); + auto DynamicDSLayout = pSignature->GetDynamicVkDescriptorSetLayout(); if (StaticDSLayout) DescSetLayouts[DescSetLayoutCount++] = StaticDSLayout; if (DynamicDSLayout) DescSetLayouts[DescSetLayoutCount++] = DynamicDSLayout; - DynamicBufferCount += m_Signatures[i]->GetDynamicBufferCount(); + DynamicOffsetCount += pSignature->GetDynamicBufferCount(); + +#ifdef DILIGENT_DEBUG + for (Uint32 r = 0, ResCount = pSignature->GetTotalResourceCount(); r < ResCount; ++r) + { + const auto& Attr = pSignature->GetAttribs(r); + + if (Attr.Type == DescriptorType::UniformBufferDynamic) + { + ++DynamicUniformBufferCount; + } + else if (Attr.Type == DescriptorType::StorageBufferDynamic || + Attr.Type == DescriptorType::StorageBufferDynamic_ReadOnly) + { + ++DynamicStorageBufferCount; + } + } +#endif } VkPipelineLayoutCreateInfo PipelineLayoutCI = {}; @@ -113,14 +134,19 @@ void PipelineLayoutVk::Finalize() PipelineLayoutCI.pSetLayouts = DescSetLayoutCount ? DescSetLayouts.data() : nullptr; PipelineLayoutCI.pushConstantRangeCount = 0; PipelineLayoutCI.pPushConstantRanges = nullptr; - m_VkPipelineLayout = m_pDeviceVk->GetLogicalDevice().CreatePipelineLayout(PipelineLayoutCI); + m_VkPipelineLayout = pDeviceVk->GetLogicalDevice().CreatePipelineLayout(PipelineLayoutCI); - const auto& Limits = m_pDeviceVk->GetPhysicalDevice().GetProperties().limits; + const auto& Limits = pDeviceVk->GetPhysicalDevice().GetProperties().limits; VERIFY_EXPR(DescSetLayoutCount <= Limits.maxBoundDescriptorSets); - VERIFY_EXPR(DynamicBufferCount <= (Limits.maxDescriptorSetUniformBuffersDynamic + Limits.maxDescriptorSetStorageBuffersDynamic)); // AZ TODO + VERIFY_EXPR(DescSetLayoutCount <= MAX_RESOURCE_SIGNATURES * 2); + +#ifdef DILIGENT_DEBUG + VERIFY_EXPR(DynamicUniformBufferCount <= Limits.maxDescriptorSetUniformBuffersDynamic); + VERIFY_EXPR(DynamicStorageBufferCount <= Limits.maxDescriptorSetStorageBuffersDynamic); +#endif m_DescrSetCount = static_cast(DescSetLayoutCount); - m_DynamicOffsetCount = static_cast(DynamicBufferCount); + m_DynamicOffsetCount = DynamicOffsetCount; } size_t PipelineLayoutVk::GetHash() const @@ -129,10 +155,8 @@ size_t PipelineLayoutVk::GetHash() const HashCombine(hash, m_SignatureCount); for (Uint32 i = 0; i < m_SignatureCount; ++i) { - if (m_Signatures[i] != nullptr) - HashCombine(hash, m_Signatures[i]->GetHash()); - else - HashCombine(hash, 0); + VERIFY_EXPR(m_Signatures[i] != nullptr); + HashCombine(hash, m_Signatures[i]->GetHash()); } return hash; } @@ -140,27 +164,22 @@ size_t PipelineLayoutVk::GetHash() const bool PipelineLayoutVk::GetResourceInfo(const char* Name, SHADER_TYPE Stage, ResourceInfo& Info) const { // AZ TODO: optimize - for (Uint32 i = 0, Count = GetSignatureCount(); i < Count; ++i) + for (Uint32 i = 0, DSCount = GetSignatureCount(); i < DSCount; ++i) { - auto* pSign = GetSignature(i); - if (pSign) + auto* pSignature = GetSignature(i); + VERIFY_EXPR(pSignature != nullptr); + + for (Uint32 r = 0, ResCount = pSignature->GetTotalResourceCount(); r < ResCount; ++r) { - auto& Desc = pSign->GetDesc(); - Uint32 BindingCount[2] = {}; + const auto& Res = pSignature->GetResource(r); + const auto& Attr = pSignature->GetAttribs(r); - for (Uint32 j = 0; j < Desc.NumResources; ++j) + if ((Res.ShaderStages & Stage) && strcmp(Res.Name, Name) == 0) { - auto& Res = Desc.Resources[j]; - Uint16 DSIndex = (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC ? 1 : 0); - - if ((Res.ShaderStages & Stage) && strcmp(Res.Name, Name) == 0) - { - Info.Type = Res.ResourceType; - Info.BindingIndex = static_cast(BindingCount[DSIndex]); - Info.DescrSetIndex = m_DescSetOffset[i] + DSIndex; - return true; - } - BindingCount[DSIndex] += Res.ArraySize; + Info.Type = Res.ResourceType; + Info.BindingIndex = static_cast(Attr.BindingIndex); + Info.DescrSetIndex = m_DescSetOffset[i] + Attr.DescrSet; + return true; } } } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index da646a96..8513a161 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -30,124 +30,231 @@ #include "ShaderResourceBindingVkImpl.hpp" #include "RenderDeviceVkImpl.hpp" #include "VulkanTypeConversions.hpp" +#include "SamplerVkImpl.hpp" +#include "TextureViewVkImpl.hpp" +#include "TopLevelASVkImpl.hpp" +#include "BasicMath.hpp" +#include "DynamicLinearAllocator.hpp" namespace Diligent { +namespace +{ +VkDescriptorType GetVkDescriptorType(DescriptorType Type) +{ + static_assert(static_cast(DescriptorType::Count) == 16, "Please update the switch below to handle the new descriptor type"); + switch (Type) + { + // clang-format off + case DescriptorType::Sampler: return VK_DESCRIPTOR_TYPE_SAMPLER; + case DescriptorType::CombinedImageSampler: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + case DescriptorType::SeparateImage: return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + case DescriptorType::StorageImage: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + case DescriptorType::StorageImage_ReadOnly: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + case DescriptorType::UniformTexelBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + case DescriptorType::StorageTexelBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + case DescriptorType::StorageTexelBuffer_ReadOnly: return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + case DescriptorType::UniformBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case DescriptorType::UniformBufferDynamic: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + case DescriptorType::StorageBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + case DescriptorType::StorageBuffer_ReadOnly: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + case DescriptorType::StorageBufferDynamic: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + case DescriptorType::StorageBufferDynamic_ReadOnly: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + case DescriptorType::InputAttachment: return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + case DescriptorType::AccelerationStructure: return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + // clang-format on + default: + UNEXPECTED("unknown descriptor type"); + return VK_DESCRIPTOR_TYPE_MAX_ENUM; + } +} -VkDescriptorType PipelineResourceSignatureVkImpl::GetVkDescriptorType(const PipelineResourceDesc& Res) +DescriptorType GetDescriptorType(const PipelineResourceDesc& Res) { const bool WithDynamicOffset = !(Res.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_OFFSETS); const bool CombinedSampler = (Res.Flags & PIPELINE_RESOURCE_FLAG_COMBINED_IMAGE); const bool UseTexelBuffer = (Res.Flags & PIPELINE_RESOURCE_FLAG_TEXEL_BUFFER); - VERIFY_EXPR(WithDynamicOffset ^ UseTexelBuffer); - VERIFY_EXPR(CombinedSampler ? !(WithDynamicOffset | UseTexelBuffer) : true); - static_assert(SHADER_RESOURCE_TYPE_LAST == SHADER_RESOURCE_TYPE_ACCEL_STRUCT, "Please update the switch below to handle the new shader resource type"); switch (Res.ResourceType) + { + case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: + VERIFY_EXPR(!(Res.Flags & ~PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_OFFSETS)); + return WithDynamicOffset ? DescriptorType::UniformBufferDynamic : DescriptorType::UniformBuffer; + + case SHADER_RESOURCE_TYPE_BUFFER_UAV: + VERIFY_EXPR(!(Res.Flags & ~(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_OFFSETS | PIPELINE_RESOURCE_FLAG_TEXEL_BUFFER))); + return UseTexelBuffer ? DescriptorType::StorageTexelBuffer : + (WithDynamicOffset ? DescriptorType::StorageBufferDynamic : DescriptorType::StorageBuffer); + + case SHADER_RESOURCE_TYPE_TEXTURE_SRV: + VERIFY_EXPR(!(Res.Flags & ~PIPELINE_RESOURCE_FLAG_COMBINED_IMAGE)); + return CombinedSampler ? DescriptorType::CombinedImageSampler : DescriptorType::SeparateImage; + + case SHADER_RESOURCE_TYPE_BUFFER_SRV: + VERIFY_EXPR(!(Res.Flags & ~(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_OFFSETS | PIPELINE_RESOURCE_FLAG_TEXEL_BUFFER))); + return UseTexelBuffer ? DescriptorType::UniformTexelBuffer : + (WithDynamicOffset ? DescriptorType::StorageBufferDynamic_ReadOnly : DescriptorType::StorageBuffer_ReadOnly); + + case SHADER_RESOURCE_TYPE_TEXTURE_UAV: + VERIFY_EXPR(!Res.Flags); + return DescriptorType::StorageImage; + + case SHADER_RESOURCE_TYPE_SAMPLER: + VERIFY_EXPR(!Res.Flags); + return DescriptorType::Sampler; + + case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: + VERIFY_EXPR(!Res.Flags); + return DescriptorType::InputAttachment; + + case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: + VERIFY_EXPR(!Res.Flags); + return DescriptorType::AccelerationStructure; + + default: + UNEXPECTED("unknown resource type"); + return DescriptorType::Unknown; + } +} + +BUFFER_VIEW_TYPE DescriptorTypeToBufferView(DescriptorType Type) +{ + static_assert(static_cast(DescriptorType::Count) == 16, "Please update the switch below to handle the new descriptor type"); + switch (Type) + { + // clang-format off + case DescriptorType::UniformTexelBuffer: + case DescriptorType::StorageTexelBuffer_ReadOnly: + case DescriptorType::StorageBuffer_ReadOnly: + case DescriptorType::StorageBufferDynamic_ReadOnly: return BUFFER_VIEW_SHADER_RESOURCE; + case DescriptorType::StorageTexelBuffer: + case DescriptorType::StorageBuffer: + case DescriptorType::StorageBufferDynamic: return BUFFER_VIEW_UNORDERED_ACCESS; + // clang-format on + default: + UNEXPECTED("unsupported descriptor type for buffer view"); + return BUFFER_VIEW_UNDEFINED; + } +} + +TEXTURE_VIEW_TYPE DescriptorTypeToTextureView(DescriptorType Type) +{ + static_assert(static_cast(DescriptorType::Count) == 16, "Please update the switch below to handle the new descriptor type"); + switch (Type) { // clang-format off - case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: return WithDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - case SHADER_RESOURCE_TYPE_BUFFER_UAV: return UseTexelBuffer ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER : (WithDynamicOffset ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); - case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return CombinedSampler ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - case SHADER_RESOURCE_TYPE_BUFFER_SRV: return UseTexelBuffer ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER : (WithDynamicOffset ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); - case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - case SHADER_RESOURCE_TYPE_SAMPLER: return VK_DESCRIPTOR_TYPE_SAMPLER; - case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; - default: UNEXPECTED("unknown resource type"); + case DescriptorType::StorageImage: return TEXTURE_VIEW_UNORDERED_ACCESS; + case DescriptorType::CombinedImageSampler: + case DescriptorType::SeparateImage: + case DescriptorType::StorageImage_ReadOnly: + case DescriptorType::InputAttachment: return TEXTURE_VIEW_SHADER_RESOURCE; // clang-format on + default: + UNEXPECTED("unsupported descriptor type for texture view"); + return TEXTURE_VIEW_UNDEFINED; } - return VK_DESCRIPTOR_TYPE_MAX_ENUM; } +Int32 FindImmutableSampler(const PipelineResourceDesc& Res, + DescriptorType DescType, + const PipelineResourceSignatureDesc& Desc, + const char* SamplerSuffix) +{ + if (DescType == DescriptorType::CombinedImageSampler) + { + SamplerSuffix = nullptr; + } + else if (DescType == DescriptorType::Sampler) + { + // Use SamplerSuffix. If HLSL-style combined images samplers are not used, + // SamplerSuffix will be null and we will be looking for the sampler itself. + } + else + { + UNEXPECTED("Immutable sampler can only be assigned to a sampled image or separate sampler"); + return -1; + } + + for (Uint32 s = 0; s < Desc.NumImmutableSamplers; ++s) + { + const auto& ImtblSam = Desc.ImmutableSamplers[s]; + if (((ImtblSam.ShaderStages & Res.ShaderStages) != 0) && StreqSuff(Res.Name, ImtblSam.SamplerOrTextureName, SamplerSuffix)) + { + VERIFY_EXPR((ImtblSam.ShaderStages & Res.ShaderStages) == Res.ShaderStages); + return s; + } + } + + return -1; +} +} // namespace + + +RESOURCE_STATE DescriptorTypeToResourceState(DescriptorType Type) +{ + static_assert(static_cast(DescriptorType::Count) == 16, "Please update the switch below to handle the new descriptor type"); + switch (Type) + { + // clang-format off + case DescriptorType::Sampler: return RESOURCE_STATE_UNKNOWN; + case DescriptorType::CombinedImageSampler: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::SeparateImage: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::StorageImage: return RESOURCE_STATE_UNORDERED_ACCESS; + case DescriptorType::StorageImage_ReadOnly: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::UniformTexelBuffer: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::StorageTexelBuffer: return RESOURCE_STATE_UNORDERED_ACCESS; + case DescriptorType::StorageTexelBuffer_ReadOnly: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::UniformBuffer: return RESOURCE_STATE_CONSTANT_BUFFER; + case DescriptorType::UniformBufferDynamic: return RESOURCE_STATE_CONSTANT_BUFFER; + case DescriptorType::StorageBuffer: return RESOURCE_STATE_UNORDERED_ACCESS; + case DescriptorType::StorageBuffer_ReadOnly: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::StorageBufferDynamic: return RESOURCE_STATE_UNORDERED_ACCESS; + case DescriptorType::StorageBufferDynamic_ReadOnly: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::InputAttachment: return RESOURCE_STATE_SHADER_RESOURCE; + case DescriptorType::AccelerationStructure: return RESOURCE_STATE_SHADER_RESOURCE; + // clang-format on + default: + UNEXPECTED("unknown descriptor type"); + return RESOURCE_STATE_UNKNOWN; + } +} + + +#define LOG_PRS_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of a pipeline resource signature '", (Desc.Name ? Desc.Name : ""), "' is invalid: ", ##__VA_ARGS__) + PipelineResourceSignatureVkImpl::PipelineResourceSignatureVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDevice, const PipelineResourceSignatureDesc& Desc, bool bIsDeviceInternal) : TPipelineResourceSignatureBase{pRefCounters, pDevice, Desc, bIsDeviceInternal}, - m_SRBMemAllocator{GetRawAllocator()} + m_SRBMemAllocator{GetRawAllocator()}, + m_DynamicBufferCount{0}, + m_NumShaders{0} { -#define LOG_PRS_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of a pipeline resource signature '", (Desc.Name ? Desc.Name : ""), "' is invalid: ", ##__VA_ARGS__) - try { FixedLinearAllocator MemPool{GetRawAllocator()}; - MemPool.AddSpace(Desc.NumResources); + // reserve at least 1 element because m_pResourceAttribs must hold pointer to memory + MemPool.AddSpace(std::max(1u, Desc.NumResources)); + MemPool.AddSpace(Desc.NumImmutableSamplers); ReserveSpaceForDescription(MemPool, Desc); Int8 StaticVarStageCount = 0; Uint32 StaticVarCount = 0; - { - // get active shader stages - SHADER_TYPE Stages = SHADER_TYPE_UNKNOWN; - SHADER_TYPE StaticResStages = SHADER_TYPE_UNKNOWN; - - for (Uint32 i = 0; i < Desc.NumResources; ++i) - { - const auto& Res = Desc.Resources[i]; - Stages |= Res.ShaderStages; - - if (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) - { - StaticResStages |= Res.ShaderStages; - StaticVarCount += Res.ArraySize; - } - } - - m_ShaderStages = Stages; - m_NumShaders = static_cast(PlatformMisc::CountOneBits(static_cast(Stages))); - - if (Stages == SHADER_TYPE_COMPUTE) - { - m_PipelineType = PIPELINE_TYPE_COMPUTE; - } - else if (Stages & (SHADER_TYPE_AMPLIFICATION | SHADER_TYPE_MESH)) - { - m_PipelineType = PIPELINE_TYPE_MESH; - } - else if (Stages < SHADER_TYPE_COMPUTE) - { - m_PipelineType = PIPELINE_TYPE_GRAPHICS; - } - else if (Stages >= SHADER_TYPE_RAY_GEN) - { - m_PipelineType = PIPELINE_TYPE_RAY_TRACING; - } - else - { - LOG_PRS_ERROR_AND_THROW("can not deduce pipeline type - used incompatible shader stages"); - } - - m_StaticVarIndex.fill(-1); - - for (; StaticResStages != SHADER_TYPE_UNKNOWN; ++StaticVarStageCount) - { - auto StageBit = static_cast(1 << PlatformMisc::GetLSB(Uint32{StaticResStages})); - StaticResStages = StaticResStages & ~StageBit; - - const auto ShaderTypeInd = GetShaderTypePipelineIndex(StageBit, m_PipelineType); - m_StaticVarIndex[ShaderTypeInd] = StaticVarStageCount; - } - - if (StaticVarStageCount > 0) - { - MemPool.AddSpace(1); - MemPool.AddSpace(StaticVarStageCount); - } - } - + Uint8 DSMapping[2] = {}; + ReserveSpaceForStaticVarsMgrs(Desc, MemPool, StaticVarStageCount, StaticVarCount, DSMapping); MemPool.Reserve(); - - m_pBindingIndices = MemPool.Allocate(m_Desc.NumResources); + m_pResourceAttribs = MemPool.ConstructArray(std::max(1u, m_Desc.NumResources)); + m_ImmutableSamplers = MemPool.ConstructArray(m_Desc.NumImmutableSamplers); // The memory is now owned by PipelineResourceSignatureVkImpl and will be freed by Destruct(). auto* Ptr = MemPool.ReleaseOwnership(); - VERIFY_EXPR(Ptr == m_pBindingIndices); + VERIFY_EXPR(Ptr == m_pResourceAttribs); (void)Ptr; CopyDescription(MemPool, Desc); @@ -161,79 +268,245 @@ PipelineResourceSignatureVkImpl::PipelineResourceSignatureVkImpl(IReferenceCount const SHADER_RESOURCE_VARIABLE_TYPE AllowedVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_STATIC}; - for (Int8 i = 0; i < StaticVarStageCount; ++i) + for (Uint32 i = 0; i < m_StaticVarIndex.size(); ++i) { - new (m_StaticVarsMgrs + i) ShaderVariableManagerVk{*this, *m_pResourceCache}; - m_StaticVarsMgrs[i].Initialize(*this, GetRawAllocator(), AllowedVarTypes, _countof(AllowedVarTypes)); + Int8 Idx = m_StaticVarIndex[i]; + if (Idx >= 0) + { + VERIFY_EXPR(Idx < StaticVarStageCount); + const auto ShaderType = GetShaderTypeFromPipelineIndex(i, GetPipelineType()); + new (m_StaticVarsMgrs + Idx) ShaderVariableManagerVk{*this, *m_pResourceCache}; + m_StaticVarsMgrs[Idx].Initialize(*this, GetRawAllocator(), AllowedVarTypes, _countof(AllowedVarTypes), ShaderType); + } } } - std::vector LayoutBindings[2]; + CreateLayout(StaticVarCount, DSMapping); + } + catch (...) + { + Destruct(); + throw; + } +} - for (Uint32 i = 0; i < m_Desc.NumResources; ++i) +void PipelineResourceSignatureVkImpl::ReserveSpaceForStaticVarsMgrs(const PipelineResourceSignatureDesc& Desc, + FixedLinearAllocator& MemPool, + Int8& StaticVarStageCount, + Uint32& StaticVarCount, + Uint8 DSMapping[2]) +{ + // get active shader stages + SHADER_TYPE Stages = SHADER_TYPE_UNKNOWN; + SHADER_TYPE StaticResStages = SHADER_TYPE_UNKNOWN; + + bool VarExist[SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES] = {}; + + for (Uint32 i = 0; i < Desc.NumResources; ++i) + { + const auto& Res = Desc.Resources[i]; + Stages |= Res.ShaderStages; + + if (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - const auto& Res = m_Desc.Resources[i]; - auto& Binding = m_pBindingIndices[i]; + StaticResStages |= Res.ShaderStages; + StaticVarCount += Res.ArraySize; + } - Binding.DescSet = (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC ? 1 : 0); - Binding.Binding = static_cast(LayoutBindings[Binding.DescSet].size()); - Binding.SamplerInd = 0; // AZ TODO + VarExist[Res.VarType] = true; + } - LayoutBindings[Binding.DescSet].emplace_back(); - auto& LayoutBinding = LayoutBindings[Binding.DescSet].back(); + // initialize descriptor set mapping table + { + Uint8 Idx = 0; + if (VarExist[SHADER_RESOURCE_VARIABLE_TYPE_STATIC] || VarExist[SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE]) + DSMapping[0] = Idx++; + else + DSMapping[0] = 0xFF; + + if (VarExist[SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC]) + DSMapping[1] = Idx; + else + DSMapping[1] = 0xFF; + } - LayoutBinding.binding = Binding.Binding; - LayoutBinding.descriptorCount = Res.ArraySize; - LayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(Res.ShaderStages); - LayoutBinding.pImmutableSamplers = nullptr; - LayoutBinding.descriptorType = GetVkDescriptorType(Res); + m_ShaderStages = Stages; + m_NumShaders = PlatformMisc::CountOneBits(static_cast(Stages)); - if (!(Res.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_OFFSETS)) - m_DynamicBufferCount += static_cast(Res.ArraySize); + if (Stages == SHADER_TYPE_COMPUTE) + { + m_PipelineType = PIPELINE_TYPE_COMPUTE; + } + else if (Stages & (SHADER_TYPE_AMPLIFICATION | SHADER_TYPE_MESH)) + { + m_PipelineType = PIPELINE_TYPE_MESH; + } + else if (Stages < SHADER_TYPE_COMPUTE) + { + m_PipelineType = PIPELINE_TYPE_GRAPHICS; + } + else if (Stages >= SHADER_TYPE_RAY_GEN) + { + m_PipelineType = PIPELINE_TYPE_RAY_TRACING; + } + else + { + LOG_PRS_ERROR_AND_THROW("can not deduce pipeline type - used incompatible shader stages"); + } - if (m_pResourceCache && Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) - m_pResourceCache->InitializeResources(0, Binding.Binding, Res.ArraySize, LayoutBinding.descriptorType); - } + m_StaticVarIndex.fill(-1); - if (m_Desc.SRBAllocationGranularity > 1) - { - const SHADER_RESOURCE_VARIABLE_TYPE AllowedVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; + for (; StaticResStages != SHADER_TYPE_UNKNOWN; ++StaticVarStageCount) + { + const auto StageBit = ExtractBit(StaticResStages); + const auto ShaderTypeInd = GetShaderTypePipelineIndex(StageBit, m_PipelineType); + m_StaticVarIndex[ShaderTypeInd] = StaticVarStageCount; + } + + if (StaticVarStageCount > 0) + { + MemPool.AddSpace(1); + MemPool.AddSpace(StaticVarStageCount); + } +} +#undef LOG_PRS_ERROR_AND_THROW - Uint32 UnusedNumVars = 0; - const size_t ShaderVariableDataSize = ShaderVariableManagerVk::GetRequiredMemorySize(*this, AllowedVarTypes, _countof(AllowedVarTypes), UnusedNumVars); +void PipelineResourceSignatureVkImpl::CreateLayout(Uint32 StaticVarCount, const Uint8 DSMapping[2]) +{ + std::vector LayoutBindings[2]; + DynamicLinearAllocator TempAllocator{GetRawAllocator()}; + + Uint32 CacheOffsets[Uint32{SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES}] = {0, StaticVarCount, 0}; + + for (Uint32 i = 0; i < m_Desc.NumResources; ++i) + { + const auto& Res = m_Desc.Resources[i]; + auto& Attribs = m_pResourceAttribs[i]; + auto& CacheOffset = CacheOffsets[Uint32{Res.VarType}]; + const Uint32 SetIdx = (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC ? 1 : 0); + + // If all resources are dynamic then signature contains only one descriptor set layout with index 0, + // so remap SetIdx to actual descriptor set index. + VERIFY_EXPR(DSMapping[SetIdx] <= 1); + + Attribs.DescrSet = DSMapping[SetIdx]; + Attribs.BindingIndex = static_cast(LayoutBindings[SetIdx].size()); + Attribs.CacheOffset = CacheOffset; + Attribs.Type = GetDescriptorType(Res); + + CacheOffset += Res.ArraySize; + + LayoutBindings[SetIdx].emplace_back(); + auto& LayoutBinding = LayoutBindings[SetIdx].back(); - const Uint32 NumSets = !LayoutBindings[0].empty() + !LayoutBindings[1].empty(); - const Uint32 DescriptorSetSizes[] = {static_cast(LayoutBindings[0].size()), static_cast(LayoutBindings[1].size())}; - const size_t CacheMemorySize = ShaderResourceCacheVk::GetRequiredMemorySize(NumSets, DescriptorSetSizes); + LayoutBinding.binding = Attribs.BindingIndex; + LayoutBinding.descriptorCount = Res.ArraySize; + LayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(Res.ShaderStages); + LayoutBinding.pImmutableSamplers = nullptr; + LayoutBinding.descriptorType = GetVkDescriptorType(Attribs.Type); - m_SRBMemAllocator.Initialize(m_Desc.SRBAllocationGranularity, 1, &ShaderVariableDataSize, 1, &CacheMemorySize); + if (Attribs.Type == DescriptorType::SeparateImage) + { + Attribs.SamplerInd = FindAssignedSampler(Res); } - VkDescriptorSetLayoutCreateInfo SetLayoutCI = {}; + if (Attribs.Type == DescriptorType::CombinedImageSampler || + Attribs.Type == DescriptorType::Sampler) + { + // Only search for the immutable sampler for combined image samplers and separate samplers + Int32 SrcImmutableSamplerInd = FindImmutableSampler(Res, Attribs.Type, m_Desc, GetCombinedSamplerSuffix()); + if (SrcImmutableSamplerInd >= 0) + { + auto& ImmutableSampler = m_ImmutableSamplers[SrcImmutableSamplerInd]; + const auto& ImmutableSamplerDesc = m_Desc.ImmutableSamplers[SrcImmutableSamplerInd].Desc; + if (!ImmutableSampler) + GetDevice()->CreateSampler(ImmutableSamplerDesc, &ImmutableSampler); + + const VkSampler VkImmutableSampler = ImmutableSampler.RawPtr()->GetVkSampler(); + VkSampler* SamplerArray = TempAllocator.Allocate(Res.ArraySize); + for (Uint32 a = 0; a < Res.ArraySize; ++a) + SamplerArray[a] = VkImmutableSampler; + + LayoutBinding.pImmutableSamplers = SamplerArray; + Attribs.ImmutableSamplerAssigned = true; + } + } - SetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - SetLayoutCI.pNext = nullptr; - SetLayoutCI.flags = 0; + if (Attribs.Type == DescriptorType::UniformBufferDynamic || + Attribs.Type == DescriptorType::StorageBufferDynamic || + Attribs.Type == DescriptorType::StorageBufferDynamic_ReadOnly) + { + m_DynamicBufferCount += Res.ArraySize; + } - const auto& LogicalDevice = pDevice->GetLogicalDevice(); + if (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + m_pResourceCache->InitializeResources(Attribs.DescrSet, Attribs.CacheOffset, Res.ArraySize, Attribs.Type); + } + VERIFY_EXPR(CacheOffsets[0] == StaticVarCount); - for (Uint32 i = 0; i < _countof(LayoutBindings); ++i) + if (m_Desc.SRBAllocationGranularity > 1) + { + std::array ShaderVariableDataSizes = {}; + for (Uint32 s = 0; s < GetNumShaderStages(); ++s) { - auto& LayoutBinding = LayoutBindings[i]; - if (LayoutBinding.empty()) - continue; + const SHADER_RESOURCE_VARIABLE_TYPE AllowedVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; - SetLayoutCI.bindingCount = static_cast(LayoutBinding.size()); - SetLayoutCI.pBindings = LayoutBinding.data(); - m_VkDescSetLayouts[i] = LogicalDevice.CreateDescriptorSetLayout(SetLayoutCI); + Uint32 UnusedNumVars = 0; + ShaderVariableDataSizes[s] = ShaderVariableManagerVk::GetRequiredMemorySize(*this, AllowedVarTypes, _countof(AllowedVarTypes), GetShaderStageType(s), UnusedNumVars); } + + Uint32 DescriptorSetSizes[2] = {CacheOffsets[SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE], CacheOffsets[SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC]}; + const Uint32 NumSets = !!DescriptorSetSizes[0] + !!DescriptorSetSizes[1]; + + if (DescriptorSetSizes[0] == 0) + DescriptorSetSizes[0] = DescriptorSetSizes[1]; + + const size_t CacheMemorySize = ShaderResourceCacheVk::GetRequiredMemorySize(NumSets, DescriptorSetSizes); + + m_SRBMemAllocator.Initialize(m_Desc.SRBAllocationGranularity, GetNumShaderStages(), ShaderVariableDataSizes.data(), 1, &CacheMemorySize); } - catch (...) + + VkDescriptorSetLayoutCreateInfo SetLayoutCI = {}; + + SetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + SetLayoutCI.pNext = nullptr; + SetLayoutCI.flags = 0; + + const auto& LogicalDevice = GetDevice()->GetLogicalDevice(); + + for (Uint32 i = 0; i < _countof(LayoutBindings); ++i) { - Destruct(); - throw; + auto& LayoutBinding = LayoutBindings[i]; + if (LayoutBinding.empty()) + continue; + + SetLayoutCI.bindingCount = static_cast(LayoutBinding.size()); + SetLayoutCI.pBindings = LayoutBinding.data(); + m_VkDescSetLayouts[i] = LogicalDevice.CreateDescriptorSetLayout(SetLayoutCI); } -#undef LOG_PRS_ERROR_AND_THROW +} + +Uint32 PipelineResourceSignatureVkImpl::FindAssignedSampler(const PipelineResourceDesc& SepImg) const +{ + Uint32 SamplerInd = InvalidSamplerInd; + if (IsUsingCombinedSamplers()) + { + for (Uint32 i = 0; i < m_Desc.NumResources; ++i) + { + const auto& Res = m_Desc.Resources[i]; + + if (Res.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && + SepImg.VarType == Res.VarType && + (SepImg.ShaderStages & Res.ShaderStages) && + StreqSuff(Res.Name, SepImg.Name, GetCombinedSamplerSuffix())) + { + VERIFY_EXPR((Res.ShaderStages & SepImg.ShaderStages) == SepImg.ShaderStages); + SamplerInd = i; + break; + } + } + } + return SamplerInd; } PipelineResourceSignatureVkImpl::~PipelineResourceSignatureVkImpl() @@ -251,21 +524,25 @@ void PipelineResourceSignatureVkImpl::Destruct() m_pDevice->SafeReleaseDeviceObject(std::move(Layout), ~0ull); } - if (m_pBindingIndices == nullptr) + if (m_pResourceAttribs == nullptr) return; // memory is not allocated auto& RawAllocator = GetRawAllocator(); - for (size_t i = 0; m_StaticVarsMgrs && i < m_StaticVarIndex.size(); ++i) + if (m_StaticVarsMgrs != nullptr) { - Int8 Idx = m_StaticVarIndex[i]; - if (Idx >= 0) + for (size_t i = 0; i < m_StaticVarIndex.size(); ++i) { - m_StaticVarsMgrs[Idx].DestroyVariables(RawAllocator); - m_StaticVarsMgrs[Idx].~ShaderVariableManagerVk(); + Int8 Idx = m_StaticVarIndex[i]; + if (Idx >= 0) + { + m_StaticVarsMgrs[Idx].DestroyVariables(RawAllocator); + m_StaticVarsMgrs[Idx].~ShaderVariableManagerVk(); + } } + m_StaticVarIndex.fill(-1); + m_StaticVarsMgrs = nullptr; } - m_StaticVarsMgrs = nullptr; if (m_pResourceCache != nullptr) { @@ -273,93 +550,78 @@ void PipelineResourceSignatureVkImpl::Destruct() m_pResourceCache = nullptr; } - if (void* pRawMem = m_pBindingIndices) + for (Uint32 i = 0; i < m_Desc.NumImmutableSamplers; ++i) + { + m_ImmutableSamplers[i].~ImmutableSamplerPtrType(); + } + m_ImmutableSamplers = nullptr; + + if (void* pRawMem = m_pResourceAttribs) { RawAllocator.Free(pRawMem); - m_pBindingIndices = nullptr; + m_pResourceAttribs = nullptr; } } -void PipelineResourceSignatureVkImpl::CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, - bool InitStaticResources) +bool PipelineResourceSignatureVkImpl::IsCompatibleWith(const PipelineResourceSignatureVkImpl& Other) const { - auto& SRBAllocator = m_pDevice->GetSRBAllocator(); - auto pResBindingVk = NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingVkImpl instance", ShaderResourceBindingVkImpl)(this, false); - if (InitStaticResources) - pResBindingVk->InitializeStaticResources(nullptr); - pResBindingVk->QueryInterface(IID_ShaderResourceBinding, reinterpret_cast(ppShaderResourceBinding)); -} + if (this == &Other) + return true; -void PipelineResourceSignatureVkImpl::PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, - VkPipelineBindPoint BindPoint, - const ShaderResourceCacheVk& ResourceCache, - DescriptorSetBindInfo& BindInfo, - VkDescriptorSet VkDynamicDescrSet) const -{ - /* -#ifdef DILIGENT_DEBUG - BindInfo.vkSets.clear(); -#endif + if (GetHash() != Other.GetHash()) + return false; + + if (GetDesc().BindingIndex != Other.GetDesc().BindingIndex) + return false; - // Do not use vector::resize for BindInfo.vkSets and BindInfo.DynamicOffsets as this - // causes unnecessary work to zero-initialize new elements + const Uint32 LCount = GetTotalResourceCount(); + const Uint32 RCount = Other.GetTotalResourceCount(); - VERIFY(m_LayoutMgr.GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE_STATIC).SetIndex == m_LayoutMgr.GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE).SetIndex, - "Static and mutable variables are expected to share the same descriptor set"); - Uint32 TotalDynamicDescriptors = 0; + if (LCount != RCount) + return false; - BindInfo.SetCout = 0; - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE; VarType <= SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC; VarType = static_cast(VarType + 1)) + for (Uint32 r = 0; r < LCount; ++r) { - const auto& Set = m_LayoutMgr.GetDescriptorSet(VarType); - if (Set.SetIndex >= 0) + const auto& LAttr = GetAttribs(r); + const auto& RAttr = Other.GetAttribs(r); + + if (LAttr.Type != RAttr.Type || + LAttr.DescrSet != RAttr.DescrSet || + LAttr.BindingIndex != RAttr.BindingIndex || + LAttr.CacheOffset != RAttr.CacheOffset || + LAttr.ImmutableSamplerAssigned != RAttr.ImmutableSamplerAssigned) { - BindInfo.SetCout = std::max(BindInfo.SetCout, static_cast(Set.SetIndex + 1)); - if (BindInfo.SetCout > BindInfo.vkSets.size()) - BindInfo.vkSets.resize(BindInfo.SetCout); - VERIFY_EXPR(BindInfo.vkSets[Set.SetIndex] == VK_NULL_HANDLE); - if (VarType == SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) - BindInfo.vkSets[Set.SetIndex] = ResourceCache.GetDescriptorSet(Set.SetIndex).GetVkDescriptorSet(); - else - { - VERIFY_EXPR(ResourceCache.GetDescriptorSet(Set.SetIndex).GetVkDescriptorSet() == VK_NULL_HANDLE); - BindInfo.vkSets[Set.SetIndex] = VkDynamicDescrSet; - } - VERIFY(BindInfo.vkSets[Set.SetIndex] != VK_NULL_HANDLE, "Descriptor set must not be null"); + return false; } - TotalDynamicDescriptors += Set.NumDynamicDescriptors; } -#ifdef DILIGENT_DEBUG - for (const auto& set : BindInfo.vkSets) - VERIFY(set != VK_NULL_HANDLE, "Descriptor set must not be null"); -#endif + const Uint32 LSampCount = GetDesc().NumImmutableSamplers; + const Uint32 RSampCount = Other.GetDesc().NumImmutableSamplers; - BindInfo.DynamicOffsetCount = TotalDynamicDescriptors; - if (TotalDynamicDescriptors > BindInfo.DynamicOffsets.size()) - BindInfo.DynamicOffsets.resize(TotalDynamicDescriptors); - BindInfo.BindPoint = BindPoint; - BindInfo.pResourceCache = &ResourceCache; -#ifdef DILIGENT_DEBUG - BindInfo.pDbgPipelineLayout = this; -#endif - BindInfo.DynamicBuffersPresent = ResourceCache.GetNumDynamicBuffers() > 0; + if (LSampCount != RSampCount) + return false; - if (TotalDynamicDescriptors == 0) + for (Uint32 s = 0; s < LSampCount; ++s) { - // There are no dynamic descriptors, so we can bind descriptor sets right now - auto& CmdBuffer = pCtxVkImpl->GetCommandBuffer(); - CmdBuffer.BindDescriptorSets(BindInfo.BindPoint, - m_LayoutMgr.GetVkPipelineLayout(), - 0, // First set - BindInfo.SetCout, - BindInfo.vkSets.data(), // BindInfo.vkSets is never empty - 0, - nullptr); + const auto& LSamp = GetDesc().ImmutableSamplers[s]; + const auto& RSamp = Other.GetDesc().ImmutableSamplers[s]; + + if (LSamp.ShaderStages != RSamp.ShaderStages || + !(LSamp.Desc == RSamp.Desc)) + return false; } - BindInfo.DynamicDescriptorsBound = false; - */ + return true; +} + +void PipelineResourceSignatureVkImpl::CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, + bool InitStaticResources) +{ + auto& SRBAllocator = m_pDevice->GetSRBAllocator(); + auto pResBindingVk = NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingVkImpl instance", ShaderResourceBindingVkImpl)(this, false); + if (InitStaticResources) + pResBindingVk->InitializeStaticResources(nullptr); + pResBindingVk->QueryInterface(IID_ShaderResourceBinding, reinterpret_cast(ppShaderResourceBinding)); } Uint32 PipelineResourceSignatureVkImpl::GetStaticVariableCount(SHADER_TYPE ShaderType) const @@ -392,21 +654,48 @@ IShaderResourceVariable* PipelineResourceSignatureVkImpl::GetStaticVariableByInd return StaticVarMgr.GetVariable(Index); } +void PipelineResourceSignatureVkImpl::BindStaticResources(Uint32 ShaderFlags, + IResourceMapping* pResMapping, + Uint32 Flags) +{ + const auto PipelineType = GetPipelineType(); + for (Uint32 ShaderInd = 0; ShaderInd < m_StaticVarIndex.size(); ++ShaderInd) + { + const auto VarMngrInd = m_StaticVarIndex[ShaderInd]; + if (VarMngrInd >= 0) + { + // ShaderInd is the shader type pipeline index here + const auto ShaderType = GetShaderTypeFromPipelineIndex(ShaderInd, PipelineType); + if (ShaderFlags & ShaderType) + { + m_StaticVarsMgrs[VarMngrInd].BindResources(pResMapping, Flags); + } + } + } +} + void PipelineResourceSignatureVkImpl::InitResourceCache(ShaderResourceCacheVk& ResourceCache, IMemoryAllocator& CacheMemAllocator, const char* DbgPipelineName) const { std::array VarCount = {}; + Uint32 NumSets = static_cast(VarCount.size()); + for (Uint32 r = 0; r < m_Desc.NumResources; ++r) { const auto& Res = GetResource(r); - const auto& Bind = GetBinding(r); - VarCount[Bind.DescSet] += Res.ArraySize; + const auto& Attr = GetAttribs(r); + VarCount[Attr.DescrSet] += Res.ArraySize; } + if (VarCount[1] == 0) + --NumSets; + + VERIFY_EXPR(NumSets > 0 && VarCount[0] > 0); + // This call only initializes descriptor sets (ShaderResourceCacheVk::DescriptorSet) in the resource cache // Resources are initialized by source layout when shader resource binding objects are created - ResourceCache.InitializeSets(CacheMemAllocator, static_cast(VarCount.size()), VarCount.data()); + ResourceCache.InitializeSets(CacheMemAllocator, NumSets, VarCount.data()); if (auto VkLayout = GetStaticVkDescriptorSetLayout()) { @@ -417,7 +706,7 @@ void PipelineResourceSignatureVkImpl::InitResourceCache(ShaderResourceCacheVk& R DescrSetName = _DescrSetName.c_str(); #endif DescriptorSetAllocation SetAllocation = GetDevice()->AllocateDescriptorSet(~Uint64{0}, VkLayout, DescrSetName); - ResourceCache.GetDescriptorSet(0).AssignDescriptorSetAllocation(std::move(SetAllocation)); + ResourceCache.GetDescriptorSet(GetStaticDescrSetIndex()).AssignDescriptorSetAllocation(std::move(SetAllocation)); } } @@ -426,8 +715,7 @@ SHADER_TYPE PipelineResourceSignatureVkImpl::GetShaderStageType(Uint32 StageInde SHADER_TYPE Stages = m_ShaderStages; for (Uint32 Index = 0; Stages != SHADER_TYPE_UNKNOWN; ++Index) { - auto StageBit = static_cast(1 << PlatformMisc::GetLSB(Uint32{Stages})); - Stages = Stages & ~StageBit; + auto StageBit = ExtractBit(Stages); if (Index == StageIndex) return StageBit; @@ -437,29 +725,843 @@ SHADER_TYPE PipelineResourceSignatureVkImpl::GetShaderStageType(Uint32 StageInde return SHADER_TYPE_UNKNOWN; } +Uint32 PipelineResourceSignatureVkImpl::GetNumDescriptorSets() const +{ + return Uint32{m_VkDescSetLayouts[0] != VK_NULL_HANDLE} + Uint32{m_VkDescSetLayouts[1] != VK_NULL_HANDLE}; +} + void PipelineResourceSignatureVkImpl::InitializeResourceMemoryInCache(ShaderResourceCacheVk& ResourceCache) const { auto TotalResources = GetTotalResourceCount(); for (Uint32 r = 0; r < TotalResources; ++r) { const auto& Res = GetResource(r); - const auto& Bind = GetBinding(r); - ResourceCache.InitializeResources(Bind.DescSet, Bind.Binding, Res.ArraySize, GetVkDescriptorType(Res)); + const auto& Attr = GetAttribs(r); + ResourceCache.InitializeResources(Attr.DescrSet, Attr.CacheOffset, Res.ArraySize, Attr.Type); } } void PipelineResourceSignatureVkImpl::InitializeStaticSRBResources(ShaderResourceCacheVk& DstResourceCache) const { + if (GetStaticVkDescriptorSetLayout() == VK_NULL_HANDLE || m_pResourceCache == nullptr) + return; + + // SrcResourceCache contains only static resources. + // DstResourceCache contains static and mutable resources. const auto& SrcResourceCache = *m_pResourceCache; - (void)(SrcResourceCache); + const auto& SrcDescrSet = SrcResourceCache.GetDescriptorSet(GetStaticDescrSetIndex()); + auto& DstDescrSet = DstResourceCache.GetDescriptorSet(GetStaticDescrSetIndex()); - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) + // AZ TODO: optimize + for (Uint32 r = 0; r < GetTotalResourceCount(); ++r) { - // AZ TODO + const auto& Res = GetResource(r); + const auto& Attr = GetAttribs(r); + + if (Res.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && Attr.ImmutableSamplerAssigned) + continue; // Skip immutable separate samplers + + if (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + for (Uint32 ArrInd = 0; ArrInd < Res.ArraySize; ++ArrInd) + { + auto CacheOffset = Attr.CacheOffset + ArrInd; + const auto& SrcCachedRes = SrcDescrSet.GetResource(CacheOffset); + IDeviceObject* pObject = SrcCachedRes.pObject.RawPtr(); + if (!pObject) + LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetPrintName(Res, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + + IDeviceObject* pCachedResource = DstDescrSet.GetResource(CacheOffset).pObject; + if (pCachedResource != pObject) + { + VERIFY(pCachedResource == nullptr, "Static resource has already been initialized, and the resource to be assigned from the shader does not match previously assigned resource"); + BindResource(pObject, ArrInd, r, DstResourceCache); + } + } + } } + #ifdef DILIGENT_DEBUG DstResourceCache.DbgVerifyDynamicBuffersCounter(); #endif } +Uint32 PipelineResourceSignatureVkImpl::GetStaticDescrSetIndex() const +{ + return m_VkDescSetLayouts[0] != VK_NULL_HANDLE ? 0 : ~0u; +} + +Uint32 PipelineResourceSignatureVkImpl::GetDynamicDescrSetIndex() const +{ + return m_VkDescSetLayouts[1] != VK_NULL_HANDLE ? Uint32{m_VkDescSetLayouts[0] != VK_NULL_HANDLE} : ~0u; +} + +void PipelineResourceSignatureVkImpl::CommitDynamicResources(const ShaderResourceCacheVk& ResourceCache, + VkDescriptorSet vkDynamicDescriptorSet) const +{ + VERIFY(HasDynamicDescrSet(), "This shader resource layout does not contain dynamic resources"); + VERIFY_EXPR(vkDynamicDescriptorSet != VK_NULL_HANDLE); + +#ifdef DILIGENT_DEBUG + static constexpr size_t ImgUpdateBatchSize = 4; + static constexpr size_t BuffUpdateBatchSize = 2; + static constexpr size_t TexelBuffUpdateBatchSize = 2; + static constexpr size_t AccelStructBatchSize = 2; + static constexpr size_t WriteDescriptorSetBatchSize = 2; +#else + static constexpr size_t ImgUpdateBatchSize = 128; + static constexpr size_t BuffUpdateBatchSize = 64; + static constexpr size_t TexelBuffUpdateBatchSize = 32; + static constexpr size_t AccelStructBatchSize = 32; + static constexpr size_t WriteDescriptorSetBatchSize = 32; +#endif + + // Do not zero-initiaize arrays! + std::array DescrImgInfoArr; + std::array DescrBuffInfoArr; + std::array DescrBuffViewArr; + std::array DescrAccelStructArr; + std::array WriteDescrSetArr; + + auto DescrImgIt = DescrImgInfoArr.begin(); + auto DescrBuffIt = DescrBuffInfoArr.begin(); + auto BuffViewIt = DescrBuffViewArr.begin(); + auto AccelStructIt = DescrAccelStructArr.begin(); + auto WriteDescrSetIt = WriteDescrSetArr.begin(); + + const auto& SetResources = ResourceCache.GetDescriptorSet(GetDynamicDescrSetIndex()); + const auto& LogicalDevice = GetDevice()->GetLogicalDevice(); + + // AZ TODO: optimize + for (Uint32 ResNum = 0, ArrElem = 0, Count = GetTotalResourceCount(); ResNum < Count;) + { + const auto& Res = GetResource(ResNum); + const auto& Attr = GetAttribs(ResNum); + + if (Res.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VERIFY_EXPR(ArrElem == 0); + ++ResNum; + continue; + } + + WriteDescrSetIt->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteDescrSetIt->pNext = nullptr; + VERIFY(SetResources.GetVkDescriptorSet() == VK_NULL_HANDLE, "Dynamic descriptor set must not be assigned to the resource cache"); + WriteDescrSetIt->dstSet = vkDynamicDescriptorSet; + VERIFY(WriteDescrSetIt->dstSet != VK_NULL_HANDLE, "Vulkan descriptor set must not be null"); + WriteDescrSetIt->dstBinding = Attr.BindingIndex; + WriteDescrSetIt->dstArrayElement = ArrElem; + // descriptorType must be the same type as that specified in VkDescriptorSetLayoutBinding for dstSet at dstBinding. + // The type of the descriptor also controls which array the descriptors are taken from. (13.2.4) + WriteDescrSetIt->descriptorType = GetVkDescriptorType(Attr.Type); + + // For every resource type, try to batch as many descriptor updates as we can + static_assert(static_cast(DescriptorType::Count) == 16, "Please update the switch below to handle the new descriptor type"); + switch (Attr.Type) + { + case DescriptorType::UniformBuffer: + case DescriptorType::UniformBufferDynamic: + WriteDescrSetIt->pBufferInfo = &(*DescrBuffIt); + while (ArrElem < Res.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) + { + const auto& CachedRes = SetResources.GetResource(Attr.CacheOffset + ArrElem); + *DescrBuffIt = CachedRes.GetUniformBufferDescriptorWriteInfo(); + ++DescrBuffIt; + ++ArrElem; + } + break; + + case DescriptorType::StorageBuffer: + case DescriptorType::StorageBufferDynamic: + case DescriptorType::StorageBuffer_ReadOnly: + case DescriptorType::StorageBufferDynamic_ReadOnly: + WriteDescrSetIt->pBufferInfo = &(*DescrBuffIt); + while (ArrElem < Res.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) + { + const auto& CachedRes = SetResources.GetResource(Attr.CacheOffset + ArrElem); + *DescrBuffIt = CachedRes.GetStorageBufferDescriptorWriteInfo(); + ++DescrBuffIt; + ++ArrElem; + } + break; + + case DescriptorType::UniformTexelBuffer: + case DescriptorType::StorageTexelBuffer: + case DescriptorType::StorageTexelBuffer_ReadOnly: + WriteDescrSetIt->pTexelBufferView = &(*BuffViewIt); + while (ArrElem < Res.ArraySize && BuffViewIt != DescrBuffViewArr.end()) + { + const auto& CachedRes = SetResources.GetResource(Attr.CacheOffset + ArrElem); + *BuffViewIt = CachedRes.GetBufferViewWriteInfo(); + ++BuffViewIt; + ++ArrElem; + } + break; + + case DescriptorType::CombinedImageSampler: + case DescriptorType::SeparateImage: + case DescriptorType::StorageImage: + case DescriptorType::StorageImage_ReadOnly: + case DescriptorType::InputAttachment: + WriteDescrSetIt->pImageInfo = &(*DescrImgIt); + while (ArrElem < Res.ArraySize && DescrImgIt != DescrImgInfoArr.end()) + { + const auto& CachedRes = SetResources.GetResource(Attr.CacheOffset + ArrElem); + *DescrImgIt = CachedRes.GetImageDescriptorWriteInfo(Attr.ImmutableSamplerAssigned); + ++DescrImgIt; + ++ArrElem; + } + break; + + case DescriptorType::Sampler: + // Immutable samplers are permanently bound into the set layout; later binding a sampler + // into an immutable sampler slot in a descriptor set is not allowed (13.2.1) + if (!Attr.ImmutableSamplerAssigned) + { + WriteDescrSetIt->pImageInfo = &(*DescrImgIt); + while (ArrElem < Res.ArraySize && DescrImgIt != DescrImgInfoArr.end()) + { + const auto& CachedRes = SetResources.GetResource(Attr.CacheOffset + ArrElem); + *DescrImgIt = CachedRes.GetSamplerDescriptorWriteInfo(); + ++DescrImgIt; + ++ArrElem; + } + } + else + { + ArrElem = Res.ArraySize; + WriteDescrSetIt->dstArrayElement = Res.ArraySize; + } + break; + + case DescriptorType::AccelerationStructure: + WriteDescrSetIt->pNext = &(*AccelStructIt); + while (ArrElem < Res.ArraySize && AccelStructIt != DescrAccelStructArr.end()) + { + const auto& CachedRes = SetResources.GetResource(Attr.CacheOffset + ArrElem); + *AccelStructIt = CachedRes.GetAccelerationStructureWriteInfo(); + ++AccelStructIt; + ++ArrElem; + } + break; + + default: + UNEXPECTED("Unexpected resource type"); + } + + WriteDescrSetIt->descriptorCount = ArrElem - WriteDescrSetIt->dstArrayElement; + if (ArrElem == Res.ArraySize) + { + ArrElem = 0; + ++ResNum; + } + // descriptorCount == 0 for immutable separate samplers + if (WriteDescrSetIt->descriptorCount > 0) + ++WriteDescrSetIt; + + // If we ran out of space in any of the arrays or if we processed all resources, + // flush pending updates and reset iterators + if (DescrImgIt == DescrImgInfoArr.end() || + DescrBuffIt == DescrBuffInfoArr.end() || + BuffViewIt == DescrBuffViewArr.end() || + AccelStructIt == DescrAccelStructArr.end() || + WriteDescrSetIt == WriteDescrSetArr.end()) + { + auto DescrWriteCount = static_cast(std::distance(WriteDescrSetArr.begin(), WriteDescrSetIt)); + if (DescrWriteCount > 0) + LogicalDevice.UpdateDescriptorSets(DescrWriteCount, WriteDescrSetArr.data(), 0, nullptr); + + DescrImgIt = DescrImgInfoArr.begin(); + DescrBuffIt = DescrBuffInfoArr.begin(); + BuffViewIt = DescrBuffViewArr.begin(); + AccelStructIt = DescrAccelStructArr.begin(); + WriteDescrSetIt = WriteDescrSetArr.begin(); + } + } + + auto DescrWriteCount = static_cast(std::distance(WriteDescrSetArr.begin(), WriteDescrSetIt)); + if (DescrWriteCount > 0) + LogicalDevice.UpdateDescriptorSets(DescrWriteCount, WriteDescrSetArr.data(), 0, nullptr); +} + +String PipelineResourceSignatureVkImpl::GetPrintName(const PipelineResourceDesc& ResDesc, Uint32 ArrayInd) +{ + VERIFY_EXPR(ArrayInd < ResDesc.ArraySize); + + if (ResDesc.ArraySize > 1) + { + std::stringstream ss; + ss << ResDesc.Name << '[' << ArrayInd << ']'; + return ss.str(); + } + else + return ResDesc.Name; +} + +namespace +{ +struct BindResourceHelper +{ + // AZ TODO: cache DstRes.Type and ResDesc.VarType for better performace ??? + ShaderResourceCacheVk::Resource& DstRes; + const PipelineResourceDesc& ResDesc; + const PipelineResourceSignatureVkImpl::ResourceAttribs& Attribs; + const Uint32 ArrayIndex; + const VkDescriptorSet vkDescrSet; + PipelineResourceSignatureVkImpl const& Signature; + ShaderResourceCacheVk& ResourceCache; + + void BindResource(IDeviceObject* pObj) const; + + String GetPrintName(Uint32 ArrayInd) const; + + RESOURCE_DIMENSION GetResourceDimension() const; + + bool IsMultisample() const; + +private: + void CacheUniformBuffer(IDeviceObject* pBuffer, + Uint16& DynamicBuffersCounter) const; + + void CacheStorageBuffer(IDeviceObject* pBufferView, + Uint16& DynamicBuffersCounter) const; + + void CacheTexelBuffer(IDeviceObject* pBufferView, + Uint16& DynamicBuffersCounter) const; + + void CacheImage(IDeviceObject* pTexView) const; + + void CacheSeparateSampler(IDeviceObject* pSampler) const; + + void CacheInputAttachment(IDeviceObject* pTexView) const; + + void CacheAccelerationStructure(IDeviceObject* pTLAS) const; + + template + bool UpdateCachedResource(RefCntAutoPtr&& pObject, + TPreUpdateObject PreUpdateObject) const; + + bool IsImmutableSamplerAssigned() const { return Attribs.ImmutableSamplerAssigned; } + + // Updates resource descriptor in the descriptor set + inline void UpdateDescriptorHandle(const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView, + const VkWriteDescriptorSetAccelerationStructureKHR* pAccelStructInfo = nullptr) const; +}; + +void BindResourceHelper::BindResource(IDeviceObject* pObj) const +{ +#ifdef DILIGENT_DEBUG + if (ResourceCache.DbgGetContentType() == ShaderResourceCacheVk::DbgCacheContentType::SRBResources) + { + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC || ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) + { + VERIFY(vkDescrSet != VK_NULL_HANDLE, "Static and mutable variables must have valid vulkan descriptor set assigned"); + // Dynamic variables do not have vulkan descriptor set only until they are assigned one the first time + } + } + else if (ResourceCache.DbgGetContentType() == ShaderResourceCacheVk::DbgCacheContentType::StaticShaderResources) + { + VERIFY(vkDescrSet == VK_NULL_HANDLE, "Static shader resource cache should not have vulkan descriptor set allocation"); + } + else + { + UNEXPECTED("Unexpected shader resource cache content type"); + } +#endif + + if (pObj) + { + static_assert(static_cast(DescriptorType::Count) == 16, "Please update the switch below to handle the new descriptor type"); + switch (DstRes.Type) + { + case DescriptorType::UniformBuffer: + case DescriptorType::UniformBufferDynamic: + CacheUniformBuffer(pObj, ResourceCache.GetDynamicBuffersCounter()); + break; + + case DescriptorType::StorageBuffer: + case DescriptorType::StorageBuffer_ReadOnly: + case DescriptorType::StorageBufferDynamic: + case DescriptorType::StorageBufferDynamic_ReadOnly: + CacheStorageBuffer(pObj, ResourceCache.GetDynamicBuffersCounter()); + break; + + case DescriptorType::UniformTexelBuffer: + case DescriptorType::StorageTexelBuffer: + case DescriptorType::StorageTexelBuffer_ReadOnly: + CacheTexelBuffer(pObj, ResourceCache.GetDynamicBuffersCounter()); + break; + + case DescriptorType::StorageImage: + case DescriptorType::StorageImage_ReadOnly: + case DescriptorType::SeparateImage: + case DescriptorType::CombinedImageSampler: + CacheImage(pObj); + break; + + case DescriptorType::Sampler: + if (!IsImmutableSamplerAssigned()) + { + CacheSeparateSampler(pObj); + } + else + { + // Immutable samplers are permanently bound into the set layout; later binding a sampler + // into an immutable sampler slot in a descriptor set is not allowed (13.2.1) + LOG_ERROR_MESSAGE("Attempting to assign a sampler to an immutable sampler '", ResDesc.Name, '\''); + } + break; + + case DescriptorType::InputAttachment: + CacheInputAttachment(pObj); + break; + + case DescriptorType::AccelerationStructure: + CacheAccelerationStructure(pObj); + break; + + default: UNEXPECTED("Unknown resource type ", static_cast(DstRes.Type)); + } + } + else + { + if (DstRes.pObject && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + LOG_ERROR_MESSAGE("Shader variable '", ResDesc.Name, "' is not dynamic but being unbound. This is an error and may cause unpredicted behavior. ", + "Use another shader resource binding instance or label shader variable as dynamic if you need to bind another resource."); + } + + DstRes.pObject.Release(); + } +} + +template +bool BindResourceHelper::UpdateCachedResource(RefCntAutoPtr&& pObject, + TPreUpdateObject PreUpdateObject) const +{ + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + if (pObject) + { + if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && DstRes.pObject != nullptr) + { + // Do not update resource if one is already bound unless it is dynamic. This may be + // dangerous as writing descriptors while they are used by the GPU is an undefined behavior + return false; + } + + PreUpdateObject(DstRes.pObject.template RawPtr(), pObject.template RawPtr()); + DstRes.pObject.Attach(pObject.Detach()); + return true; + } + else + { + return false; + } +} + +void BindResourceHelper::CacheUniformBuffer(IDeviceObject* pBuffer, + Uint16& DynamicBuffersCounter) const +{ + // clang-format off + VERIFY(DstRes.Type == DescriptorType::UniformBuffer || + DstRes.Type == DescriptorType::UniformBufferDynamic, + "Uniform buffer resource is expected"); + // clang-format on + RefCntAutoPtr pBufferVk{pBuffer, IID_BufferVk}; +#ifdef DILIGENT_DEVELOPMENT + VerifyConstantBufferBinding(*this, ResDesc.VarType, ArrayIndex, pBuffer, pBufferVk.RawPtr(), DstRes.pObject.RawPtr()); + + // AZ TODO + /*if (pBufferVk->GetDesc().uiSizeInBytes < BufferStaticSize) + { + // It is OK if robustBufferAccess feature is enabled, otherwise access outside of buffer range may lead to crash or undefined behavior. + LOG_WARNING_MESSAGE("Error binding uniform buffer '", pBufferVk->GetDesc().Name, "' to shader variable '", + GetDesc().Name, "': buffer size in the shader (", + BufferStaticSize, ") is incompatible with the actual buffer size (", pBufferVk->GetDesc().uiSizeInBytes, ")."); + }*/ +#endif + + auto UpdateDynamicBuffersCounter = [&DynamicBuffersCounter](const BufferVkImpl* pOldBuffer, const BufferVkImpl* pNewBuffer) { + if (pOldBuffer != nullptr && pOldBuffer->GetDesc().Usage == USAGE_DYNAMIC) + { + VERIFY(DynamicBuffersCounter > 0, "Dynamic buffers counter must be greater than zero when there is at least one dynamic buffer bound in the resource cache"); + --DynamicBuffersCounter; + } + if (pNewBuffer != nullptr && pNewBuffer->GetDesc().Usage == USAGE_DYNAMIC) + ++DynamicBuffersCounter; + }; + if (UpdateCachedResource(std::move(pBufferVk), UpdateDynamicBuffersCounter)) + { + // VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER or VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC descriptor type require + // buffer to be created with VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + + // Do not update descriptor for a dynamic uniform buffer. All dynamic resource + // descriptors are updated at once by CommitDynamicResources() when SRB is committed. + if (vkDescrSet != VK_NULL_HANDLE && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VkDescriptorBufferInfo DescrBuffInfo = DstRes.GetUniformBufferDescriptorWriteInfo(); + UpdateDescriptorHandle(nullptr, &DescrBuffInfo, nullptr); + } + } +} + +void BindResourceHelper::CacheStorageBuffer(IDeviceObject* pBufferView, + Uint16& DynamicBuffersCounter) const +{ + // clang-format off + VERIFY(DstRes.Type == DescriptorType::StorageBuffer || + DstRes.Type == DescriptorType::StorageBuffer_ReadOnly || + DstRes.Type == DescriptorType::StorageBufferDynamic || + DstRes.Type == DescriptorType::StorageBufferDynamic_ReadOnly, + "Storage buffer resource is expected"); + // clang-format on + + RefCntAutoPtr pBufferViewVk{pBufferView, IID_BufferViewVk}; +#ifdef DILIGENT_DEVELOPMENT + { + // HLSL buffer SRVs are mapped to storge buffers in GLSL + auto RequiredViewType = DescriptorTypeToBufferView(DstRes.Type); + VerifyResourceViewBinding(*this, ResDesc.VarType, ArrayIndex, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr()); + if (pBufferViewVk != nullptr) + { + const auto& ViewDesc = pBufferViewVk->GetDesc(); + const auto& BuffDesc = pBufferViewVk->GetBuffer()->GetDesc(); + if (BuffDesc.Mode != BUFFER_MODE_STRUCTURED && BuffDesc.Mode != BUFFER_MODE_RAW) + { + LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", + ResDesc.Name, "': structured buffer view is expected."); + } + + // AZ TODO + /*if (BufferStride == 0 && ViewDesc.ByteWidth < BufferStaticSize) + { + // It is OK if robustBufferAccess feature is enabled, otherwise access outside of buffer range may lead to crash or undefined behavior. + LOG_WARNING_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", + Name, "': buffer size in the shader (", + BufferStaticSize, ") is incompatible with the actual buffer view size (", ViewDesc.ByteWidth, ")."); + } + + if (BufferStride > 0 && (ViewDesc.ByteWidth < BufferStaticSize || (ViewDesc.ByteWidth - BufferStaticSize) % BufferStride != 0)) + { + // For buffers with dynamic arrays we know only static part size and array element stride. + // Element stride in the shader may be differ than in the code. Here we check that the buffer size is exactly the same as the array with N elements. + LOG_WARNING_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", + Name, "': static buffer size in the shader (", + BufferStaticSize, ") and array element stride (", BufferStride, ") are incompatible with the actual buffer view size (", ViewDesc.ByteWidth, "),", + " this may be the result of the array element size mismatch."); + }*/ + } + } +#endif + + auto UpdateDynamicBuffersCounter = [&DynamicBuffersCounter](const BufferViewVkImpl* pOldBufferView, const BufferViewVkImpl* pNewBufferView) { + if (pOldBufferView != nullptr && pOldBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) + { + VERIFY(DynamicBuffersCounter > 0, "Dynamic buffers counter must be greater than zero when there is at least one dynamic buffer bound in the resource cache"); + --DynamicBuffersCounter; + } + if (pNewBufferView != nullptr && pNewBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) + ++DynamicBuffersCounter; + }; + + if (UpdateCachedResource(std::move(pBufferViewVk), UpdateDynamicBuffersCounter)) + { + // VK_DESCRIPTOR_TYPE_STORAGE_BUFFER or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC descriptor type + // require buffer to be created with VK_BUFFER_USAGE_STORAGE_BUFFER_BIT (13.2.4) + + // Do not update descriptor for a dynamic storage buffer. All dynamic resource + // descriptors are updated at once by CommitDynamicResources() when SRB is committed. + if (vkDescrSet != VK_NULL_HANDLE && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VkDescriptorBufferInfo DescrBuffInfo = DstRes.GetStorageBufferDescriptorWriteInfo(); + UpdateDescriptorHandle(nullptr, &DescrBuffInfo, nullptr); + } + } +} + +void BindResourceHelper::CacheTexelBuffer(IDeviceObject* pBufferView, + Uint16& DynamicBuffersCounter) const +{ + // clang-format off + VERIFY(DstRes.Type == DescriptorType::UniformTexelBuffer || + DstRes.Type == DescriptorType::StorageTexelBuffer || + DstRes.Type == DescriptorType::StorageTexelBuffer_ReadOnly, + "Uniform or storage buffer resource is expected"); + // clang-format on + + RefCntAutoPtr pBufferViewVk{pBufferView, IID_BufferViewVk}; +#ifdef DILIGENT_DEVELOPMENT + { + // HLSL buffer SRVs are mapped to storge buffers in GLSL + auto RequiredViewType = DescriptorTypeToBufferView(DstRes.Type); + VerifyResourceViewBinding(*this, ResDesc.VarType, ArrayIndex, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr()); + if (pBufferViewVk != nullptr) + { + const auto& ViewDesc = pBufferViewVk->GetDesc(); + const auto& BuffDesc = pBufferViewVk->GetBuffer()->GetDesc(); + if (!((BuffDesc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType != VT_UNDEFINED) || BuffDesc.Mode == BUFFER_MODE_RAW)) + { + LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", + ResDesc.Name, "': formatted buffer view is expected."); + } + } + } +#endif + + auto UpdateDynamicBuffersCounter = [&DynamicBuffersCounter](const BufferViewVkImpl* pOldBufferView, const BufferViewVkImpl* pNewBufferView) { + if (pOldBufferView != nullptr && pOldBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) + { + VERIFY(DynamicBuffersCounter > 0, "Dynamic buffers counter must be greater than zero when there is at least one dynamic buffer bound in the resource cache"); + --DynamicBuffersCounter; + } + if (pNewBufferView != nullptr && pNewBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) + ++DynamicBuffersCounter; + }; + + if (UpdateCachedResource(std::move(pBufferViewVk), UpdateDynamicBuffersCounter)) + { + // The following bits must have been set at buffer creation time: + // * VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER -> VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT + // * VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER -> VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT + + // Do not update descriptor for a dynamic texel buffer. All dynamic resource descriptors + // are updated at once by CommitDynamicResources() when SRB is committed. + if (vkDescrSet != VK_NULL_HANDLE && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VkBufferView BuffView = DstRes.pObject.RawPtr()->GetVkBufferView(); + UpdateDescriptorHandle(nullptr, nullptr, &BuffView); + } + } +} + +void BindResourceHelper::CacheImage(IDeviceObject* pTexView) const +{ + // clang-format off + VERIFY(DstRes.Type == DescriptorType::StorageImage || + DstRes.Type == DescriptorType::StorageImage_ReadOnly || + DstRes.Type == DescriptorType::SeparateImage || + DstRes.Type == DescriptorType::CombinedImageSampler, + "Storage image, separate image or sampled image resource is expected"); + // clang-format on + RefCntAutoPtr pTexViewVk0{pTexView, IID_TextureViewVk}; +#ifdef DILIGENT_DEVELOPMENT + { + // HLSL buffer SRVs are mapped to storge buffers in GLSL + auto RequiredViewType = DescriptorTypeToTextureView(DstRes.Type); + VerifyResourceViewBinding(*this, ResDesc.VarType, ArrayIndex, pTexView, pTexViewVk0.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr()); + } +#endif + if (UpdateCachedResource(std::move(pTexViewVk0), [](const TextureViewVkImpl*, const TextureViewVkImpl*) {})) + { + // We can do RawPtr here safely since UpdateCachedResource() returned true + auto* pTexViewVk = DstRes.pObject.RawPtr(); +#ifdef DILIGENT_DEVELOPMENT + if (DstRes.Type == DescriptorType::CombinedImageSampler && !IsImmutableSamplerAssigned()) + { + if (pTexViewVk->GetSampler() == nullptr) + { + LOG_ERROR_MESSAGE("Error binding texture view '", pTexViewVk->GetDesc().Name, "' to variable '", GetPrintName(ArrayIndex), + "'. No sampler is assigned to the view"); + } + } +#endif + + // Do not update descriptor for a dynamic image. All dynamic resource descriptors + // are updated at once by CommitDynamicResources() when SRB is committed. + if (vkDescrSet != VK_NULL_HANDLE && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VkDescriptorImageInfo DescrImgInfo = DstRes.GetImageDescriptorWriteInfo(IsImmutableSamplerAssigned()); + UpdateDescriptorHandle(&DescrImgInfo, nullptr, nullptr); + } + + if (Attribs.SamplerInd != PipelineResourceSignatureVkImpl::InvalidSamplerInd) + { + VERIFY(DstRes.Type == DescriptorType::SeparateImage, + "Only separate images can be assigned separate samplers when using HLSL-style combined samplers."); + VERIFY(!IsImmutableSamplerAssigned(), "Separate image can't be assigned an immutable sampler."); + + auto& SamplerResDesc = Signature.GetResource(Attribs.SamplerInd); + auto& SamplerAttribs = Signature.GetAttribs(Attribs.SamplerInd); + VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER); + + if (!SamplerAttribs.ImmutableSamplerAssigned) + { + auto* pSampler = pTexViewVk->GetSampler(); + if (pSampler != nullptr) + { + VERIFY_EXPR(DstRes.Type == DescriptorType::SeparateImage); + DEV_CHECK_ERR(SamplerResDesc.ArraySize == 1 || SamplerResDesc.ArraySize == ResDesc.ArraySize, + "Array size (", SamplerResDesc.ArraySize, + ") of separate sampler variable '", + SamplerResDesc.Name, + "' must be one or the same as the array size (", ResDesc.ArraySize, + ") of separate image variable '", ResDesc.Name, "' it is assigned to"); + + auto& DstDescrSet = ResourceCache.GetDescriptorSet(SamplerAttribs.DescrSet); + const auto SamplerArrInd = SamplerResDesc.ArraySize == 1 ? 0 : ArrayIndex; + auto& SampleDstRes = DstDescrSet.GetResource(SamplerAttribs.CacheOffset + SamplerArrInd); + + BindResourceHelper SeparateSampler{ + SampleDstRes, + SamplerResDesc, + SamplerAttribs, + SamplerArrInd, + DstDescrSet.GetVkDescriptorSet(), + Signature, + ResourceCache}; + SeparateSampler.BindResource(pSampler); + } + else + { + LOG_ERROR_MESSAGE("Failed to bind sampler to sampler variable '", SamplerResDesc.Name, + "' assigned to separate image '", GetPrintName(ArrayIndex), + "': no sampler is set in texture view '", pTexViewVk->GetDesc().Name, '\''); + } + } + } + } +} + +void BindResourceHelper::CacheSeparateSampler(IDeviceObject* pSampler) const +{ + VERIFY(DstRes.Type == DescriptorType::Sampler, "Separate sampler resource is expected"); + VERIFY(!IsImmutableSamplerAssigned(), "This separate sampler is assigned an immutable sampler"); + + RefCntAutoPtr pSamplerVk{pSampler, IID_Sampler}; +#ifdef DILIGENT_DEVELOPMENT + if (pSampler != nullptr && pSamplerVk == nullptr) + { + LOG_ERROR_MESSAGE("Failed to bind object '", pSampler->GetDesc().Name, "' to variable '", GetPrintName(ArrayIndex), + "'. Unexpected object type: sampler is expected"); + } + if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && DstRes.pObject != nullptr && DstRes.pObject != pSamplerVk) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(ResDesc.VarType); + LOG_ERROR_MESSAGE("Non-null sampler is already bound to ", VarTypeStr, " shader variable '", GetPrintName(ArrayIndex), + "'. Attempting to bind another sampler or null is an error and may " + "cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."); + } +#endif + if (UpdateCachedResource(std::move(pSamplerVk), [](const SamplerVkImpl*, const SamplerVkImpl*) {})) + { + // Do not update descriptor for a dynamic sampler. All dynamic resource descriptors + // are updated at once by CommitDynamicResources() when SRB is committed. + if (vkDescrSet != VK_NULL_HANDLE && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VkDescriptorImageInfo DescrImgInfo = DstRes.GetSamplerDescriptorWriteInfo(); + UpdateDescriptorHandle(&DescrImgInfo, nullptr, nullptr); + } + } +} + +void BindResourceHelper::CacheInputAttachment(IDeviceObject* pTexView) const +{ + VERIFY(DstRes.Type == DescriptorType::InputAttachment, "Input attachment resource is expected"); + RefCntAutoPtr pTexViewVk0{pTexView, IID_TextureViewVk}; +#ifdef DILIGENT_DEVELOPMENT + VerifyResourceViewBinding(*this, ResDesc.VarType, ArrayIndex, pTexView, pTexViewVk0.RawPtr(), {TEXTURE_VIEW_SHADER_RESOURCE}, DstRes.pObject.RawPtr()); +#endif + if (UpdateCachedResource(std::move(pTexViewVk0), [](const TextureViewVkImpl*, const TextureViewVkImpl*) {})) + { + // Do not update descriptor for a dynamic image. All dynamic resource descriptors + // are updated at once by CommitDynamicResources() when SRB is committed. + if (vkDescrSet != VK_NULL_HANDLE && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VkDescriptorImageInfo DescrImgInfo = DstRes.GetInputAttachmentDescriptorWriteInfo(); + UpdateDescriptorHandle(&DescrImgInfo, nullptr, nullptr); + } + // + } +} + +void BindResourceHelper::CacheAccelerationStructure(IDeviceObject* pTLAS) const +{ + VERIFY(DstRes.Type == DescriptorType::AccelerationStructure, "Acceleration Structure resource is expected"); + RefCntAutoPtr pTLASVk{pTLAS, IID_TopLevelASVk}; +#ifdef DILIGENT_DEVELOPMENT + VerifyTLASResourceBinding(*this, ResDesc.VarType, ArrayIndex, pTLASVk.RawPtr(), DstRes.pObject.RawPtr()); +#endif + if (UpdateCachedResource(std::move(pTLASVk), [](const TopLevelASVkImpl*, const TopLevelASVkImpl*) {})) + { + // Do not update descriptor for a dynamic TLAS. All dynamic resource descriptors + // are updated at once by CommitDynamicResources() when SRB is committed. + if (vkDescrSet != VK_NULL_HANDLE && ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + VkWriteDescriptorSetAccelerationStructureKHR DescrASInfo = DstRes.GetAccelerationStructureWriteInfo(); + UpdateDescriptorHandle(nullptr, nullptr, nullptr, &DescrASInfo); + } + // + } +} + +void BindResourceHelper::UpdateDescriptorHandle(const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView, + const VkWriteDescriptorSetAccelerationStructureKHR* pAccelStructInfo) const +{ + VERIFY_EXPR(vkDescrSet != VK_NULL_HANDLE); + + VkWriteDescriptorSet WriteDescrSet; + WriteDescrSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + WriteDescrSet.pNext = pAccelStructInfo; + WriteDescrSet.dstSet = vkDescrSet; + WriteDescrSet.dstBinding = Attribs.BindingIndex; + WriteDescrSet.dstArrayElement = ArrayIndex; + WriteDescrSet.descriptorCount = 1; + // descriptorType must be the same type as that specified in VkDescriptorSetLayoutBinding for dstSet at dstBinding. + // The type of the descriptor also controls which array the descriptors are taken from. (13.2.4) + WriteDescrSet.descriptorType = GetVkDescriptorType(DstRes.Type); + WriteDescrSet.pImageInfo = pImageInfo; + WriteDescrSet.pBufferInfo = pBufferInfo; + WriteDescrSet.pTexelBufferView = pTexelBufferView; + + Signature.GetDevice()->GetLogicalDevice().UpdateDescriptorSets(1, &WriteDescrSet, 0, nullptr); +} + +String BindResourceHelper::GetPrintName(Uint32 ArrayInd) const +{ + return PipelineResourceSignatureVkImpl::GetPrintName(ResDesc, ArrayInd); +} + +RESOURCE_DIMENSION BindResourceHelper::GetResourceDimension() const +{ + return RESOURCE_DIM_UNDEFINED; // AZ TODO +} + +bool BindResourceHelper::IsMultisample() const +{ + return false; // AZ TODO +} + +} // namespace + +void PipelineResourceSignatureVkImpl::BindResource(IDeviceObject* pObj, + Uint32 ArrayIndex, + Uint32 ResIndex, + ShaderResourceCacheVk& ResourceCache) const +{ + auto& ResDesc = GetResource(ResIndex); + auto& Attribs = GetAttribs(ResIndex); + auto& DstDescrSet = ResourceCache.GetDescriptorSet(Attribs.DescrSet); + auto& DstRes = DstDescrSet.GetResource(Attribs.CacheOffset + ArrayIndex); + + VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize); + VERIFY(DstRes.Type == Attribs.Type, "Inconsistent types"); + + BindResourceHelper Helper{ + DstRes, + ResDesc, + Attribs, + ArrayIndex, + DstDescrSet.GetVkDescriptorSet(), + *this, + ResourceCache}; + + Helper.BindResource(pObj); +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 225e3f82..736154a5 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -502,6 +502,98 @@ std::vector BuildRTShaderGroupDescription( return ShaderGroups; } +void GetShaderResourceTypeAndFlags(SPIRVShaderResourceAttribs::ResourceType Type, + SHADER_RESOURCE_TYPE& OutType, + PIPELINE_RESOURCE_FLAGS& OutFlags) +{ + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type below"); + OutFlags = PIPELINE_RESOURCE_FLAG_UNKNOWN; + switch (Type) + { + case SPIRVShaderResourceAttribs::ResourceType::UniformBuffer: + OutType = SHADER_RESOURCE_TYPE_CONSTANT_BUFFER; + break; + + case SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer: + // Read-only storage buffers map to buffer SRV + // https://github.com/KhronosGroup/SPIRV-Cross/wiki/Reflection-API-user-guide#read-write-vs-read-only-resources-for-hlsl + OutType = SHADER_RESOURCE_TYPE_BUFFER_SRV; + break; + + case SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer: + OutType = SHADER_RESOURCE_TYPE_BUFFER_UAV; + break; + + case SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer: + OutType = SHADER_RESOURCE_TYPE_BUFFER_SRV; + OutFlags = PIPELINE_RESOURCE_FLAG_TEXEL_BUFFER; + break; + + case SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer: + OutType = SHADER_RESOURCE_TYPE_BUFFER_UAV; + OutFlags = PIPELINE_RESOURCE_FLAG_TEXEL_BUFFER; + break; + + case SPIRVShaderResourceAttribs::ResourceType::StorageImage: + OutType = SHADER_RESOURCE_TYPE_TEXTURE_UAV; + break; + + case SPIRVShaderResourceAttribs::ResourceType::SampledImage: + OutType = SHADER_RESOURCE_TYPE_TEXTURE_SRV; + OutFlags = PIPELINE_RESOURCE_FLAG_COMBINED_IMAGE; + break; + + case SPIRVShaderResourceAttribs::ResourceType::AtomicCounter: + LOG_WARNING_MESSAGE("There is no appropriate shader resource type for atomic counter"); + OutType = SHADER_RESOURCE_TYPE_BUFFER_UAV; + break; + + case SPIRVShaderResourceAttribs::ResourceType::SeparateImage: + OutType = SHADER_RESOURCE_TYPE_TEXTURE_SRV; + break; + + case SPIRVShaderResourceAttribs::ResourceType::SeparateSampler: + OutType = SHADER_RESOURCE_TYPE_SAMPLER; + break; + + case SPIRVShaderResourceAttribs::ResourceType::InputAttachment: + OutType = SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT; + break; + + case SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure: + OutType = SHADER_RESOURCE_TYPE_ACCEL_STRUCT; + break; + + default: + UNEXPECTED("Unknown SPIRV resource type"); + OutType = SHADER_RESOURCE_TYPE_UNKNOWN; + break; + } +} + +void VerifyResourceMerge(const SPIRVShaderResourceAttribs& ExistingRes, + const SPIRVShaderResourceAttribs& NewResAttribs) +{ + DEV_CHECK_ERR(ExistingRes.Type == NewResAttribs.Type, + "Shader variable '", NewResAttribs.Name, + "' exists in multiple shaders from the same shader stage, but its type is not consistent between " + "shaders. All variables with the same name from the same shader stage must have the same type."); + + DEV_CHECK_ERR(ExistingRes.ResourceDim == NewResAttribs.ResourceDim, + "Shader variable '", NewResAttribs.Name, + "' exists in multiple shaders from the same shader stage, but its resource dimension is not consistent between " + "shaders. All variables with the same name from the same shader stage must have the same resource dimension."); + + DEV_CHECK_ERR(ExistingRes.ArraySize == NewResAttribs.ArraySize, + "Shader variable '", NewResAttribs.Name, + "' exists in multiple shaders from the same shader stage, but its array size is not consistent between " + "shaders. All variables with the same name from the same shader stage must have the same array size."); + + DEV_CHECK_ERR(ExistingRes.IsMS == NewResAttribs.IsMS, + "Shader variable '", NewResAttribs.Name, + "' exists in multiple shaders from the same shader stage, but its multisample flag is not consistent between " + "shaders. All variables with the same name from the same shader stage must either be multisample or non-multisample."); +} } // namespace @@ -509,8 +601,7 @@ PipelineStateVkImpl::ShaderStageInfo::ShaderStageInfo(const ShaderVkImpl* pShade Type{pShader->GetDesc().ShaderType}, Shaders{pShader}, SPIRVs{pShader->GetSPIRV()} -{ -} +{} void PipelineStateVkImpl::ShaderStageInfo::Append(const ShaderVkImpl* pShader) { @@ -640,56 +731,105 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea if (SignatureCount == 0 || CreateInfo.ppResourceSignatures == nullptr) { - using ResourceNameToIndex_t = std::unordered_map; + struct UniqueResource + { + SPIRVShaderResourceAttribs const* Attribs = nullptr; + Uint32 DescIndex = ~0u; + }; + using ResourceNameToIndex_t = std::unordered_map; std::vector Resources; ResourceNameToIndex_t UniqueNames; - const PipelineResourceLayoutDesc& LayoutDesc = CreateInfo.PSODesc.ResourceLayout; + const char* pCombinedSamplerSuffix = nullptr; + const auto& LayoutDesc = CreateInfo.PSODesc.ResourceLayout; for (auto& Stage : ShaderStages) { UniqueNames.clear(); for (auto* pShader : Stage.Shaders) { - pShader->GetShaderResources().ProcessResources( + const auto DefaultVarType = LayoutDesc.DefaultVariableType; + auto& ShaderResources = pShader->GetShaderResources(); + + ShaderResources.ProcessResources( [&](const SPIRVShaderResourceAttribs& Res, Uint32) // { - bool IsNewResource = UniqueNames.emplace(HashMapStringKey{Res.Name}, static_cast(Resources.size())).second; - if (IsNewResource) + auto IterAndAssigned = UniqueNames.emplace(HashMapStringKey{Res.Name}, UniqueResource{&Res, static_cast(Resources.size())}); + if (IterAndAssigned.second) { - Resources.emplace_back(Res.Name, Res.ArraySize, Res.GetShaderResourceType(Res.Type), Stage.Type, CreateInfo.PSODesc.ResourceLayout.DefaultVariableType); + SHADER_RESOURCE_TYPE Type; + PIPELINE_RESOURCE_FLAGS Flags; + GetShaderResourceTypeAndFlags(Res.Type, Type, Flags); + + Resources.emplace_back(Res.Name, Res.ArraySize, Type, Stage.Type, DefaultVarType, Flags); + } + else + { + VerifyResourceMerge(*IterAndAssigned.first->second.Attribs, Res); } }); + // merge combined sampler suffixes + if (ShaderResources.IsUsingCombinedSamplers() && ShaderResources.GetNumSepSmplrs() > 0) + { + if (pCombinedSamplerSuffix != nullptr) + { + if (strcmp(pCombinedSamplerSuffix, ShaderResources.GetCombinedSamplerSuffix()) != 0) + LOG_ERROR_AND_THROW("AZ TODO"); + } + else + { + pCombinedSamplerSuffix = ShaderResources.GetCombinedSamplerSuffix(); + } + } + for (Uint32 i = 0; i < LayoutDesc.NumVariables; ++i) { const auto& Var = LayoutDesc.Variables[i]; if (Var.ShaderStages & Stage.Type) { auto Iter = UniqueNames.find(HashMapStringKey{Var.Name}); - if (Iter != UniqueNames.end() && Iter->second < Resources.size()) - Resources[Iter->second].VarType = Var.Type; + if (Iter != UniqueNames.end()) + { + auto& Res = Resources[Iter->second.DescIndex]; + Res.VarType = Var.Type; + + // apply new variable type to sampler too + if (ShaderResources.IsUsingCombinedSamplers() && Res.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV) + { + String SampName = String{Var.Name} + ShaderResources.GetCombinedSamplerSuffix(); + auto SampIter = UniqueNames.find(HashMapStringKey{SampName.c_str()}); + if (SampIter != UniqueNames.end()) + Resources[SampIter->second.DescIndex].VarType = Var.Type; + } + } } } } } - PipelineResourceSignatureDesc ResSignDesc; - ResSignDesc.Resources = Resources.data(); - ResSignDesc.NumResources = static_cast(Resources.size()); - ResSignDesc.ImmutableSamplers = LayoutDesc.ImmutableSamplers; - ResSignDesc.NumImmutableSamplers = LayoutDesc.NumImmutableSamplers; - ResSignDesc.BindingIndex = 0; - - GetDevice()->CreatePipelineResourceSignature(ResSignDesc, &TempSignature); - if (TempSignature == nullptr) - LOG_ERROR_AND_THROW(""); - - Signatures[0] = TempSignature; - SignatureCount = 1; + if (Resources.size()) + { + PipelineResourceSignatureDesc ResSignDesc; + ResSignDesc.Resources = Resources.data(); + ResSignDesc.NumResources = static_cast(Resources.size()); + ResSignDesc.ImmutableSamplers = LayoutDesc.ImmutableSamplers; + ResSignDesc.NumImmutableSamplers = LayoutDesc.NumImmutableSamplers; + ResSignDesc.BindingIndex = 0; + ResSignDesc.SRBAllocationGranularity = CreateInfo.PSODesc.SRBAllocationGranularity; + ResSignDesc.CombinedSamplerSuffix = pCombinedSamplerSuffix; + + GetDevice()->CreatePipelineResourceSignature(ResSignDesc, &TempSignature, true); + + if (TempSignature == nullptr) + LOG_ERROR_AND_THROW("AZ TODO"); + + Signatures[0] = TempSignature; + SignatureCount = 1; + } } - m_PipelineLayout = GetDevice()->GetPipelineLayoutCache().GetLayout(Signatures.data(), SignatureCount); + m_PipelineLayout.Create(GetDevice(), Signatures.data(), SignatureCount); // verify that pipeline layout is compatible with shader resources and // remap resource bindings @@ -710,10 +850,10 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea [&](const SPIRVShaderResourceAttribs& Res, Uint32) // { PipelineLayoutVk::ResourceInfo Info; - if (!m_PipelineLayout->GetResourceInfo(Res.Name, ShaderType, Info)) + if (!m_PipelineLayout.GetResourceInfo(Res.Name, ShaderType, Info)) { LOG_ERROR_AND_THROW("Shader '", pShader->GetDesc().Name, "' contains resource with name '", Res.Name, - "' that is not presented in any pipeline resource signature that used to create pipeline state '", + "' that is not presented in any pipeline resource signature that is used to create pipeline state '", m_Desc.Name, "'."); } SPIRV[Res.BindingDecorationOffset] = Info.BindingIndex; @@ -744,11 +884,6 @@ PipelineStateVkImpl::TShaderStages PipelineStateVkImpl::InitInternalObjects( InitializePipelineDesc(CreateInfo, MemPool); - // It is important to construct all objects before initializing them because if an exception is thrown, - // destructors will be called for all objects - - //InitResourceLayouts(CreateInfo, ShaderStages); - // Create shader modules and initialize shader stages InitPipelineShaderStages(LogicalDevice, ShaderStages, ShaderModules, vkShaderStages); @@ -767,7 +902,7 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, Rende InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules); - CreateGraphicsPipeline(pDeviceVk, vkShaderStages, *m_PipelineLayout, m_Desc, GetGraphicsPipelineDesc(), m_Pipeline, m_pRenderPass); + CreateGraphicsPipeline(pDeviceVk, vkShaderStages, m_PipelineLayout, m_Desc, GetGraphicsPipelineDesc(), m_Pipeline, m_pRenderPass); } catch (...) { @@ -786,7 +921,7 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, Rende InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules); - CreateComputePipeline(pDeviceVk, vkShaderStages, *m_PipelineLayout, m_Desc, m_Pipeline); + CreateComputePipeline(pDeviceVk, vkShaderStages, m_PipelineLayout, m_Desc, m_Pipeline); } catch (...) { @@ -809,7 +944,7 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, Rende const auto vkShaderGroups = BuildRTShaderGroupDescription(CreateInfo, m_pRayTracingPipelineData->NameToGroupIndex, ShaderStages); - CreateRayTracingPipeline(pDeviceVk, vkShaderStages, vkShaderGroups, *m_PipelineLayout, m_Desc, GetRayTracingPipelineDesc(), m_Pipeline); + CreateRayTracingPipeline(pDeviceVk, vkShaderStages, vkShaderGroups, m_PipelineLayout, m_Desc, GetRayTracingPipelineDesc(), m_Pipeline); VERIFY(m_pRayTracingPipelineData->NameToGroupIndex.size() == vkShaderGroups.size(), "The size of NameToGroupIndex map does not match the actual number of groups in the pipeline. This is a bug."); @@ -835,6 +970,7 @@ void PipelineStateVkImpl::Destruct() TPipelineStateBase::Destruct(); m_pDevice->SafeReleaseDeviceObject(std::move(m_Pipeline), m_Desc.CommandQueueMask); + m_PipelineLayout.Release(m_pDevice, m_Desc.CommandQueueMask); auto& RawAllocator = GetRawAllocator(); if (m_pRawMem) @@ -842,33 +978,23 @@ void PipelineStateVkImpl::Destruct() RawAllocator.Free(m_pRawMem); m_pRawMem = nullptr; } - - // AZ TODO: - /* - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) - { - if (m_StaticVarsMgrs != nullptr) - { - m_StaticVarsMgrs[s].DestroyVariables(GetRawAllocator()); - m_StaticVarsMgrs[s].~ShaderVariableManagerVk(); - } - - if (m_StaticResCaches != nullptr) - { - m_StaticResCaches[s].~ShaderResourceCacheVk(); - } - } - */ } bool PipelineStateVkImpl::IsCompatibleWith(const IPipelineState* pPSO) const { - return m_PipelineLayout == ValidatedCast(pPSO)->m_PipelineLayout; -} + const auto& lhs = m_PipelineLayout; + const auto& rhs = ValidatedCast(pPSO)->m_PipelineLayout; -void PipelineStateVkImpl::BindStaticResources(Uint32 ShaderFlags, IResourceMapping* pResourceMapping, Uint32 Flags) -{ - //m_StaticVarsMgr.BindResources(ShaderFlags, pResourceMapping, Flags); + if (lhs.GetSignatureCount() != rhs.GetSignatureCount()) + return false; + + if (lhs.GetSignatureCount() == 0) + return true; + + if (lhs.GetSignatureCount() == 1) + return lhs.GetSignature(0)->IsCompatibleWith(*rhs.GetSignature(0)); + + return false; } } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp index cc1a444c..edfcb670 100644 --- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp @@ -91,7 +91,6 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* m_LogicalVkDevice {std::move(LogicalDevice) }, m_EngineAttribs {EngineCI }, m_FramebufferCache {*this }, - m_PipelineLayoutCache {*this }, m_ImplicitRenderPassCache{*this }, m_DescriptorSetAllocator { @@ -105,8 +104,8 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, EngineCI.MainDescriptorPoolSize.NumStorageImageDescriptors}, {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, EngineCI.MainDescriptorPoolSize.NumUniformTexelBufferDescriptors}, {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, EngineCI.MainDescriptorPoolSize.NumStorageTexelBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.MainDescriptorPoolSize.NumUniformBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.MainDescriptorPoolSize.NumStorageBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.MainDescriptorPoolSize.NumUniformBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.MainDescriptorPoolSize.NumStorageBufferDescriptors}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, EngineCI.MainDescriptorPoolSize.NumUniformBufferDescriptors}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, EngineCI.MainDescriptorPoolSize.NumStorageBufferDescriptors}, {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, EngineCI.MainDescriptorPoolSize.NumInputAttachmentDescriptors}, @@ -127,8 +126,8 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, EngineCI.DynamicDescriptorPoolSize.NumStorageImageDescriptors}, {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumUniformTexelBufferDescriptors}, {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumStorageTexelBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumUniformBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumStorageBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumUniformBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumStorageBufferDescriptors}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, EngineCI.DynamicDescriptorPoolSize.NumUniformBufferDescriptors}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, EngineCI.DynamicDescriptorPoolSize.NumStorageBufferDescriptors}, {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, EngineCI.MainDescriptorPoolSize.NumInputAttachmentDescriptors}, @@ -171,8 +170,7 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* m_PhysicalDevice->GetExtProperties().MeshShader.maxDrawMeshTasksCount, m_PhysicalDevice->GetExtProperties().RayTracingPipeline.maxRayRecursionDepth, m_PhysicalDevice->GetExtProperties().RayTracingPipeline.maxRayDispatchInvocationCount - }, - m_PipelineLayoutAllocator{GetRawAllocator(), sizeof(PipelineLayoutVk), 128} + } // clang-format on { static_assert(sizeof(VulkanDescriptorPoolSize) == sizeof(Uint32) * 11, "Please add new descriptors to m_DescriptorSetAllocator and m_DynamicDescriptorPool constructors"); @@ -278,10 +276,16 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* SamCaps.BorderSamplingModeSupported = True; SamCaps.AnisotropicFilteringSupported = vkEnabledFeatures.samplerAnisotropy; SamCaps.LODBiasSupported = True; + + PipelineResourceSignatureDesc Desc; + Desc.Name = "Empty resource signature"; + CreatePipelineResourceSignature(Desc, &m_pEmptySignature, true); } RenderDeviceVkImpl::~RenderDeviceVkImpl() { + m_pEmptySignature.Release(); + // Explicitly destroy dynamic heap. This will move resources owned by // the heap into release queues m_DynamicMemoryManager.Destroy(); @@ -833,20 +837,21 @@ void RenderDeviceVkImpl::CreateSBT(const ShaderBindingTableDesc& Desc, void RenderDeviceVkImpl::CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, IPipelineResourceSignature** ppSignature) +{ + CreatePipelineResourceSignature(Desc, ppSignature, false); +} + +void RenderDeviceVkImpl::CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature, + bool IsDeviceInternal) { CreateDeviceObject("PipelineResourceSignature", Desc, ppSignature, [&]() // { - PipelineResourceSignatureVkImpl* pPRSVk(NEW_RC_OBJ(m_PipeResSignAllocator, "PipelineResourceSignatureVkImpl instance", PipelineResourceSignatureVkImpl)(this, Desc)); + PipelineResourceSignatureVkImpl* pPRSVk(NEW_RC_OBJ(m_PipeResSignAllocator, "PipelineResourceSignatureVkImpl instance", PipelineResourceSignatureVkImpl)(this, Desc, IsDeviceInternal)); pPRSVk->QueryInterface(IID_PipelineResourceSignature, reinterpret_cast(ppSignature)); OnCreateDeviceObject(pPRSVk); }); } -void RenderDeviceVkImpl::CreatePipelineLayout(IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount, PipelineLayoutVk** ppPipelineLayout) -{ - PipelineLayoutVk* pPLVk(NEW_RC_OBJ(m_PipelineLayoutAllocator, "PipelineLayoutVk instance", PipelineLayoutVk)(this, ppSignatures, SignatureCount)); - *ppPipelineLayout = pPLVk; -} - } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp index 3672853f..a4932563 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp @@ -73,22 +73,23 @@ ShaderResourceBindingVkImpl::ShaderResourceBindingVkImpl(IReferenceCounters* auto& ResourceCacheDataAllocator = SRBMemAllocator.GetResourceCacheDataAllocator(0); pPRS->InitResourceCache(m_ShaderResourceCache, ResourceCacheDataAllocator, pPRS->GetDesc().Name); + // Use resource signature to initialize resource memory in the cache + pPRS->InitializeResourceMemoryInCache(m_ShaderResourceCache); + for (Uint32 s = 0; s < m_NumShaders; ++s) { - auto ShaderInd = GetShaderTypePipelineIndex(pPRS->GetShaderStageType(s), pPRS->GetPipelineType()); + const auto ShaderType = pPRS->GetShaderStageType(s); + const auto ShaderInd = GetShaderTypePipelineIndex(ShaderType, pPRS->GetPipelineType()); m_ShaderVarIndex[ShaderInd] = static_cast(s); auto& VarDataAllocator = SRBMemAllocator.GetShaderVariableDataAllocator(s); - // Use source layout to initialize resource memory in the cache - pPRS->InitializeResourceMemoryInCache(m_ShaderResourceCache); - // Create shader variable manager in place // Initialize vars manager to reference mutable and dynamic variables // Note that the cache has space for all variable types const SHADER_RESOURCE_VARIABLE_TYPE VarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; - m_pShaderVarMgrs[s].Initialize(*pPRS, VarDataAllocator, VarTypes, _countof(VarTypes)); + m_pShaderVarMgrs[s].Initialize(*pPRS, VarDataAllocator, VarTypes, _countof(VarTypes), ShaderType); } #ifdef DILIGENT_DEBUG m_ShaderResourceCache.DbgVerifyResourceInitialization(); @@ -124,7 +125,20 @@ void ShaderResourceBindingVkImpl::Destruct() void ShaderResourceBindingVkImpl::BindResources(Uint32 ShaderFlags, IResourceMapping* pResMapping, Uint32 Flags) { - // AZ TODO + const auto PipelineType = GetPipelineType(); + for (Uint32 ShaderInd = 0; ShaderInd < m_ShaderVarIndex.size(); ++ShaderInd) + { + const auto VarMngrInd = m_ShaderVarIndex[ShaderInd]; + if (VarMngrInd >= 0) + { + // ShaderInd is the shader type pipeline index here + const auto ShaderType = GetShaderTypeFromPipelineIndex(ShaderInd, PipelineType); + if (ShaderFlags & ShaderType) + { + m_pShaderVarMgrs[VarMngrInd].BindResources(pResMapping, Flags); + } + } + } } Uint32 ShaderResourceBindingVkImpl::GetVariableCount(SHADER_TYPE ShaderType) const diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index 820d1e9a..28adf43a 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -65,8 +65,13 @@ void ShaderResourceCacheVk::InitializeSets(IMemoryAllocator& MemAllocator, Uint3 VERIFY(NumSets < std::numeric_limits::max(), "NumSets (", NumSets, ") exceed maximum representable value"); m_NumSets = static_cast(NumSets); m_TotalResources = 0; + for (Uint32 t = 0; t < NumSets; ++t) + { + VERIFY_EXPR(SetSizes[t] > 0); m_TotalResources += SetSizes[t]; + } + auto MemorySize = NumSets * sizeof(DescriptorSet) + m_TotalResources * sizeof(Resource); VERIFY_EXPR(MemorySize == GetRequiredMemorySize(NumSets, SetSizes)); #ifdef DILIGENT_DEBUG @@ -89,7 +94,7 @@ void ShaderResourceCacheVk::InitializeSets(IMemoryAllocator& MemAllocator, Uint3 } } -void ShaderResourceCacheVk::InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, VkDescriptorType Type) +void ShaderResourceCacheVk::InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, DescriptorType Type) { auto& DescrSet = GetDescriptorSet(Set); for (Uint32 res = 0; res < ArraySize; ++res) @@ -117,19 +122,27 @@ void ShaderResourceCacheVk::DbgVerifyDynamicBuffersCounter() const for (Uint32 res = 0; res < m_TotalResources; ++res) { const auto& Res = pResources[res]; - if (Res.Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || - Res.Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) - { - if (Res.pObject && Res.pObject.RawPtr()->GetDesc().Usage == USAGE_DYNAMIC) - ++NumDynamicBuffers; - } - else if (Res.Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || - Res.Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC || - Res.Type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || - Res.Type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) + switch (Res.Type) { - if (Res.pObject && Res.pObject.RawPtr()->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) - ++NumDynamicBuffers; + case DescriptorType::UniformBuffer: + case DescriptorType::UniformBufferDynamic: + { + if (Res.pObject && Res.pObject.RawPtr()->GetDesc().Usage == USAGE_DYNAMIC) + ++NumDynamicBuffers; + break; + } + case DescriptorType::StorageBuffer: + case DescriptorType::StorageBufferDynamic: + case DescriptorType::StorageBuffer_ReadOnly: + case DescriptorType::StorageBufferDynamic_ReadOnly: + case DescriptorType::UniformTexelBuffer: + case DescriptorType::StorageTexelBuffer: + case DescriptorType::StorageTexelBuffer_ReadOnly: + { + if (Res.pObject && Res.pObject.RawPtr()->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) + ++NumDynamicBuffers; + break; + } } } VERIFY(NumDynamicBuffers == m_NumDynamicBuffers, "The number of dynamic buffers (", m_NumDynamicBuffers, ") does not match the actual number (", NumDynamicBuffers, ")"); @@ -159,8 +172,8 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) auto& Res = pResources[res]; switch (Res.Type) { - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case DescriptorType::UniformBuffer: + case DescriptorType::UniformBufferDynamic: { auto* pBufferVk = Res.pObject.RawPtr(); if (pBufferVk != nullptr && pBufferVk->IsInKnownState()) @@ -192,19 +205,19 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + case DescriptorType::StorageBuffer: + case DescriptorType::StorageBufferDynamic: + case DescriptorType::StorageBuffer_ReadOnly: + case DescriptorType::StorageBufferDynamic_ReadOnly: + case DescriptorType::UniformTexelBuffer: + case DescriptorType::StorageTexelBuffer: + case DescriptorType::StorageTexelBuffer_ReadOnly: { auto* pBuffViewVk = Res.pObject.RawPtr(); auto* pBufferVk = pBuffViewVk != nullptr ? ValidatedCast(pBuffViewVk->GetBuffer()) : nullptr; if (pBufferVk != nullptr && pBufferVk->IsInKnownState()) { - const RESOURCE_STATE RequiredState = - Res.Type != VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ? // AZ TODO: read-only buffer ??? - RESOURCE_STATE_UNORDERED_ACCESS : - RESOURCE_STATE_SHADER_RESOURCE; + const RESOURCE_STATE RequiredState = DescriptorTypeToResourceState(Res.Type); #ifdef DILIGENT_DEBUG const VkAccessFlags RequiredAccessFlags = (RequiredState == RESOURCE_STATE_SHADER_RESOURCE) ? VK_ACCESS_SHADER_READ_BIT : @@ -239,9 +252,10 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case DescriptorType::CombinedImageSampler: + case DescriptorType::SeparateImage: + case DescriptorType::StorageImage: + case DescriptorType::StorageImage_ReadOnly: { auto* pTextureViewVk = Res.pObject.RawPtr(); auto* pTextureVk = pTextureViewVk != nullptr ? ValidatedCast(pTextureViewVk->GetTexture()) : nullptr; @@ -253,7 +267,7 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) // VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // or VK_IMAGE_LAYOUT_GENERAL layout in order to access its data in a shader (13.1.3, 13.1.4). RESOURCE_STATE RequiredState; - if (Res.Type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) + if (Res.Type == DescriptorType::StorageImage) { RequiredState = RESOURCE_STATE_UNORDERED_ACCESS; VERIFY_EXPR(ResourceStateToVkImageLayout(RequiredState) == VK_IMAGE_LAYOUT_GENERAL); @@ -302,13 +316,13 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case VK_DESCRIPTOR_TYPE_SAMPLER: + case DescriptorType::Sampler: { // Nothing to do with samplers } break; - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case DescriptorType::InputAttachment: { // Nothing to do with input attachments - they are transitioned by the render pass. // There is nothing we can validate here - a texture may be in different state at @@ -316,7 +330,7 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: + case DescriptorType::AccelerationStructure: { auto* pTLASVk = Res.pObject.RawPtr(); if (pTLASVk != nullptr && pTLASVk->IsInKnownState()) @@ -362,8 +376,8 @@ template void ShaderResourceCacheVk::TransitionResources(DeviceContextVkIm VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetUniformBufferDescriptorWriteInfo() const { // clang-format off - VERIFY(Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || - Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + VERIFY(Type == DescriptorType::UniformBuffer || + Type == DescriptorType::UniformBufferDynamic, "Uniform buffer resource is expected"); // clang-format on DEV_CHECK_ERR(pObject != nullptr, "Unable to get uniform buffer write info: cached object is null"); @@ -385,10 +399,10 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetUniformBufferDescript VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescriptorWriteInfo() const { // clang-format off - VERIFY(Type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || - Type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER || - Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || - Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, + VERIFY(Type == DescriptorType::StorageBuffer || + Type == DescriptorType::StorageBufferDynamic || + Type == DescriptorType::StorageBuffer_ReadOnly || + Type == DescriptorType::StorageBufferDynamic_ReadOnly, "Storage buffer resource is expected"); // clang-format on DEV_CHECK_ERR(pObject != nullptr, "Unable to get storage buffer write info: cached object is null"); @@ -399,7 +413,7 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescript // VK_DESCRIPTOR_TYPE_STORAGE_BUFFER or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC descriptor type // require buffer to be created with VK_BUFFER_USAGE_STORAGE_BUFFER_BIT (13.2.4) - /*if (Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer) // AZ TODO + if (Type == DescriptorType::StorageBuffer_ReadOnly || Type == DescriptorType::StorageBufferDynamic_ReadOnly) { // HLSL buffer SRVs are mapped to read-only storge buffers in SPIR-V VERIFY(ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE, "Attempting to bind buffer view '", ViewDesc.Name, @@ -407,7 +421,7 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescript GetBufferViewTypeLiteralName(ViewDesc.ViewType)); VERIFY((pBuffVk->GetDesc().BindFlags & BIND_SHADER_RESOURCE) != 0, "Buffer '", pBuffVk->GetDesc().Name, "' being set as read-only storage buffer was not created with BIND_SHADER_RESOURCE flag"); } - else if (Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) + else if (Type == DescriptorType::StorageBuffer || Type == DescriptorType::StorageBufferDynamic) { VERIFY(ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS, "Attempting to bind buffer view '", ViewDesc.Name, "' as writable storage buffer. Expected view type is BUFFER_VIEW_UNORDERED_ACCESS. Actual type: ", @@ -417,7 +431,7 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescript else { UNEXPECTED("Unexpected resource type"); - }*/ + } VkDescriptorBufferInfo DescrBuffInfo; DescrBuffInfo.buffer = pBuffVk->GetVkBuffer(); @@ -431,23 +445,24 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescript VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetImageDescriptorWriteInfo(bool IsImmutableSampler) const { // clang-format off - VERIFY(Type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || - Type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || - Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VERIFY(Type == DescriptorType::StorageImage || + Type == DescriptorType::StorageImage_ReadOnly || + Type == DescriptorType::SeparateImage || + Type == DescriptorType::CombinedImageSampler, "Storage image, separate image or sampled image resource is expected"); // clang-format on DEV_CHECK_ERR(pObject != nullptr, "Unable to get image descriptor write info: cached object is null"); - bool IsStorageImage = Type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + bool IsStorageImage = (Type == DescriptorType::StorageImage); auto* pTexViewVk = pObject.RawPtr(); VERIFY_EXPR(pTexViewVk->GetDesc().ViewType == (IsStorageImage ? TEXTURE_VIEW_UNORDERED_ACCESS : TEXTURE_VIEW_SHADER_RESOURCE)); VkDescriptorImageInfo DescrImgInfo; DescrImgInfo.sampler = VK_NULL_HANDLE; - VERIFY(Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || !IsImmutableSampler, + VERIFY(Type == DescriptorType::CombinedImageSampler || !IsImmutableSampler, "Immutable sampler can't be assigned to separarate image or storage image"); - if (Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER && !IsImmutableSampler) + if (Type == DescriptorType::CombinedImageSampler && !IsImmutableSampler) { // Immutable samplers are permanently bound into the set layout; later binding a sampler // into an immutable sampler slot in a descriptor set is not allowed (13.2.1) @@ -488,8 +503,9 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetImageDescriptorWriteIn VkBufferView ShaderResourceCacheVk::Resource::GetBufferViewWriteInfo() const { // clang-format off - VERIFY(Type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || - Type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + VERIFY(Type == DescriptorType::UniformTexelBuffer || + Type == DescriptorType::StorageTexelBuffer || + Type == DescriptorType::StorageTexelBuffer_ReadOnly, "Uniform or storage buffer resource is expected"); // clang-format on DEV_CHECK_ERR(pObject != nullptr, "Unable to get buffer view write info: cached object is null"); @@ -503,7 +519,7 @@ VkBufferView ShaderResourceCacheVk::Resource::GetBufferViewWriteInfo() const VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWriteInfo() const { - VERIFY(Type == VK_DESCRIPTOR_TYPE_SAMPLER, "Separate sampler resource is expected"); + VERIFY(Type == DescriptorType::Sampler, "Separate sampler resource is expected"); DEV_CHECK_ERR(pObject != nullptr, "Unable to get separate sampler descriptor write info: cached object is null"); auto* pSamplerVk = pObject.RawPtr(); @@ -518,7 +534,7 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWrite VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetInputAttachmentDescriptorWriteInfo() const { - VERIFY(Type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, "Input attachment resource is expected"); + VERIFY(Type == DescriptorType::InputAttachment, "Input attachment resource is expected"); DEV_CHECK_ERR(pObject != nullptr, "Unable to get input attachment write info: cached object is null"); auto* pTexViewVk = pObject.RawPtr(); @@ -534,7 +550,7 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetInputAttachmentDescrip VkWriteDescriptorSetAccelerationStructureKHR ShaderResourceCacheVk::Resource::GetAccelerationStructureWriteInfo() const { - VERIFY(Type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, "Acceleration structure resource is expected"); + VERIFY(Type == DescriptorType::AccelerationStructure, "Acceleration structure resource is expected"); DEV_CHECK_ERR(pObject != nullptr, "Unable to get acceleration structure write info: cached object is null"); auto* pTLASVk = pObject.RawPtr(); diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp index a93c4482..4eaf49bd 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp @@ -30,9 +30,6 @@ #include "ShaderVariableVk.hpp" #include "ShaderResourceVariableBase.hpp" #include "PipelineResourceSignatureVkImpl.hpp" -#include "SamplerVkImpl.hpp" -#include "TextureViewVkImpl.hpp" -#include "TopLevelASVkImpl.hpp" namespace Diligent { @@ -40,28 +37,30 @@ namespace Diligent size_t ShaderVariableManagerVk::GetRequiredMemorySize(const PipelineResourceSignatureVkImpl& Layout, const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, Uint32 NumAllowedTypes, + SHADER_TYPE ShaderStages, Uint32& NumVariables) { NumVariables = 0; const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); - const bool UsingSeparateSamplers = true; //Layout.IsUsingSeparateSamplers(); - const auto& Desc = Layout.GetDesc(); + const bool UsingSeparateSamplers = Layout.IsUsingSeparateSamplers(); - for (Uint32 r = 0; r < Desc.NumResources; ++r) + for (Uint32 r = 0, Count = Layout.GetTotalResourceCount(); r < Count; ++r) { - const auto& SrcRes = Desc.Resources[r]; + const auto& Res = Layout.GetResource(r); + const auto& Attr = Layout.GetAttribs(r); - if (!IsAllowedType(SrcRes.VarType, AllowedTypeBits)) + if (!IsAllowedType(Res.VarType, AllowedTypeBits)) + continue; + + if (!(Res.ShaderStages & ShaderStages)) continue; - // AZ TODO - /* // When using HLSL-style combined image samplers, we need to skip separate samplers. // Also always skip immutable separate samplers. - if (SrcRes.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && // AZ TODO - (!UsingSeparateSamplers || SrcRes.IsImmutableSamplerAssigned())) + if (Res.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && + (!UsingSeparateSamplers || Attr.ImmutableSamplerAssigned)) continue; - */ + ++NumVariables; } @@ -72,7 +71,8 @@ size_t ShaderVariableManagerVk::GetRequiredMemorySize(const PipelineResourceSign void ShaderVariableManagerVk::Initialize(const PipelineResourceSignatureVkImpl& SrcLayout, IMemoryAllocator& Allocator, const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes) + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType) { #ifdef DILIGENT_DEBUG m_pDbgAllocator = &Allocator; @@ -82,7 +82,7 @@ void ShaderVariableManagerVk::Initialize(const PipelineResourceSignatureVkImpl& const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); VERIFY_EXPR(m_NumVariables == 0); - auto MemSize = GetRequiredMemorySize(SrcLayout, AllowedVarTypes, NumAllowedTypes, m_NumVariables); + auto MemSize = GetRequiredMemorySize(SrcLayout, AllowedVarTypes, NumAllowedTypes, ShaderType, m_NumVariables); if (m_NumVariables == 0) return; @@ -90,25 +90,26 @@ void ShaderVariableManagerVk::Initialize(const PipelineResourceSignatureVkImpl& auto* pRawMem = ALLOCATE_RAW(Allocator, "Raw memory buffer for shader variables", MemSize); m_pVariables = reinterpret_cast(pRawMem); - Uint32 VarInd = 0; - const bool UsingSeparateSamplers = false; //SrcLayout.IsUsingSeparateSamplers(); - const auto& Desc = SrcLayout.GetDesc(); + Uint32 VarInd = 0; + const bool UsingSeparateSamplers = SrcLayout.IsUsingSeparateSamplers(); - for (Uint32 r = 0; r < Desc.NumResources; ++r) + for (Uint32 r = 0, Count = SrcLayout.GetTotalResourceCount(); r < Count; ++r) { - const auto& SrcRes = Desc.Resources[r]; + const auto& Res = SrcLayout.GetResource(r); + const auto& Attr = SrcLayout.GetAttribs(r); - if (!IsAllowedType(SrcRes.VarType, AllowedTypeBits)) + if (!IsAllowedType(Res.VarType, AllowedTypeBits)) + continue; + + if (!(Res.ShaderStages & ShaderType)) continue; - // AZ TODO - /* // Skip separate samplers when using combined HLSL-style image samplers. Also always skip immutable separate samplers. - if (SrcRes.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && - (!UsingSeparateSamplers || SrcRes.IsImmutableSamplerAssigned())) + if (Res.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && + (!UsingSeparateSamplers || Attr.ImmutableSamplerAssigned)) continue; - */ - ::new (m_pVariables + VarInd) ShaderVariableVkImpl(*this); + + ::new (m_pVariables + VarInd) ShaderVariableVkImpl(*this, r); ++VarInd; } VERIFY_EXPR(VarInd == m_NumVariables); @@ -192,22 +193,23 @@ void ShaderVariableManagerVk::BindResources(IResourceMapping* pResourceMapping, if ((Flags & BIND_SHADER_RESOURCES_UPDATE_ALL) == 0) Flags |= BIND_SHADER_RESOURCES_UPDATE_ALL; - /* + for (Uint32 v = 0; v < m_NumVariables; ++v) { - auto& Var = m_pVariables[v]; - const auto& Res = Var.m_Resource; + auto& Var = m_pVariables[v]; + const auto& Res = Var.GetDesc(); + const auto& Attr = Var.GetAttribs(); // There should be no immutable separate samplers - VERIFY(Res.Type != SPIRVShaderResourceAttribs::ResourceType::SeparateSampler || !Res.IsImmutableSamplerAssigned(), + VERIFY(Attr.Type != DescriptorType::Sampler || !Attr.ImmutableSamplerAssigned, "There must be no shader resource variables for immutable separate samplers"); - if ((Flags & (1 << Res.GetVariableType())) == 0) + if ((Flags & (1u << Res.VarType)) == 0) continue; for (Uint32 ArrInd = 0; ArrInd < Res.ArraySize; ++ArrInd) { - if ((Flags & BIND_SHADER_RESOURCES_KEEP_EXISTING) && Res.IsBound(ArrInd, m_ResourceCache)) + if ((Flags & BIND_SHADER_RESOURCES_KEEP_EXISTING) && Var.IsBound(ArrInd)) continue; const auto* VarName = Res.Name; @@ -215,19 +217,20 @@ void ShaderVariableManagerVk::BindResources(IResourceMapping* pResourceMapping, pResourceMapping->GetResource(VarName, &pObj, ArrInd); if (pObj) { - Res.BindResource(pObj, ArrInd, m_ResourceCache); + Var.BindResource(pObj, ArrInd); } else { - if ((Flags & BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED) && !Res.IsBound(ArrInd, m_ResourceCache)) + if ((Flags & BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED) && !Var.IsBound(ArrInd)) { - LOG_ERROR_MESSAGE("Unable to bind resource to shader variable '", Res.GetPrintName(ArrInd), + LOG_ERROR_MESSAGE("Unable to bind resource to shader variable '", + PipelineResourceSignatureVkImpl::GetPrintName(Res, ArrInd), "': resource is not found in the resource mapping. " "Do not use BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED flag to suppress the message if this is not an issue."); } } } - }*/ + } } void ShaderVariableVkImpl::Set(IDeviceObject* pObject) @@ -250,14 +253,14 @@ bool ShaderVariableVkImpl::IsBound(Uint32 ArrayIndex) const { auto& ResourceCache = m_ParentManager.m_ResourceCache; const auto& ResDesc = GetDesc(); - const auto& Binding = GetBinding(); - Uint32 CacheOffset = GetIndex(); + const auto& Attribs = GetAttribs(); + Uint32 CacheOffset = Attribs.CacheOffset; VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize); - if (Binding.DescSet < ResourceCache.GetNumDescriptorSets()) + if (Attribs.DescrSet < ResourceCache.GetNumDescriptorSets()) { - const auto& Set = ResourceCache.GetDescriptorSet(Binding.DescSet); + const auto& Set = ResourceCache.GetDescriptorSet(Attribs.DescrSet); if (CacheOffset + ArrayIndex < Set.GetSize()) { const auto& CachedRes = Set.GetResource(CacheOffset + ArrayIndex); @@ -270,511 +273,10 @@ bool ShaderVariableVkImpl::IsBound(Uint32 ArrayIndex) const void ShaderVariableVkImpl::BindResource(IDeviceObject* pObj, Uint32 ArrayIndex) const { - auto CacheOffset = GetIndex(); - auto& ResDesc = GetDesc(); - auto& Bindings = GetBinding(); + auto* pSign = m_ParentManager.m_pSignature; auto& ResourceCache = m_ParentManager.m_ResourceCache; - auto& DstDescrSet = ResourceCache.GetDescriptorSet(Bindings.DescSet); - - VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize); - - UpdateInfo Info{ - DstDescrSet.GetResource(CacheOffset + ArrayIndex), - DstDescrSet.GetVkDescriptorSet(), - ArrayIndex, - ResDesc.VarType, - Bindings.Binding, - Bindings.SamplerInd, - ResDesc.Name}; - -#ifdef DILIGENT_DEBUG - if (ResourceCache.DbgGetContentType() == ShaderResourceCacheVk::DbgCacheContentType::SRBResources) - { - if (Info.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC || Info.VarType == SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) - { - VERIFY(Info.vkDescrSet != VK_NULL_HANDLE, "Static and mutable variables must have valid vulkan descriptor set assigned"); - // Dynamic variables do not have vulkan descriptor set only until they are assigned one the first time - } - } - else if (ResourceCache.DbgGetContentType() == ShaderResourceCacheVk::DbgCacheContentType::StaticShaderResources) - { - VERIFY(Info.vkDescrSet == VK_NULL_HANDLE, "Static shader resource cache should not have vulkan descriptor set allocation"); - } - else - { - UNEXPECTED("Unexpected shader resource cache content type"); - } -#endif - - VERIFY(Info.DstRes.Type == PipelineResourceSignatureVkImpl::GetVkDescriptorType(ResDesc), "Inconsistent types"); - - if (pObj) - { - switch (Info.DstRes.Type) - { - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - CacheUniformBuffer(pObj, Info, ResourceCache.GetDynamicBuffersCounter()); - break; - - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - CacheStorageBuffer(pObj, Info, ResourceCache.GetDynamicBuffersCounter()); - break; - - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - CacheTexelBuffer(pObj, Info, ResourceCache.GetDynamicBuffersCounter()); - break; - - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - CacheImage(pObj, Info, - [&](const ShaderVariableVkImpl& SeparateSampler, ISampler* pSampler) { - VERIFY(!SeparateSampler.IsImmutableSamplerAssigned(), "Separate sampler '", SeparateSampler.GetDesc().Name, "' is assigned an immutable sampler"); - VERIFY_EXPR(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); - DEV_CHECK_ERR(SeparateSampler.GetDesc().ArraySize == 1 || SeparateSampler.GetDesc().ArraySize == ResDesc.ArraySize, - "Array size (", SeparateSampler.GetDesc().ArraySize, - ") of separate sampler variable '", - SeparateSampler.GetDesc().Name, - "' must be one or the same as the array size (", ResDesc.ArraySize, - ") of separate image variable '", ResDesc.Name, "' it is assigned to"); - Uint32 SamplerArrInd = SeparateSampler.GetDesc().ArraySize == 1 ? 0 : ArrayIndex; - SeparateSampler.BindResource(pSampler, SamplerArrInd); - }); - break; - - case VK_DESCRIPTOR_TYPE_SAMPLER: - if (!IsImmutableSamplerAssigned()) - { - CacheSeparateSampler(pObj, Info); - } - else - { - // Immutable samplers are permanently bound into the set layout; later binding a sampler - // into an immutable sampler slot in a descriptor set is not allowed (13.2.1) - LOG_ERROR_MESSAGE("Attempting to assign a sampler to an immutable sampler '", ResDesc.Name, '\''); - } - break; - - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - CacheInputAttachment(pObj, Info); - break; - - case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: - CacheAccelerationStructure(pObj, Info); - break; - - default: UNEXPECTED("Unknown resource type ", static_cast(Info.DstRes.Type)); - } - } - else - { - if (Info.DstRes.pObject && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - LOG_ERROR_MESSAGE("Shader variable '", ResDesc.Name, "' is not dynamic but being unbound. This is an error and may cause unpredicted behavior. " - "Use another shader resource binding instance or label shader variable as dynamic if you need to bind another resource."); - } - - Info.DstRes.pObject.Release(); - } -} - -template -bool ShaderVariableVkImpl::UpdateCachedResource(UpdateInfo& Info, - RefCntAutoPtr&& pObject, - TPreUpdateObject PreUpdateObject) const -{ - // We cannot use ValidatedCast<> here as the resource retrieved from the - // resource mapping can be of wrong type - if (pObject) - { - if (Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && Info.DstRes.pObject != nullptr) - { - // Do not update resource if one is already bound unless it is dynamic. This may be - // dangerous as writing descriptors while they are used by the GPU is an undefined behavior - return false; - } - - PreUpdateObject(Info.DstRes.pObject.template RawPtr(), pObject.template RawPtr()); - Info.DstRes.pObject.Attach(pObject.Detach()); - return true; - } - else - { - return false; - } -} - -void ShaderVariableVkImpl::CacheUniformBuffer(IDeviceObject* pBuffer, - UpdateInfo& Info, - Uint16& DynamicBuffersCounter) const -{ - // clang-format off - VERIFY(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || - Info.DstRes.Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - "Uniform buffer resource is expected"); - // clang-format on - RefCntAutoPtr pBufferVk{pBuffer, IID_BufferVk}; -#ifdef DILIGENT_DEVELOPMENT - VerifyConstantBufferBinding(*this, Info.VarType, Info.ArrayIndex, pBuffer, pBufferVk.RawPtr(), Info.DstRes.pObject.RawPtr()); - - /*if (pBufferVk->GetDesc().uiSizeInBytes < BufferStaticSize) - { - // It is OK if robustBufferAccess feature is enabled, otherwise access outside of buffer range may lead to crash or undefined behavior. - LOG_WARNING_MESSAGE("Error binding uniform buffer '", pBufferVk->GetDesc().Name, "' to shader variable '", - GetDesc().Name, "': buffer size in the shader (", - BufferStaticSize, ") is incompatible with the actual buffer size (", pBufferVk->GetDesc().uiSizeInBytes, ")."); - }*/ -#endif - - auto UpdateDynamicBuffersCounter = [&DynamicBuffersCounter](const BufferVkImpl* pOldBuffer, const BufferVkImpl* pNewBuffer) { - if (pOldBuffer != nullptr && pOldBuffer->GetDesc().Usage == USAGE_DYNAMIC) - { - VERIFY(DynamicBuffersCounter > 0, "Dynamic buffers counter must be greater than zero when there is at least one dynamic buffer bound in the resource cache"); - --DynamicBuffersCounter; - } - if (pNewBuffer != nullptr && pNewBuffer->GetDesc().Usage == USAGE_DYNAMIC) - ++DynamicBuffersCounter; - }; - if (UpdateCachedResource(Info, std::move(pBufferVk), UpdateDynamicBuffersCounter)) - { - // VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER or VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC descriptor type require - // buffer to be created with VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT - - // Do not update descriptor for a dynamic uniform buffer. All dynamic resource - // descriptors are updated at once by CommitDynamicResources() when SRB is committed. - if (Info.vkDescrSet != VK_NULL_HANDLE && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorBufferInfo DescrBuffInfo = Info.DstRes.GetUniformBufferDescriptorWriteInfo(); - UpdateDescriptorHandle(Info, nullptr, &DescrBuffInfo, nullptr); - } - } -} - -void ShaderVariableVkImpl::CacheStorageBuffer(IDeviceObject* pBufferView, - UpdateInfo& Info, - Uint16& DynamicBuffersCounter) const -{ - // clang-format off - VERIFY(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || - Info.DstRes.Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, - "Storage buffer resource is expected"); - // clang-format on - - RefCntAutoPtr pBufferViewVk{pBufferView, IID_BufferViewVk}; -#ifdef DILIGENT_DEVELOPMENT - { - // HLSL buffer SRVs are mapped to storge buffers in GLSL - auto RequiredViewType = BUFFER_VIEW_UNORDERED_ACCESS; // AZ TODO - VerifyResourceViewBinding(*this, Info.VarType, Info.ArrayIndex, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, Info.DstRes.pObject.RawPtr()); - if (pBufferViewVk != nullptr) - { - const auto& ViewDesc = pBufferViewVk->GetDesc(); - const auto& BuffDesc = pBufferViewVk->GetBuffer()->GetDesc(); - if (BuffDesc.Mode != BUFFER_MODE_STRUCTURED && BuffDesc.Mode != BUFFER_MODE_RAW) - { - LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", - GetDesc().Name, "': structured buffer view is expected."); - } - - /*if (BufferStride == 0 && ViewDesc.ByteWidth < BufferStaticSize) - { - // It is OK if robustBufferAccess feature is enabled, otherwise access outside of buffer range may lead to crash or undefined behavior. - LOG_WARNING_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", - Name, "': buffer size in the shader (", - BufferStaticSize, ") is incompatible with the actual buffer view size (", ViewDesc.ByteWidth, ")."); - } - - if (BufferStride > 0 && (ViewDesc.ByteWidth < BufferStaticSize || (ViewDesc.ByteWidth - BufferStaticSize) % BufferStride != 0)) - { - // For buffers with dynamic arrays we know only static part size and array element stride. - // Element stride in the shader may be differ than in the code. Here we check that the buffer size is exactly the same as the array with N elements. - LOG_WARNING_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", - Name, "': static buffer size in the shader (", - BufferStaticSize, ") and array element stride (", BufferStride, ") are incompatible with the actual buffer view size (", ViewDesc.ByteWidth, "),", - " this may be the result of the array element size mismatch."); - }*/ - } - } -#endif - - auto UpdateDynamicBuffersCounter = [&DynamicBuffersCounter](const BufferViewVkImpl* pOldBufferView, const BufferViewVkImpl* pNewBufferView) { - if (pOldBufferView != nullptr && pOldBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) - { - VERIFY(DynamicBuffersCounter > 0, "Dynamic buffers counter must be greater than zero when there is at least one dynamic buffer bound in the resource cache"); - --DynamicBuffersCounter; - } - if (pNewBufferView != nullptr && pNewBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) - ++DynamicBuffersCounter; - }; - - if (UpdateCachedResource(Info, std::move(pBufferViewVk), UpdateDynamicBuffersCounter)) - { - // VK_DESCRIPTOR_TYPE_STORAGE_BUFFER or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC descriptor type - // require buffer to be created with VK_BUFFER_USAGE_STORAGE_BUFFER_BIT (13.2.4) - - // Do not update descriptor for a dynamic storage buffer. All dynamic resource - // descriptors are updated at once by CommitDynamicResources() when SRB is committed. - if (Info.vkDescrSet != VK_NULL_HANDLE && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorBufferInfo DescrBuffInfo = Info.DstRes.GetStorageBufferDescriptorWriteInfo(); - UpdateDescriptorHandle(Info, nullptr, &DescrBuffInfo, nullptr); - } - } -} - -void ShaderVariableVkImpl::CacheTexelBuffer(IDeviceObject* pBufferView, - UpdateInfo& Info, - Uint16& DynamicBuffersCounter) const -{ - // clang-format off - VERIFY(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || - Info.DstRes.Type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, - "Uniform or storage buffer resource is expected"); - // clang-format on - - RefCntAutoPtr pBufferViewVk{pBufferView, IID_BufferViewVk}; -#ifdef DILIGENT_DEVELOPMENT - { - // HLSL buffer SRVs are mapped to storge buffers in GLSL - auto RequiredViewType = BUFFER_VIEW_UNORDERED_ACCESS; // AZ TODO - VerifyResourceViewBinding(*this, Info.VarType, Info.ArrayIndex, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, Info.DstRes.pObject.RawPtr()); - if (pBufferViewVk != nullptr) - { - const auto& ViewDesc = pBufferViewVk->GetDesc(); - const auto& BuffDesc = pBufferViewVk->GetBuffer()->GetDesc(); - if (!((BuffDesc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType != VT_UNDEFINED) || BuffDesc.Mode == BUFFER_MODE_RAW)) - { - LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", - GetDesc().Name, "': formatted buffer view is expected."); - } - } - } -#endif - - auto UpdateDynamicBuffersCounter = [&DynamicBuffersCounter](const BufferViewVkImpl* pOldBufferView, const BufferViewVkImpl* pNewBufferView) { - if (pOldBufferView != nullptr && pOldBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) - { - VERIFY(DynamicBuffersCounter > 0, "Dynamic buffers counter must be greater than zero when there is at least one dynamic buffer bound in the resource cache"); - --DynamicBuffersCounter; - } - if (pNewBufferView != nullptr && pNewBufferView->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) - ++DynamicBuffersCounter; - }; - - if (UpdateCachedResource(Info, std::move(pBufferViewVk), UpdateDynamicBuffersCounter)) - { - // The following bits must have been set at buffer creation time: - // * VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER -> VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT - // * VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER -> VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT - - // Do not update descriptor for a dynamic texel buffer. All dynamic resource descriptors - // are updated at once by CommitDynamicResources() when SRB is committed. - if (Info.vkDescrSet != VK_NULL_HANDLE && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkBufferView BuffView = Info.DstRes.pObject.RawPtr()->GetVkBufferView(); - UpdateDescriptorHandle(Info, nullptr, nullptr, &BuffView); - } - } -} - -template -void ShaderVariableVkImpl::CacheImage(IDeviceObject* pTexView, - UpdateInfo& Info, - TCacheSampler CacheSampler) const -{ - // clang-format off - VERIFY(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || - Info.DstRes.Type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || - Info.DstRes.Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - "Storage image, separate image or sampled image resource is expected"); - // clang-format on - RefCntAutoPtr pTexViewVk0{pTexView, IID_TextureViewVk}; -#ifdef DILIGENT_DEVELOPMENT - { - // HLSL buffer SRVs are mapped to storge buffers in GLSL - auto RequiredViewType = TEXTURE_VIEW_UNORDERED_ACCESS; // AZ TODO: read only - VerifyResourceViewBinding(*this, Info.VarType, Info.ArrayIndex, pTexView, pTexViewVk0.RawPtr(), {RequiredViewType}, Info.DstRes.pObject.RawPtr()); - } -#endif - if (UpdateCachedResource(Info, std::move(pTexViewVk0), [](const TextureViewVkImpl*, const TextureViewVkImpl*) {})) - { - // We can do RawPtr here safely since UpdateCachedResource() returned true - auto* pTexViewVk = Info.DstRes.pObject.RawPtr(); -#ifdef DILIGENT_DEVELOPMENT - if (Info.DstRes.Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER && !IsImmutableSamplerAssigned()) - { - if (pTexViewVk->GetSampler() == nullptr) - { - LOG_ERROR_MESSAGE("Error binding texture view '", pTexViewVk->GetDesc().Name, "' to variable '", GetPrintName(Info.ArrayIndex), - "'. No sampler is assigned to the view"); - } - } -#endif - - // Do not update descriptor for a dynamic image. All dynamic resource descriptors - // are updated at once by CommitDynamicResources() when SRB is committed. - if (Info.vkDescrSet != VK_NULL_HANDLE && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorImageInfo DescrImgInfo = Info.DstRes.GetImageDescriptorWriteInfo(IsImmutableSamplerAssigned()); - UpdateDescriptorHandle(Info, &DescrImgInfo, nullptr, nullptr); - } - if (Info.SamplerInd != InvalidSamplerInd) - { - VERIFY(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - "Only separate images can be assigned separate samplers when using HLSL-style combined samplers."); - VERIFY(!IsImmutableSamplerAssigned(), "Separate image can't be assigned an immutable sampler."); - const auto& SamplerAttribs = *m_ParentManager.GetVariable(Info.SamplerInd); - VERIFY_EXPR(SamplerAttribs.GetDesc().ResourceType == SHADER_RESOURCE_TYPE_SAMPLER); - if (!SamplerAttribs.IsImmutableSamplerAssigned()) - { - auto* pSampler = pTexViewVk->GetSampler(); - if (pSampler != nullptr) - { - CacheSampler(SamplerAttribs, pSampler); - } - else - { - LOG_ERROR_MESSAGE("Failed to bind sampler to sampler variable '", SamplerAttribs.GetDesc().Name, - "' assigned to separate image '", GetPrintName(Info.ArrayIndex), - "': no sampler is set in texture view '", pTexViewVk->GetDesc().Name, '\''); - } - } - } - } -} - -void ShaderVariableVkImpl::CacheSeparateSampler(IDeviceObject* pSampler, - UpdateInfo& Info) const -{ - VERIFY(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_SAMPLER, "Separate sampler resource is expected"); - VERIFY(!IsImmutableSamplerAssigned(), "This separate sampler is assigned an immutable sampler"); - - RefCntAutoPtr pSamplerVk{pSampler, IID_Sampler}; -#ifdef DILIGENT_DEVELOPMENT - if (pSampler != nullptr && pSamplerVk == nullptr) - { - LOG_ERROR_MESSAGE("Failed to bind object '", pSampler->GetDesc().Name, "' to variable '", GetPrintName(Info.ArrayIndex), - "'. Unexpected object type: sampler is expected"); - } - if (Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && Info.DstRes.pObject != nullptr && Info.DstRes.pObject != pSamplerVk) - { - auto VarTypeStr = GetShaderVariableTypeLiteralName(Info.VarType); - LOG_ERROR_MESSAGE("Non-null sampler is already bound to ", VarTypeStr, " shader variable '", GetPrintName(Info.ArrayIndex), - "'. Attempting to bind another sampler or null is an error and may " - "cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."); - } -#endif - if (UpdateCachedResource(Info, std::move(pSamplerVk), [](const SamplerVkImpl*, const SamplerVkImpl*) {})) - { - // Do not update descriptor for a dynamic sampler. All dynamic resource descriptors - // are updated at once by CommitDynamicResources() when SRB is committed. - if (Info.vkDescrSet != VK_NULL_HANDLE && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorImageInfo DescrImgInfo = Info.DstRes.GetSamplerDescriptorWriteInfo(); - UpdateDescriptorHandle(Info, &DescrImgInfo, nullptr, nullptr); - } - } -} - -void ShaderVariableVkImpl::CacheInputAttachment(IDeviceObject* pTexView, - UpdateInfo& Info) const -{ - VERIFY(Info.DstRes.Type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, "Input attachment resource is expected"); - RefCntAutoPtr pTexViewVk0{pTexView, IID_TextureViewVk}; -#ifdef DILIGENT_DEVELOPMENT - VerifyResourceViewBinding(*this, Info.VarType, Info.ArrayIndex, pTexView, pTexViewVk0.RawPtr(), {TEXTURE_VIEW_SHADER_RESOURCE}, Info.DstRes.pObject.RawPtr()); -#endif - if (UpdateCachedResource(Info, std::move(pTexViewVk0), [](const TextureViewVkImpl*, const TextureViewVkImpl*) {})) - { - // Do not update descriptor for a dynamic image. All dynamic resource descriptors - // are updated at once by CommitDynamicResources() when SRB is committed. - if (Info.vkDescrSet != VK_NULL_HANDLE && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorImageInfo DescrImgInfo = Info.DstRes.GetInputAttachmentDescriptorWriteInfo(); - UpdateDescriptorHandle(Info, &DescrImgInfo, nullptr, nullptr); - } - // - } -} - -void ShaderVariableVkImpl::CacheAccelerationStructure(IDeviceObject* pTLAS, - UpdateInfo& Info) const -{ - //VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure, "Acceleration Structure resource is expected"); - RefCntAutoPtr pTLASVk{pTLAS, IID_TopLevelASVk}; -#ifdef DILIGENT_DEVELOPMENT - VerifyTLASResourceBinding(*this, Info.VarType, Info.ArrayIndex, pTLASVk.RawPtr(), Info.DstRes.pObject.RawPtr()); -#endif - if (UpdateCachedResource(Info, std::move(pTLASVk), [](const TopLevelASVkImpl*, const TopLevelASVkImpl*) {})) - { - // Do not update descriptor for a dynamic TLAS. All dynamic resource descriptors - // are updated at once by CommitDynamicResources() when SRB is committed. - if (Info.vkDescrSet != VK_NULL_HANDLE && Info.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkWriteDescriptorSetAccelerationStructureKHR DescrASInfo = Info.DstRes.GetAccelerationStructureWriteInfo(); - UpdateDescriptorHandle(Info, nullptr, nullptr, nullptr, &DescrASInfo); - } - // - } -} - -bool ShaderVariableVkImpl::IsImmutableSamplerAssigned() const -{ - return true; // AZ TODO -} - -void ShaderVariableVkImpl::UpdateDescriptorHandle(UpdateInfo& Info, - const VkDescriptorImageInfo* pImageInfo, - const VkDescriptorBufferInfo* pBufferInfo, - const VkBufferView* pTexelBufferView, - const VkWriteDescriptorSetAccelerationStructureKHR* pAccelStructInfo) const -{ - VERIFY_EXPR(Info.vkDescrSet != VK_NULL_HANDLE); - - VkWriteDescriptorSet WriteDescrSet; - WriteDescrSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - WriteDescrSet.pNext = pAccelStructInfo; - WriteDescrSet.dstSet = Info.vkDescrSet; - WriteDescrSet.dstBinding = GetBinding().Binding; - WriteDescrSet.dstArrayElement = Info.ArrayIndex; - WriteDescrSet.descriptorCount = 1; - // descriptorType must be the same type as that specified in VkDescriptorSetLayoutBinding for dstSet at dstBinding. - // The type of the descriptor also controls which array the descriptors are taken from. (13.2.4) - WriteDescrSet.descriptorType = PipelineResourceSignatureVkImpl::GetVkDescriptorType(GetDesc()); - WriteDescrSet.pImageInfo = pImageInfo; - WriteDescrSet.pBufferInfo = pBufferInfo; - WriteDescrSet.pTexelBufferView = pTexelBufferView; - - m_ParentManager.m_pSignature->GetDevice()->GetLogicalDevice().UpdateDescriptorSets(1, &WriteDescrSet, 0, nullptr); -} - -String ShaderVariableVkImpl::GetPrintName(Uint32 ArrayInd) const -{ - auto& ResDesc = GetDesc(); - VERIFY_EXPR(ArrayInd < ResDesc.ArraySize); - - if (ResDesc.ArraySize > 1) - { - std::stringstream ss; - ss << ResDesc.Name << '[' << ArrayInd << ']'; - return ss.str(); - } - else - return ResDesc.Name; -} - -RESOURCE_DIMENSION ShaderVariableVkImpl::GetResourceDimension() const -{ - return GetDesc().ResourceDim; -} - -bool ShaderVariableVkImpl::IsMultisample() const -{ - return false; // AZ TODO + pSign->BindResource(pObj, ArrayIndex, m_ResIndex, ResourceCache); } } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp index 264d49a5..c2ac34a3 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp @@ -32,6 +32,7 @@ #include "VulkanTypeConversions.hpp" #include "PlatformMisc.hpp" #include "Align.hpp" +#include "BasicMath.hpp" namespace Diligent { @@ -1194,9 +1195,8 @@ VkPipelineStageFlags ResourceStateFlagsToVkPipelineStageFlags(RESOURCE_STATE Sta VkPipelineStageFlags vkPipelineStages = 0; while (StateFlags != RESOURCE_STATE_UNKNOWN) { - auto StateBit = static_cast(1 << PlatformMisc::GetLSB(Uint32{StateFlags})); + auto StateBit = ExtractBit(StateFlags); vkPipelineStages |= ResourceStateFlagToVkPipelineStage(StateBit, vkShaderStages); - StateFlags &= ~StateBit; } return vkPipelineStages; } @@ -1299,7 +1299,7 @@ VkAccessFlags AccelStructStateFlagsToVkAccessFlags(RESOURCE_STATE StateFlags) Uint32 Bits = StateFlags; while (Bits != 0) { - auto Bit = static_cast(1 << PlatformMisc::GetLSB(Bits)); + auto Bit = ExtractBit(Bits); switch (Bit) { // clang-format off @@ -1309,7 +1309,6 @@ VkAccessFlags AccelStructStateFlagsToVkAccessFlags(RESOURCE_STATE StateFlags) default: UNEXPECTED("Unexpected resource state flag"); // clang-format on } - Bits &= ~Bit; } return AccessFlags; } @@ -1637,9 +1636,8 @@ VkShaderStageFlags ShaderTypesToVkShaderStageFlags(SHADER_TYPE ShaderTypes) VkShaderStageFlags Result = 0; while (ShaderTypes != SHADER_TYPE_UNKNOWN) { - auto Type = static_cast(1u << PlatformMisc::GetLSB(Uint32{ShaderTypes})); + auto Type = ExtractBit(ShaderTypes); Result |= ShaderTypeToVkShaderStageFlagBit(Type); - ShaderTypes = ShaderTypes & ~Type; } return Result; } @@ -1652,7 +1650,7 @@ VkBuildAccelerationStructureFlagsKHR BuildASFlagsToVkBuildAccelerationStructureF VkBuildAccelerationStructureFlagsKHR Result = 0; while (Flags != RAYTRACING_BUILD_AS_NONE) { - auto FlagBit = static_cast(1u << PlatformMisc::GetLSB(Uint32{Flags})); + auto FlagBit = ExtractBit(Flags); switch (FlagBit) { // clang-format off @@ -1664,7 +1662,6 @@ VkBuildAccelerationStructureFlagsKHR BuildASFlagsToVkBuildAccelerationStructureF // clang-format on default: UNEXPECTED("unknown build AS flag"); } - Flags = Flags & ~FlagBit; } return Result; } @@ -1677,16 +1674,15 @@ VkGeometryFlagsKHR GeometryFlagsToVkGeometryFlags(RAYTRACING_GEOMETRY_FLAGS Flag VkGeometryFlagsKHR Result = 0; while (Flags != RAYTRACING_GEOMETRY_FLAG_NONE) { - auto FlagBit = static_cast(1 << PlatformMisc::GetLSB(Uint32{Flags})); + auto FlagBit = ExtractBit(Flags); switch (FlagBit) { // clang-format off - case RAYTRACING_GEOMETRY_FLAG_OPAQUE: Result |= VK_GEOMETRY_OPAQUE_BIT_KHR; break; + case RAYTRACING_GEOMETRY_FLAG_OPAQUE: Result |= VK_GEOMETRY_OPAQUE_BIT_KHR; break; case RAYTRACING_GEOMETRY_FLAG_NO_DUPLICATE_ANY_HIT_INVOCATION: Result |= VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR; break; // clang-format on default: UNEXPECTED("unknown geometry flag"); } - Flags = Flags & ~FlagBit; } return Result; } @@ -1699,18 +1695,17 @@ VkGeometryInstanceFlagsKHR InstanceFlagsToVkGeometryInstanceFlags(RAYTRACING_INS VkGeometryInstanceFlagsKHR Result = 0; while (Flags != RAYTRACING_INSTANCE_NONE) { - auto FlagBit = static_cast(1 << PlatformMisc::GetLSB(Uint32{Flags})); + auto FlagBit = ExtractBit(Flags); switch (FlagBit) { // clang-format off - case RAYTRACING_INSTANCE_TRIANGLE_FACING_CULL_DISABLE: Result |= VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; break; + case RAYTRACING_INSTANCE_TRIANGLE_FACING_CULL_DISABLE: Result |= VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; break; case RAYTRACING_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE: Result |= VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR; break; - case RAYTRACING_INSTANCE_FORCE_OPAQUE: Result |= VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR; break; - case RAYTRACING_INSTANCE_FORCE_NO_OPAQUE: Result |= VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR; break; + case RAYTRACING_INSTANCE_FORCE_OPAQUE: Result |= VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR; break; + case RAYTRACING_INSTANCE_FORCE_NO_OPAQUE: Result |= VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR; break; // clang-format on default: UNEXPECTED("unknown instance flag"); } - Flags = Flags & ~FlagBit; } return Result; } -- cgit v1.2.3