From 8a3b697948704337d08af7bea93627f5e647899e Mon Sep 17 00:00:00 2001 From: azhirnov Date: Sat, 16 Jan 2021 02:56:25 +0300 Subject: added pipeline resource signature (wip) --- Graphics/GraphicsEngineVulkan/CMakeLists.txt | 10 +- .../include/DeviceContextVkImpl.hpp | 13 +- .../include/PipelineLayout.hpp | 271 ---- .../include/PipelineLayoutCacheVk.hpp | 82 + .../include/PipelineLayoutVk.hpp | 174 +++ .../include/PipelineResourceSignatureVkImpl.hpp | 178 +++ .../include/PipelineStateVkImpl.hpp | 105 +- .../include/RenderDeviceVkImpl.hpp | 12 + .../include/ShaderResourceBindingVkImpl.hpp | 24 +- .../include/ShaderResourceCacheVk.hpp | 65 +- .../include/ShaderResourceLayoutVk.hpp | 478 ------ .../include/ShaderVariableVk.hpp | 142 +- .../GraphicsEngineVulkan/include/ShaderVkImpl.hpp | 5 +- .../include/VulkanTypeConversions.hpp | 1 + .../src/DeviceContextVkImpl.cpp | 146 +- .../GraphicsEngineVulkan/src/PipelineLayout.cpp | 505 ------- .../src/PipelineLayoutCacheVk.cpp | 80 + .../GraphicsEngineVulkan/src/PipelineLayoutVk.cpp | 170 +++ .../src/PipelineResourceSignatureVkImpl.cpp | 465 ++++++ .../src/PipelineStateVkImpl.cpp | 474 ++---- .../src/RenderDeviceVkImpl.cpp | 26 +- .../src/ShaderResourceBindingVkImpl.cpp | 101 +- .../src/ShaderResourceCacheVk.cpp | 92 +- .../src/ShaderResourceLayoutVk.cpp | 1598 -------------------- .../GraphicsEngineVulkan/src/ShaderVariableVk.cpp | 640 +++++++- .../src/VulkanTypeConversions.cpp | 14 +- 26 files changed, 2364 insertions(+), 3507 deletions(-) delete mode 100644 Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp create mode 100644 Graphics/GraphicsEngineVulkan/include/PipelineLayoutCacheVk.hpp create mode 100644 Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp create mode 100644 Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp delete mode 100644 Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp delete mode 100644 Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp create mode 100644 Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp create mode 100644 Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp create mode 100644 Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp delete mode 100644 Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp (limited to 'Graphics/GraphicsEngineVulkan') diff --git a/Graphics/GraphicsEngineVulkan/CMakeLists.txt b/Graphics/GraphicsEngineVulkan/CMakeLists.txt index e5cd319c..40a0dd49 100644 --- a/Graphics/GraphicsEngineVulkan/CMakeLists.txt +++ b/Graphics/GraphicsEngineVulkan/CMakeLists.txt @@ -16,7 +16,8 @@ set(INCLUDE include/FramebufferCache.hpp include/GenerateMipsVkHelper.hpp include/pch.h - include/PipelineLayout.hpp + include/PipelineLayoutVk.hpp + include/PipelineLayoutCacheVk.hpp include/PipelineStateVkImpl.hpp include/QueryManagerVk.hpp include/QueryVkImpl.hpp @@ -28,7 +29,6 @@ set(INCLUDE include/ManagedVulkanObject.hpp include/ShaderResourceBindingVkImpl.hpp include/ShaderResourceCacheVk.hpp - include/ShaderResourceLayoutVk.hpp include/ShaderVariableVk.hpp include/SwapChainVkImpl.hpp include/TextureVkImpl.hpp @@ -39,6 +39,7 @@ set(INCLUDE include/BottomLevelASVkImpl.hpp include/TopLevelASVkImpl.hpp include/ShaderBindingTableVkImpl.hpp + include/PipelineResourceSignatureVkImpl.hpp ) set(VULKAN_UTILS_INCLUDE @@ -92,7 +93,8 @@ set(SRC src/VulkanDynamicHeap.cpp src/FramebufferCache.cpp src/GenerateMipsVkHelper.cpp - src/PipelineLayout.cpp + src/PipelineLayoutCacheVk.cpp + src/PipelineLayoutVk.cpp src/PipelineStateVkImpl.cpp src/QueryManagerVk.cpp src/QueryVkImpl.cpp @@ -103,7 +105,6 @@ set(SRC src/ShaderVkImpl.cpp src/ShaderResourceBindingVkImpl.cpp src/ShaderResourceCacheVk.cpp - src/ShaderResourceLayoutVk.cpp src/ShaderVariableVk.cpp src/SwapChainVkImpl.cpp src/TextureVkImpl.cpp @@ -113,6 +114,7 @@ set(SRC src/BottomLevelASVkImpl.cpp src/TopLevelASVkImpl.cpp src/ShaderBindingTableVkImpl.cpp + src/PipelineResourceSignatureVkImpl.cpp ) set(VULKAN_UTILS_SRC diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index d9a2c2f2..d91feae3 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -39,7 +39,7 @@ #include "VulkanDynamicHeap.hpp" #include "ResourceReleaseQueue.hpp" #include "DescriptorPoolManager.hpp" -#include "PipelineLayout.hpp" +#include "PipelineLayoutVk.hpp" #include "GenerateMipsVkHelper.hpp" #include "BufferVkImpl.hpp" #include "TextureVkImpl.hpp" @@ -53,6 +53,7 @@ #include "BottomLevelASVkImpl.hpp" #include "TopLevelASVkImpl.hpp" #include "ShaderBindingTableVkImpl.hpp" +#include "ShaderResourceBindingVkImpl.hpp" namespace Diligent @@ -396,6 +397,7 @@ private: void CommitVkVertexBuffers(); void CommitViewports(); void CommitScissorRects(); + void BindShaderResources(PipelineStateVkImpl* pPipelineStateVk); __forceinline void TransitionOrVerifyBufferState(BufferVkImpl& Buffer, RESOURCE_STATE_TRANSITION_MODE TransitionMode, @@ -476,6 +478,14 @@ private: } m_State; + /// AZ TODO: comment + using DescSetBindingInfoArray = std::array; + DescSetBindingInfoArray m_DescrSetBindInfo; + + /// AZ TODO: comment + using ShaderResourcePerPipelineType = std::array, MAX_RESOURCE_SIGNATURES>, 3>; + ShaderResourcePerPipelineType m_ShaderResources; + /// Render pass that matches currently bound render targets. /// This render pass may or may not be currently set in the command buffer VkRenderPass m_vkRenderPass = VK_NULL_HANDLE; @@ -531,7 +541,6 @@ private: VulkanDynamicHeap m_DynamicHeap; DynamicDescriptorSetAllocator m_DynamicDescrSetAllocator; - PipelineLayout::DescriptorSetBindInfo m_DescrSetBindInfo; std::shared_ptr m_GenerateMipsHelper; RefCntAutoPtr m_GenerateMipsSRB; diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp deleted file mode 100644 index 4cb99d61..00000000 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp +++ /dev/null @@ -1,271 +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::PipelineLayout class -#include - -#include "ShaderBase.hpp" -#include "ShaderResourceLayoutVk.hpp" -#include "VulkanUtilities/VulkanObjectWrappers.hpp" -#include "VulkanUtilities/VulkanLogicalDevice.hpp" -#include "VulkanUtilities/VulkanCommandBuffer.hpp" - -namespace Diligent -{ - -class RenderDeviceVkImpl; -class DeviceContextVkImpl; -class ShaderResourceCacheVk; - -/// Implementation of the Diligent::PipelineLayout class -class PipelineLayout -{ -public: - static VkDescriptorType GetVkDescriptorType(SPIRVShaderResourceAttribs::ResourceType Type); - - PipelineLayout(); - void Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask); - void Finalize(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice); - - VkPipelineLayout GetVkPipelineLayout() const { return m_LayoutMgr.GetVkPipelineLayout(); } - - std::array GetDescriptorSetSizes(Uint32& NumSets) const; - - void InitResourceCache(RenderDeviceVkImpl* pDeviceVkImpl, - ShaderResourceCacheVk& ResourceCache, - IMemoryAllocator& CacheMemAllocator, - const char* DbgPipelineName) const; - - void AllocateResourceSlot(const SPIRVShaderResourceAttribs& ResAttribs, - SHADER_RESOURCE_VARIABLE_TYPE VariableType, - VkSampler vkImmutableSampler, - SHADER_TYPE ShaderType, - Uint32& DescriptorSet, - Uint32& Binding, - Uint32& OffsetInCache); - - Uint32 GetTotalDescriptors(SHADER_RESOURCE_VARIABLE_TYPE VarType) const - { - VERIFY_EXPR(VarType >= 0 && VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES); - return m_LayoutMgr.GetDescriptorSet(VarType).TotalDescriptors; - } - - bool IsSameAs(const PipelineLayout& RS) const - { - return m_LayoutMgr == RS.m_LayoutMgr; - } - size_t GetHash() const - { - return m_LayoutMgr.GetHash(); - } - - VkDescriptorSetLayout GetDynamicDescriptorSetVkLayout() const - { - return m_LayoutMgr.GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC).VkLayout; - } - - struct DescriptorSetBindInfo - { - std::vector vkSets; - std::vector DynamicOffsets; - const ShaderResourceCacheVk* pResourceCache = nullptr; - VkPipelineBindPoint BindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM; - Uint32 SetCout = 0; - Uint32 DynamicOffsetCount = 0; - bool DynamicBuffersPresent = false; - bool DynamicDescriptorsBound = false; -#ifdef DILIGENT_DEBUG - const PipelineLayout* pDbgPipelineLayout = nullptr; -#endif - DescriptorSetBindInfo() : - // clang-format off - vkSets (2), - DynamicOffsets(64) - // clang-format on - { - } - - void Reset() - { - pResourceCache = nullptr; - BindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM; - SetCout = 0; - DynamicOffsetCount = 0; - DynamicBuffersPresent = false; - DynamicDescriptorsBound = false; - -#ifdef DILIGENT_DEBUG - // In release mode, do not clear vectors as this causes unnecessary work - vkSets.clear(); - DynamicOffsets.clear(); - - pDbgPipelineLayout = nullptr; -#endif - } - }; - - // Prepares Vulkan descriptor sets for binding. Actual binding - // may not be possible until draw command time because dynamic offsets are - // set by the same Vulkan command. If there are no dynamic descriptors, this - // function also binds descriptor sets rightaway. - void PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, - VkPipelineBindPoint BindPoint, - const ShaderResourceCacheVk& ResourceCache, - DescriptorSetBindInfo& BindInfo, - VkDescriptorSet VkDynamicDescrSet) const; - - // Computes dynamic offsets and binds descriptor sets - __forceinline void BindDescriptorSetsWithDynamicOffsets(VulkanUtilities::VulkanCommandBuffer& CmdBuffer, - Uint32 CtxId, - DeviceContextVkImpl* pCtxVkImpl, - DescriptorSetBindInfo& BindInfo) const; - -private: - class DescriptorSetLayoutManager - { - public: - struct DescriptorSetLayout - { - DescriptorSetLayout() noexcept {} - // clang-format off - DescriptorSetLayout (DescriptorSetLayout&&) = default; - DescriptorSetLayout (const DescriptorSetLayout&) = delete; - DescriptorSetLayout& operator = (const DescriptorSetLayout&) = delete; - DescriptorSetLayout& operator = (DescriptorSetLayout&&) = delete; - // clang-format on - - uint32_t TotalDescriptors = 0; - int8_t SetIndex = -1; - uint8_t NumDynamicDescriptors = 0; // Total number of uniform and storage buffers, counting all array elements - uint16_t NumLayoutBindings = 0; - VkDescriptorSetLayoutBinding* pBindings = nullptr; - VulkanUtilities::DescriptorSetLayoutWrapper VkLayout; - - ~DescriptorSetLayout(); - void AddBinding(const VkDescriptorSetLayoutBinding& Binding, IMemoryAllocator& MemAllocator); - void Finalize(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice, IMemoryAllocator& MemAllocator, VkDescriptorSetLayoutBinding* pNewBindings); - void Release(RenderDeviceVkImpl* pRenderDeviceVk, IMemoryAllocator& MemAllocator, Uint64 CommandQueueMask); - - bool operator==(const DescriptorSetLayout& rhs) const; - bool operator!=(const DescriptorSetLayout& rhs) const { return !(*this == rhs); } - size_t GetHash() const; - - private: - void ReserveMemory(Uint32 NumBindings, IMemoryAllocator& MemAllocator); - static size_t GetMemorySize(Uint32 NumBindings); - }; - - DescriptorSetLayoutManager(IMemoryAllocator& MemAllocator); - ~DescriptorSetLayoutManager(); - - // clang-format off - DescriptorSetLayoutManager (const DescriptorSetLayoutManager&) = delete; - DescriptorSetLayoutManager& operator= (const DescriptorSetLayoutManager&) = delete; - DescriptorSetLayoutManager (DescriptorSetLayoutManager&&) = delete; - DescriptorSetLayoutManager& operator= (DescriptorSetLayoutManager&&) = delete; - // clang-format on - - void Finalize(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice); - void Release(RenderDeviceVkImpl* pRenderDeviceVk, Uint64 CommandQueueMask); - - DescriptorSetLayout& GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE VarType) { return m_DescriptorSetLayouts[VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC ? 1 : 0]; } - const DescriptorSetLayout& GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE VarType) const { return m_DescriptorSetLayouts[VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC ? 1 : 0]; } - - bool operator==(const DescriptorSetLayoutManager& rhs) const; - bool operator!=(const DescriptorSetLayoutManager& rhs) const { return !(*this == rhs); } - size_t GetHash() const; - VkPipelineLayout GetVkPipelineLayout() const { return m_VkPipelineLayout; } - - void AllocateResourceSlot(const SPIRVShaderResourceAttribs& ResAttribs, - SHADER_RESOURCE_VARIABLE_TYPE VariableType, - VkSampler vkImmutableSampler, - SHADER_TYPE ShaderType, - Uint32& DescriptorSet, - Uint32& Binding, - Uint32& OffsetInCache); - - private: - IMemoryAllocator& m_MemAllocator; - VulkanUtilities::PipelineLayoutWrapper m_VkPipelineLayout; - std::array m_DescriptorSetLayouts; - std::vector> m_LayoutBindings; - uint8_t m_ActiveSets = 0; - }; - - IMemoryAllocator& m_MemAllocator; - DescriptorSetLayoutManager m_LayoutMgr; -}; - - -__forceinline void PipelineLayout::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/PipelineLayoutCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutCacheVk.hpp new file mode 100644 index 00000000..f34110f7 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutCacheVk.hpp @@ -0,0 +1,82 @@ +/* + * 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 new file mode 100644 index 00000000..fd27d0a3 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp @@ -0,0 +1,174 @@ +/* + * 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::PipelineLayoutVk class +#include + +#include "PipelineResourceSignatureVkImpl.hpp" +#include "VulkanUtilities/VulkanObjectWrappers.hpp" +#include "VulkanUtilities/VulkanLogicalDevice.hpp" +#include "VulkanUtilities/VulkanCommandBuffer.hpp" + +namespace Diligent +{ + +class DeviceContextVkImpl; +class ShaderResourceCacheVk; + +/// Implementation of the Diligent::PipelineLayoutVk class +class PipelineLayoutVk final : public ObjectBase +{ +public: + PipelineLayoutVk(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount); + ~PipelineLayoutVk(); + + using ObjectBase::Release; + + void Finalize(); + + 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 + { + 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; + } + + Uint32 GetDescrSetIndex(IPipelineResourceSignature* pPRS) const + { + Uint32 Index = pPRS->GetDesc().BindingIndex; + return Index < m_SignatureCount ? m_DescSetOffset[Index] : ~0u; + } + + Uint32 GetDynamicBufferOffset(IPipelineResourceSignature* pPRS) const + { + Uint32 Index = pPRS->GetDesc().BindingIndex; + return Index < m_SignatureCount ? m_DynBufOffset[Index] : 0; + } + + struct ResourceInfo + { + SHADER_RESOURCE_TYPE Type = SHADER_RESOURCE_TYPE_UNKNOWN; + Uint16 DescrSetIndex = 0; + Uint16 BindingIndex = 0; + }; + 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; + + // AZ TODO: pack bits + Uint16 m_DynamicOffsetCount = 0; + Uint8 m_SignatureCount = 0; + Uint8 m_DescrSetCount = 0; + + PIPELINE_TYPE m_PipelineType = PIPELINE_TYPE(0xFF); + + using SignatureArray = std::array, MAX_RESOURCE_SIGNATURES>; + using DescSetOffsetArray = std::array; + using DynBufOffsetArray = std::array; + + SignatureArray m_Signatures; + DescSetOffsetArray m_DescSetOffset = {}; + 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 new file mode 100644 index 00000000..6b01b4cd --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -0,0 +1,178 @@ +/* + * 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::PipelineResourceSignatureVkImpl class +#include + +#include "PipelineResourceSignatureBase.hpp" +#include "VulkanUtilities/VulkanObjectWrappers.hpp" +#include "VulkanUtilities/VulkanLogicalDevice.hpp" +#include "VulkanUtilities/VulkanCommandBuffer.hpp" +#include "SRBMemoryAllocator.hpp" + +namespace Diligent +{ +class RenderDeviceVkImpl; +class ShaderResourceCacheVk; +class ShaderVariableManagerVk; +class DeviceContextVkImpl; + +/// Implementation of the Diligent::PipelineResourceSignatureVkImpl class +class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBase +{ +public: + using TPipelineResourceSignatureBase = PipelineResourceSignatureBase; + + PipelineResourceSignatureVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pDevice, + const PipelineResourceSignatureDesc& Desc, + bool bIsDeviceInternal = false); + ~PipelineResourceSignatureVkImpl(); + + Uint32 GetDynamicBufferCount() const { return m_DynamicBufferCount; } + Uint8 GetNumShaderStages() const { return m_NumShaders; } + Uint32 GetTotalResourceCount() const { return m_Desc.NumResources; } + + static constexpr Uint8 InvalidSamplerInd = 0xFF; + + struct PackedBindingIndex + { + Uint16 Binding; + Uint8 DescSet : 1; // 0 - static, 1 - dynamic + Uint8 SamplerInd = InvalidSamplerInd; + }; + + const PackedBindingIndex& GetBinding(Uint32 ResIndex) const + { + VERIFY_EXPR(ResIndex < m_Desc.NumResources); + return m_pBindingIndices[ResIndex]; + } + + const PipelineResourceDesc& GetResource(Uint32 ResIndex) const + { + VERIFY_EXPR(ResIndex < m_Desc.NumResources); + return m_Desc.Resources[ResIndex]; + } + + VkDescriptorSetLayout GetStaticVkDescriptorSetLayout() const { return m_VkDescSetLayouts[0]; } + VkDescriptorSetLayout GetDynamicVkDescriptorSetLayout() const { return m_VkDescSetLayouts[1]; } + + virtual void DILIGENT_CALL_TYPE CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, + bool InitStaticResources) override final; + + virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) override final; + + virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) override final; + + virtual Uint32 DILIGENT_CALL_TYPE GetStaticVariableCount(SHADER_TYPE ShaderType) const override final; + + using VkDescSetArray = std::array; + + struct DescriptorSetBindInfo + { + 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; + + SRBMemoryAllocator& GetSRBMemoryAllocator() + { + return m_SRBMemAllocator; + } + + void InitResourceCache(ShaderResourceCacheVk& ResourceCache, + IMemoryAllocator& CacheMemAllocator, + const char* DbgPipelineName) const; + + // Initializes resource slots in the ResourceCache + void InitializeResourceMemoryInCache(ShaderResourceCacheVk& ResourceCache) const; + + void InitializeStaticSRBResources(ShaderResourceCacheVk& ResourceCache) const; + +private: + void Destruct(); + + VulkanUtilities::DescriptorSetLayoutWrapper m_VkDescSetLayouts[2]; + + PackedBindingIndex* m_pBindingIndices = nullptr; // [m_Desc.NumResources] + + SHADER_TYPE m_ShaderStages = SHADER_TYPE_UNKNOWN; + + Uint16 m_DynamicBufferCount = 0; + Uint8 m_NumShaders = 0; + + 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] + + SRBMemoryAllocator m_SRBMemAllocator; +}; + + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index 6a753048..46d0fc93 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -35,21 +35,20 @@ #include "RenderDeviceVk.h" #include "PipelineStateVk.h" #include "PipelineStateBase.hpp" -#include "PipelineLayout.hpp" -#include "ShaderResourceLayoutVk.hpp" #include "ShaderVariableVk.hpp" #include "FixedBlockMemoryAllocator.hpp" #include "SRBMemoryAllocator.hpp" #include "VulkanUtilities/VulkanObjectWrappers.hpp" #include "VulkanUtilities/VulkanCommandBuffer.hpp" -#include "PipelineLayout.hpp" #include "RenderDeviceVkImpl.hpp" +#include "PipelineLayoutVk.hpp" namespace Diligent { class FixedBlockMemoryAllocator; class ShaderVariableManagerVk; +class ShaderVkImpl; /// Pipeline state object implementation in Vulkan backend. class PipelineStateVkImpl final : public PipelineStateBase @@ -64,9 +63,6 @@ public: IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_PipelineStateVk, TPipelineStateBase) - /// Implementation of IPipelineState::CreateShaderResourceBinding() in Vulkan backend. - virtual void DILIGENT_CALL_TYPE CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, bool InitStaticResources) override final; - /// Implementation of IPipelineState::IsCompatibleWith() in Vulkan backend. virtual bool DILIGENT_CALL_TYPE IsCompatibleWith(const IPipelineState* pPSO) const override final; @@ -79,41 +75,21 @@ public: /// Implementation of IPipelineState::BindStaticResources() in Vulkan backend. virtual void DILIGENT_CALL_TYPE BindStaticResources(Uint32 ShaderFlags, IResourceMapping* pResourceMapping, Uint32 Flags) override final; - /// Implementation of IPipelineState::GetStaticVariableCount() in Vulkan backend. - virtual Uint32 DILIGENT_CALL_TYPE GetStaticVariableCount(SHADER_TYPE ShaderType) const override final; - - /// Implementation of IPipelineState::GetStaticVariableByName() in Vulkan backend. - virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) override final; + /// Implementation of IPipelineState::GetResourceSignatureCount() in Vulkan backend. + virtual Uint32 DILIGENT_CALL_TYPE GetResourceSignatureCount() const override final { return m_PipelineLayout->GetSignatureCount(); } - /// Implementation of IPipelineState::GetStaticVariableByIndex() in Vulkan backend. - virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) override final; + /// Implementation of IPipelineState::GetResourceSignature() in Vulkan backend. + virtual IPipelineResourceSignature* DILIGENT_CALL_TYPE GetResourceSignature(Uint32 Index) const override final { return m_PipelineLayout->GetSignature(Index); } - void CommitAndTransitionShaderResources(IShaderResourceBinding* pShaderResourceBinding, - DeviceContextVkImpl* pCtxVkImpl, - bool CommitResources, - RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, - PipelineLayout::DescriptorSetBindInfo* pDescrSetBindInfo) const; - - __forceinline void BindDescriptorSetsWithDynamicOffsets(VulkanUtilities::VulkanCommandBuffer& CmdBuffer, - Uint32 CtxId, - DeviceContextVkImpl* pCtxVkImpl, - PipelineLayout::DescriptorSetBindInfo& BindInfo) + __forceinline void BindDescriptorSetsWithDynamicOffsets(VulkanUtilities::VulkanCommandBuffer& CmdBuffer, + Uint32 CtxId, + DeviceContextVkImpl* pCtxVkImpl, + PipelineLayoutVk::DescriptorSetBindInfo& BindInfo) { - m_PipelineLayout.BindDescriptorSetsWithDynamicOffsets(CmdBuffer, CtxId, pCtxVkImpl, BindInfo); + m_PipelineLayout->BindDescriptorSetsWithDynamicOffsets(CmdBuffer, CtxId, pCtxVkImpl, BindInfo); } - const PipelineLayout& GetPipelineLayout() const { return m_PipelineLayout; } - - const ShaderResourceLayoutVk& GetShaderResLayout(Uint32 ShaderInd) const - { - VERIFY_EXPR(ShaderInd < GetNumShaderStages()); - return m_ShaderResourceLayouts[ShaderInd]; - } - - SRBMemoryAllocator& GetSRBMemoryAllocator() - { - return m_SRBMemAllocator; - } + const PipelineLayoutVk& GetPipelineLayout() const { return *m_PipelineLayout; } static RenderPassDesc GetImplicitRenderPassDesc(Uint32 NumRenderTargets, const TEXTURE_FORMAT RTVFormats[], @@ -126,50 +102,45 @@ public: void InitializeStaticSRBResources(ShaderResourceCacheVk& ResourceCache) const; -private: - using TShaderStages = ShaderResourceLayoutVk::TShaderStages; + bool IsIncompatibleWith(IPipelineResourceSignature* pPRS) const + { + return !m_PipelineLayout->HasSignature(pPRS); + } + + struct ShaderStageInfo + { + ShaderStageInfo() {} + ShaderStageInfo(const ShaderVkImpl* pShader); + + void Append(const ShaderVkImpl* pShader); + size_t Count() const; + // Shader stage type. All shaders in the stage must have the same type. + SHADER_TYPE Type = SHADER_TYPE_UNKNOWN; + + std::vector Shaders; + std::vector> SPIRVs; + }; + using TShaderStages = std::vector; + +private: template TShaderStages InitInternalObjects(const PSOCreateInfoType& CreateInfo, std::vector& vkShaderStages, std::vector& ShaderModules); + void InitPipelineLayout(const PipelineStateCreateInfo& CreateInfo, + TShaderStages& ShaderStages); + void InitResourceLayouts(const PipelineStateCreateInfo& CreateInfo, TShaderStages& ShaderStages); void Destruct(); - const ShaderResourceLayoutVk& GetStaticShaderResLayout(Uint32 ShaderInd) const - { - VERIFY_EXPR(ShaderInd < GetNumShaderStages()); - return m_ShaderResourceLayouts[GetNumShaderStages() + ShaderInd]; - } - - const ShaderResourceCacheVk& GetStaticResCache(Uint32 ShaderInd) const - { - VERIFY_EXPR(ShaderInd < GetNumShaderStages()); - return m_StaticResCaches[ShaderInd]; - } - - const ShaderVariableManagerVk& GetStaticVarMgr(Uint32 ShaderInd) const - { - VERIFY_EXPR(ShaderInd < GetNumShaderStages()); - return m_StaticVarsMgrs[ShaderInd]; - } - - ShaderResourceLayoutVk* m_ShaderResourceLayouts = nullptr; // [m_NumShaderStages * 2] - ShaderResourceCacheVk* m_StaticResCaches = nullptr; // [m_NumShaderStages] - ShaderVariableManagerVk* m_StaticVarsMgrs = nullptr; // [m_NumShaderStages] - - SRBMemoryAllocator m_SRBMemAllocator; + void* m_pRawMem = nullptr; // AZ TODO: remove VulkanUtilities::PipelineWrapper m_Pipeline; - PipelineLayout m_PipelineLayout; - - // Resource layout index in m_ShaderResourceLayouts array for every shader stage, - // indexed by the shader type pipeline index (returned by GetShaderTypePipelineIndex) - std::array m_ResourceLayoutIndex = {-1, -1, -1, -1, -1, -1}; - static_assert(MAX_SHADERS_IN_PIPELINE == 6, "Please update the initializer list above"); + RefCntAutoPtr m_PipelineLayout; bool m_HasStaticResources = false; bool m_HasNonStaticResources = false; diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp index 37052725..2547dbb0 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp @@ -47,6 +47,7 @@ #include "VulkanUploadHeap.hpp" #include "FramebufferCache.hpp" #include "RenderPassCache.hpp" +#include "PipelineLayoutCacheVk.hpp" #include "CommandPoolManager.hpp" #include "DXCompiler.hpp" @@ -130,6 +131,10 @@ public: virtual void DILIGENT_CALL_TYPE CreateSBT(const ShaderBindingTableDesc& Desc, IShaderBindingTable** ppSBT) override final; + /// Implementation of IRenderDevice::CreatePipelineResourceSignature() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature) override final; + /// Implementation of IRenderDeviceVk::GetVkDevice(). virtual VkDevice DILIGENT_CALL_TYPE GetVkDevice() override final { return m_LogicalVkDevice->GetVkDevice(); } @@ -190,6 +195,8 @@ 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); @@ -224,6 +231,8 @@ public: return m_Properties; } + void CreatePipelineLayout(IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount, PipelineLayoutVk** ppPipelineLayout); + private: template void CreatePipelineState(const PSOCreateInfoType& PSOCreateInfo, IPipelineState** ppPipelineState); @@ -244,6 +253,7 @@ private: EngineVkCreateInfo m_EngineAttribs; FramebufferCache m_FramebufferCache; + PipelineLayoutCacheVk m_PipelineLayoutCache; RenderPassCache m_ImplicitRenderPassCache; DescriptorSetAllocator m_DescriptorSetAllocator; DescriptorPoolManager m_DynamicDescriptorPool; @@ -260,6 +270,8 @@ private: std::unique_ptr m_pDxCompiler; Properties m_Properties; + + FixedBlockMemoryAllocator m_PipelineLayoutAllocator; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp index 36613bff..84ce03d9 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp @@ -36,22 +36,23 @@ #include "ShaderBase.hpp" #include "ShaderResourceCacheVk.hpp" #include "ShaderVariableVk.hpp" +#include "PipelineResourceSignatureVkImpl.hpp" namespace Diligent { -class PipelineStateVkImpl; +class PipelineResourceSignatureVkImpl; /// Implementation of the Diligent::IShaderResourceBindingVk interface // sizeof(ShaderResourceBindingVkImpl) == 72 (x64, msvc, Release) -class ShaderResourceBindingVkImpl final : public ShaderResourceBindingBase +class ShaderResourceBindingVkImpl final : public ShaderResourceBindingBase { public: - using TBase = ShaderResourceBindingBase; + using TBase = ShaderResourceBindingBase; - ShaderResourceBindingVkImpl(IReferenceCounters* pRefCounters, - PipelineStateVkImpl* pPSO, - bool IsPSOInternal); + ShaderResourceBindingVkImpl(IReferenceCounters* pRefCounters, + PipelineResourceSignatureVkImpl* pPRS, + bool IsPSOInternal); ~ShaderResourceBindingVkImpl(); IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_ShaderResourceBindingVk, TBase) @@ -71,6 +72,9 @@ public: /// Implementation of IShaderResourceBinding::InitializeStaticResources() in Vulkan backend. virtual void DILIGENT_CALL_TYPE InitializeStaticResources(const IPipelineState* pPipelineState) override final; + /// Implementation of IShaderResourceBinding::InitializeStaticResourcesWithSignature() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE InitializeStaticResourcesWithSignature(const IPipelineResourceSignature* pResourceSignature) override final; + ShaderResourceCacheVk& GetResourceCache() { return m_ShaderResourceCache; } bool StaticResourcesInitialized() const { return m_bStaticResourcesInitialized; } @@ -78,14 +82,12 @@ public: private: void Destruct(); + std::array m_ShaderVarIndex = {-1, -1, -1, -1, -1, -1}; + static_assert(MAX_SHADERS_IN_PIPELINE == 6, "Please update the initializer list above"); + ShaderResourceCacheVk m_ShaderResourceCache; ShaderVariableManagerVk* m_pShaderVarMgrs = nullptr; - // Resource layout index in m_ShaderResourceCache array for every shader stage, - // indexed by the shader type pipeline index (returned by GetShaderTypePipelineIndex) - std::array m_ResourceLayoutIndex = {-1, -1, -1, -1, -1, -1}; - static_assert(MAX_SHADERS_IN_PIPELINE == 6, "Please update the initializer list above"); - bool m_bStaticResourcesInitialized = false; Uint8 m_NumShaders = 0; }; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 82c10b51..77ce5d8b 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -90,16 +90,16 @@ public: ~ShaderResourceCacheVk(); - static size_t GetRequiredMemorySize(Uint32 NumSets, Uint32 SetSizes[]); + static size_t GetRequiredMemorySize(Uint32 NumSets, const Uint32* SetSizes); - void InitializeSets(IMemoryAllocator& MemAllocator, Uint32 NumSets, Uint32 SetSizes[]); - void InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, SPIRVShaderResourceAttribs::ResourceType Type); + void InitializeSets(IMemoryAllocator& MemAllocator, Uint32 NumSets, const Uint32* SetSizes); + void InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, VkDescriptorType Type); // sizeof(Resource) == 16 (x64, msvc, Release) struct Resource { // clang-format off - explicit Resource(SPIRVShaderResourceAttribs::ResourceType _Type) noexcept : + explicit Resource(VkDescriptorType _Type) noexcept : Type{_Type} {} @@ -108,9 +108,9 @@ public: Resource& operator = (const Resource&) = delete; Resource& operator = (Resource&&) = delete; -/* 0 */ const SPIRVShaderResourceAttribs::ResourceType Type; -/*1-7*/ // Unused -/* 8 */ RefCntAutoPtr pObject; +/* 0 */ const VkDescriptorType Type; +/*4-7*/ // Unused +/* 8 */ RefCntAutoPtr pObject; VkDescriptorBufferInfo GetUniformBufferDescriptorWriteInfo () const; VkDescriptorBufferInfo GetStorageBufferDescriptorWriteInfo () const; @@ -213,7 +213,9 @@ private: IMemoryAllocator* m_pAllocator = nullptr; void* m_pMemory = nullptr; Uint16 m_NumSets = 0; - // Total number of dynamic buffers bound in the resource cache regardless of the variable type + + // Total number of dynamic buffers bound in the resource cache regardless of the variable type. + // This variable is not equal to dynamic offsets count, it just a indicator that dynamic descriptor has dynamic buffers. Uint16 m_NumDynamicBuffers = 0; Uint32 m_TotalResources = 0; @@ -243,46 +245,27 @@ __forceinline Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 { const auto& DescrSet = GetDescriptorSet(set); Uint32 res = 0; - while (res < DescrSet.GetSize()) - { - const auto& Res = DescrSet.GetResource(res); - if (Res.Type != SPIRVShaderResourceAttribs::ResourceType::UniformBuffer) - break; - - const auto* pBufferVk = Res.pObject.RawPtr(); - auto Offset = pBufferVk != nullptr ? pBufferVk->GetDynamicOffset(CtxId, pCtxVkImpl) : 0; - Offsets[OffsetInd++] = Offset; - - ++res; - } + // AZ TODO: optimize while (res < DescrSet.GetSize()) { const auto& Res = DescrSet.GetResource(res); - if (Res.Type != SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer && - Res.Type != SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer) - break; - - 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; + if (Res.Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + { + 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) + { + 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; } - -#ifdef DILIGENT_DEBUG - for (; res < DescrSet.GetSize(); ++res) - { - const auto& Res = DescrSet.GetResource(res); - // clang-format off - VERIFY(Res.Type != SPIRVShaderResourceAttribs::ResourceType::UniformBuffer && - Res.Type != SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer && - Res.Type != SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer, - "All uniform and storage buffers are expected to go first in the beginning of each descriptor set"); - // clang-format on - } -#endif } return OffsetInd; } diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp deleted file mode 100644 index bf283e1f..00000000 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp +++ /dev/null @@ -1,478 +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::ShaderResourceLayoutVk class - -// All resources are stored in a single continuous chunk of memory using the following layout: -// -// m_ResourceBuffer -// | -// || VkResource[0] ... VkResource[s-1] | VkResource[s] ... VkResource[s+m-1] | VkResource[s+m] ... VkResource[s+m+d-1] || || -// || | | || || -// || VARIABLE_TYPE_STATIC | VARIABLE_TYPE_MUTABLE | VARIABLE_TYPE_DYNAMIC || Immutable Samplers || -// || | | || || -// -// s == m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_STATIC] -// m == m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE] -// d == m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC] -// -// -// Every ShaderVariableVkImpl variable managed by ShaderVariableManagerVk keeps a reference to corresponding VkResource. -// -// ___________________________ ___________________________________________________________________________ -// | | unique_ptr | | | | | -// | ShaderResourceLayoutVk |--------------->| VkResource[0] | VkResource[1] | ... | VkResource[s+m+d-1] | -// |___________________________| |___________________|_________________|_______________|_____________________| -// A A -// / | -// Ref Ref -// / | -// __________________________ __________________/___________________________|___________________________ -// | | | | | | -// | ShaderVariableManagerVk |---------------->| ShaderVariableVkImpl[0] | ShaderVariableVkImpl[1] | ... | -// |__________________________| |___________________________|____________________________|_________________| -// -// -// -// -// Resources in the resource cache are identified by the descriptor set index and the offset from the set start -// -// ___________________________ ___________________________________________________________________________ -// | | unique_ptr | | | | | -// | ShaderResourceLayoutVk |--------------->| VkResource[0] | VkResource[1] | ... | VkResource[s+m+d-1] | -// |___________________________| |___________________|_________________|_______________|_____________________| -// | | -// | | -// | (DescriptorSet, CacheOffset) / (DescriptorSet, CacheOffset) -// \ / -// __________________________ ________V_______________________________________________________V_______ -// | | | | -// | ShaderResourceCacheVk |---------------->| Resources | -// |__________________________| |________________________________________________________________________| -// -// -// -// Every pipeline state object (PipelineStateVkImpl) keeps the following layouts: -// * One layout object per shader stage to facilitate management of static shader resources -// - Uses artificial layout where resource binding matches the resource type (SPIRVShaderResourceAttribs::ResourceType) -// * One layout object per shader stage used by SRBs to manage all resource types: -// - All variable types are preserved -// - Bindings, descriptor sets and offsets are assigned during the initialization - -#include -#include -#include - -#include "PipelineState.h" -#include "ShaderBase.hpp" -#include "HashUtils.hpp" -#include "ShaderResourceCacheVk.hpp" -#include "VulkanUtilities/VulkanLogicalDevice.hpp" -#include "StringPool.hpp" - -namespace Diligent -{ - -class ShaderVkImpl; - -/// Diligent::ShaderResourceLayoutVk class -// sizeof(ShaderResourceLayoutVk)==40 (MS compiler, x64) -class ShaderResourceLayoutVk -{ -public: - struct ShaderStageInfo - { - ShaderStageInfo() {} - ShaderStageInfo(const ShaderVkImpl* pShader); - - void Append(const ShaderVkImpl* pShader); - size_t Count() const; - - // Shader stage type. All shaders in the stage must have the same type. - SHADER_TYPE Type = SHADER_TYPE_UNKNOWN; - - std::vector Shaders; - std::vector> SPIRVs; - }; - using TShaderStages = std::vector; - - ShaderResourceLayoutVk(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice) noexcept : - m_LogicalDevice{LogicalDevice} - { -#if defined(_MSC_VER) && defined(_WIN64) - static_assert(sizeof(*this) == 40, "Unexpected sizeof(ShaderResourceLayoutVk)."); -#endif - } - - // clang-format off - ShaderResourceLayoutVk (const ShaderResourceLayoutVk&) = delete; - ShaderResourceLayoutVk (ShaderResourceLayoutVk&&) = delete; - ShaderResourceLayoutVk& operator = (const ShaderResourceLayoutVk&) = delete; - ShaderResourceLayoutVk& operator = (ShaderResourceLayoutVk&&) = delete; - // clang-format on - - ~ShaderResourceLayoutVk(); - - // This method is called by PipelineStateVkImpl class instance to initialize static - // shader resource layout and the cache - void InitializeStaticResourceLayout(const std::vector& Shaders, - IMemoryAllocator& LayoutDataAllocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - ShaderResourceCacheVk& StaticResourceCache); - - // This method is called by PipelineStateVkImpl class instance to initialize resource - // layouts for all shader stages in the pipeline. - static void Initialize(IRenderDevice* pRenderDevice, - TShaderStages& ShaderStages, - ShaderResourceLayoutVk Layouts[], - IMemoryAllocator& LayoutDataAllocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - class PipelineLayout& PipelineLayout, - bool VerifyVariables, - bool VerifyImmutableSamplers); - - // sizeof(VkResource) == 32 (x64) - struct VkResource - { - // clang-format off - VkResource (const VkResource&) = delete; - VkResource ( VkResource&&) = delete; - VkResource& operator = (const VkResource&) = delete; - VkResource& operator = ( VkResource&&) = delete; - - static constexpr const Uint32 CacheOffsetBits = 21; - static constexpr const Uint32 SamplerIndBits = 8; - static constexpr const Uint32 VariableTypeBits = 2; - static constexpr const Uint32 ImmutableSamplerFlagBits = 1; - static_assert(CacheOffsetBits + SamplerIndBits + VariableTypeBits + ImmutableSamplerFlagBits == 32, "Elements are expected to be packed into 32 bits"); - static_assert(SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES <= (1<(_Binding) }, - DescriptorSet {static_cast(_DescriptorSet)}, - CacheOffset {_CacheOffset }, - SamplerInd {_SamplerInd }, - VariableType {_VariableType}, - ImmutableSamplerAssigned {_ImmutableSamplerAssigned ? 1U : 0U}, - ArraySize {_Attribs.ArraySize }, - Type {_Attribs.Type }, - ResourceDim {_Attribs.ResourceDim}, - IsMS {_Attribs.IsMS }, -#ifdef DILIGENT_DEVELOPMENT - BufferStaticSize {_Attribs.BufferStaticSize}, - BufferStride {_Attribs.BufferStride }, -#endif - Name {_Name }, - ParentResLayout {_ParentLayout} - // clang-format on - { -#if defined(_MSC_VER) && defined(_WIN64) && !defined(DILIGENT_DEVELOPMENT) - static_assert(sizeof(*this) == 32, "Unexpected sizeof(VkResource)"); -#endif - // clang-format off - VERIFY(_CacheOffset < (1 << CacheOffsetBits), "Cache offset (", _CacheOffset, ") exceeds max representable value ", (1 << CacheOffsetBits) ); - VERIFY(_SamplerInd < (1 << SamplerIndBits), "Sampler index (", _SamplerInd, ") exceeds max representable value ", (1 << SamplerIndBits) ); - VERIFY(_Binding <= std::numeric_limits::max(), "Binding (", _Binding, ") exceeds max representable value ", std::numeric_limits::max() ); - VERIFY(_DescriptorSet <= std::numeric_limits::max(), "Descriptor set (", _DescriptorSet, ") exceeds max representable value ", std::numeric_limits::max()); - VERIFY(_VariableType < (1 << VariableTypeBits), "Variable type (", Uint32{_VariableType}, ") exceeds max representable value ", (1 << VariableTypeBits) ); - VERIFY(_Attribs.ResourceDim < (1 << ResourceDimBits), "Resource dimension (", Uint32{_Attribs.ResourceDim}, ") exceeds max representable value ", (1 << ResourceDimBits) ); - // clang-format on - } - - - // Checks if a resource is bound in ResourceCache at the given ArrayIndex - bool IsBound(Uint32 ArrayIndex, const ShaderResourceCacheVk& ResourceCache) const; - - // Binds a resource pObject in the ResourceCache - void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex, ShaderResourceCacheVk& ResourceCache) const; - - // Updates resource descriptor in the descriptor set - inline void UpdateDescriptorHandle(VkDescriptorSet vkDescrSet, - uint32_t ArrayElement, - const VkDescriptorImageInfo* pImageInfo, - const VkDescriptorBufferInfo* pBufferInfo, - const VkBufferView* pTexelBufferView, - const VkWriteDescriptorSetAccelerationStructureKHR* pAccelStructInfo = nullptr) const; - - bool IsImmutableSamplerAssigned() const - { - VERIFY(ImmutableSamplerAssigned == 0 || - Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || - Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler, - "Immutable sampler can only be assigned to a sampled image or separate sampler"); - return ImmutableSamplerAssigned != 0; - } - - SHADER_RESOURCE_VARIABLE_TYPE GetVariableType() const - { - return static_cast(VariableType); - } - - String GetPrintName(Uint32 ArrayInd) const - { - VERIFY_EXPR(ArrayInd < ArraySize); - if (ArraySize > 1) - { - std::stringstream ss; - ss << Name << '[' << ArrayInd << ']'; - return ss.str(); - } - else - return Name; - } - - ShaderResourceDesc GetResourceDesc() const - { - return ShaderResourceDesc{Name, SPIRVShaderResourceAttribs::GetShaderResourceType(Type), ArraySize}; - } - - RESOURCE_DIMENSION GetResourceDimension() const - { - return static_cast(ResourceDim); - } - - bool IsMultisample() const - { - return IsMS != 0; - } - - bool IsCompatibleWith(const VkResource& rhs) const - { - // clang-format off - return Binding == rhs.Binding && - DescriptorSet == rhs.DescriptorSet && - ArraySize == rhs.ArraySize && - Type == rhs.Type; - // clang-format on - } - - private: - void CacheUniformBuffer(IDeviceObject* pBuffer, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - Uint16& DynamicBuffersCounter) const; - - void CacheStorageBuffer(IDeviceObject* pBufferView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - Uint16& DynamicBuffersCounter) const; - - void CacheTexelBuffer(IDeviceObject* pBufferView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - Uint16& DynamicBuffersCounter) const; - - template - void CacheImage(IDeviceObject* pTexView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - TCacheSampler CacheSampler) const; - - void CacheSeparateSampler(IDeviceObject* pSampler, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd) const; - - void CacheInputAttachment(IDeviceObject* pTexView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd) const; - - void CacheAccelerationStructure(IDeviceObject* pTLAS, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd) const; - - template - bool UpdateCachedResource(ShaderResourceCacheVk::Resource& DstRes, - RefCntAutoPtr&& pObject, - TPreUpdateObject PreUpdateObject) const; - }; - - // Copies static resources from SrcResourceCache defined by SrcLayout - // to DstResourceCache defined by this layout - void InitializeStaticResources(const ShaderResourceLayoutVk& SrcLayout, - const ShaderResourceCacheVk& SrcResourceCache, - ShaderResourceCacheVk& DstResourceCache) const; - -#ifdef DILIGENT_DEVELOPMENT - bool dvpVerifyBindings(const ShaderResourceCacheVk& ResourceCache) const; - static void dvpVerifyResourceLayoutDesc(const TShaderStages& ShaderStages, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - bool VerifyVariables, - bool VerifyImmutableSamplers); -#endif - - Uint32 GetResourceCount(SHADER_RESOURCE_VARIABLE_TYPE VarType) const - { - return m_NumResources[VarType]; - } - - // Initializes resource slots in the ResourceCache - void InitializeResourceMemoryInCache(ShaderResourceCacheVk& ResourceCache) const; - - // Writes dynamic resource descriptors from ResourceCache to vkDynamicDescriptorSet - void CommitDynamicResources(const ShaderResourceCacheVk& ResourceCache, - VkDescriptorSet vkDynamicDescriptorSet) const; - - const Char* GetShaderName() const - { - return GetStringPoolData(); - } - - SHADER_TYPE GetShaderType() const { return m_ShaderType; } - - const VkResource& GetResource(SHADER_RESOURCE_VARIABLE_TYPE VarType, Uint32 r) const - { - VERIFY_EXPR(r < m_NumResources[VarType]); - auto* Resources = reinterpret_cast(m_ResourceBuffer.get()); - return Resources[GetResourceOffset(VarType, r)]; - } - - bool IsUsingSeparateSamplers() const { return m_IsUsingSeparateSamplers; } - - bool IsCompatibleWith(const ShaderResourceLayoutVk& ResLayout) const; - -private: - Uint32 GetResourceOffset(SHADER_RESOURCE_VARIABLE_TYPE VarType, Uint32 r) const - { - VERIFY_EXPR(r < m_NumResources[VarType]); - static_assert(SHADER_RESOURCE_VARIABLE_TYPE_STATIC == 0, "SHADER_RESOURCE_VARIABLE_TYPE_STATIC == 0 expected"); - r += (VarType > SHADER_RESOURCE_VARIABLE_TYPE_STATIC) ? m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_STATIC] : 0; - static_assert(SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE == 1, "SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE == 1 expected"); - r += (VarType > SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) ? m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE] : 0; - return r; - } - VkResource& GetResource(SHADER_RESOURCE_VARIABLE_TYPE VarType, Uint32 r) - { - VERIFY_EXPR(r < m_NumResources[VarType]); - auto* Resources = reinterpret_cast(m_ResourceBuffer.get()); - return Resources[GetResourceOffset(VarType, r)]; - } - - const VkResource& GetResource(Uint32 r) const - { - VERIFY_EXPR(r < GetTotalResourceCount()); - const auto* Resources = reinterpret_cast(m_ResourceBuffer.get()); - return Resources[r]; - } - - Uint32 GetTotalResourceCount() const - { - return m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES]; - } - - const char* GetStringPoolData() const - { - const auto* ResourceDataEnd = reinterpret_cast(m_ResourceBuffer.get()) + GetTotalResourceCount(); - const auto* SamplerDataEnd = reinterpret_cast(ResourceDataEnd) + m_NumImmutableSamplers; - return reinterpret_cast(SamplerDataEnd); - } - - static constexpr const Uint32 InvalidResourceIndex = ~0u; - - // Maps resource name to its index in m_ResourceBuffer - using ResourceNameToIndex_t = std::unordered_map; - StringPool AllocateMemory(const std::vector& Shaders, - IMemoryAllocator& Allocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - ResourceNameToIndex_t& UniqueNames, - bool AllocateImmutableSamplers); - - using ImmutableSamplerPtrType = RefCntAutoPtr; - ImmutableSamplerPtrType& GetImmutableSampler(Uint32 n) noexcept - { - VERIFY(n < m_NumImmutableSamplers, "Immutable sampler index (", n, ") is out of range. Total immutable sampler count: ", m_NumImmutableSamplers); - auto* ResourceMemoryEnd = reinterpret_cast(m_ResourceBuffer.get()) + GetTotalResourceCount(); - return reinterpret_cast(ResourceMemoryEnd)[n]; - } - - // clang-format off -/* 0 */ const VulkanUtilities::VulkanLogicalDevice& m_LogicalDevice; -/* 8 */ std::unique_ptr> m_ResourceBuffer; - -/*24 */ std::array m_NumResources = {}; - -/*32 */ Uint16 m_NumImmutableSamplers = 0; -/*34 */ bool m_IsUsingSeparateSamplers = false; -/*36 */ SHADER_TYPE m_ShaderType = SHADER_TYPE_UNKNOWN; - -/*40 */ // End of class - // clang-format on -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp index cebe33c3..6eaaf35c 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp @@ -61,13 +61,14 @@ #include -#include "ShaderResourceLayoutVk.hpp" #include "ShaderResourceVariableBase.hpp" +#include "ShaderResourceCacheVk.hpp" namespace Diligent { class ShaderVariableVkImpl; +class PipelineResourceSignatureVkImpl; // sizeof(ShaderVariableManagerVk) == 32 (x64, msvc, Release) class ShaderVariableManagerVk @@ -79,10 +80,10 @@ public: m_ResourceCache{ResourceCache} {} - void Initialize(const ShaderResourceLayoutVk& SrcLayout, - IMemoryAllocator& Allocator, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes); + void Initialize(const PipelineResourceSignatureVkImpl& SrcLayout, + IMemoryAllocator& Allocator, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes); ~ShaderVariableManagerVk(); @@ -93,19 +94,35 @@ public: void BindResources(IResourceMapping* pResourceMapping, Uint32 Flags) const; - static size_t GetRequiredMemorySize(const ShaderResourceLayoutVk& Layout, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - Uint32& NumVariables); + static size_t GetRequiredMemorySize(const PipelineResourceSignatureVkImpl& Layout, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + Uint32& NumVariables); Uint32 GetVariableCount() const { return m_NumVariables; } private: friend ShaderVariableVkImpl; + using PackedBindingIndex = PipelineResourceSignatureVkImpl::PackedBindingIndex; Uint32 GetVariableIndex(const ShaderVariableVkImpl& Variable); + const PipelineResourceDesc& GetResource(Uint32 Index) const + { + VERIFY_EXPR(m_pSignature); + return m_pSignature->GetResource(Index); + } + const PackedBindingIndex& GetBinding(Uint32 Index) const + { + VERIFY_EXPR(m_pSignature); + return m_pSignature->GetBinding(Index); + } + +private: + PipelineResourceSignatureVkImpl const* m_pSignature = nullptr; + IObject& m_Owner; + // Variable mgr is owned by either Pipeline state object (in which case m_ResourceCache references // static resource cache owned by the same PSO object), or by SRB object (in which case // m_ResourceCache references the cache in the SRB). Thus the cache and the resource layout @@ -123,14 +140,12 @@ private: #endif }; -// sizeof(ShaderVariableVkImpl) == 24 (x64) +// sizeof(ShaderVariableVkImpl) == 16 (x64) class ShaderVariableVkImpl final : public IShaderResourceVariable { public: - ShaderVariableVkImpl(ShaderVariableManagerVk& ParentManager, - const ShaderResourceLayoutVk::VkResource& Resource) : - m_ParentManager{ParentManager}, - m_Resource{Resource} + explicit ShaderVariableVkImpl(ShaderVariableManagerVk& ParentManager) : + m_ParentManager{ParentManager} {} // clang-format off @@ -171,48 +186,101 @@ public: virtual SHADER_RESOURCE_VARIABLE_TYPE DILIGENT_CALL_TYPE GetType() const override final { - return m_Resource.GetVariableType(); + return GetDesc().VarType; } - virtual void DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final - { - m_Resource.BindResource(pObject, 0, m_ParentManager.m_ResourceCache); - } + virtual void DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final; virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, Uint32 FirstElement, - Uint32 NumElements) override final - { - VerifyAndCorrectSetArrayArguments(m_Resource.Name, m_Resource.ArraySize, FirstElement, NumElements); - for (Uint32 Elem = 0; Elem < NumElements; ++Elem) - m_Resource.BindResource(ppObjects[Elem], FirstElement + Elem, m_ParentManager.m_ResourceCache); - } + Uint32 NumElements) override final; virtual void DILIGENT_CALL_TYPE GetResourceDesc(ShaderResourceDesc& ResourceDesc) const override final { - ResourceDesc = m_Resource.GetResourceDesc(); + const auto& Desc = GetDesc(); + ResourceDesc.Name = Desc.Name; + ResourceDesc.Type = Desc.ResourceType; + ResourceDesc.ArraySize = Desc.ArraySize; } virtual Uint32 DILIGENT_CALL_TYPE GetIndex() const override final { - return m_ParentManager.GetVariableIndex(*this); + VERIFY_EXPR(m_ParentManager.m_pVariables); + return Uint32(size_t(this - m_ParentManager.m_pVariables)); } - virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final - { - return m_Resource.IsBound(ArrayIndex, m_ParentManager.m_ResourceCache); - } + virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final; - const ShaderResourceLayoutVk::VkResource& GetResource() const - { - return m_Resource; - } + String GetPrintName(Uint32 ArrayInd) const; + + RESOURCE_DIMENSION GetResourceDimension() const; + + bool IsMultisample() const; private: friend ShaderVariableManagerVk; + using PackedBindingIndex = PipelineResourceSignatureVkImpl::PackedBindingIndex; + + const PipelineResourceDesc& GetDesc() const { return m_ParentManager.GetResource(GetIndex()); } + const PackedBindingIndex& GetBinding() const { return m_ParentManager.GetBinding(GetIndex()); } + + 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; - ShaderVariableManagerVk& m_ParentManager; - const ShaderResourceLayoutVk::VkResource& m_Resource; + 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; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp index 112a5664..e3bb52f6 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp @@ -68,15 +68,14 @@ public: return m_SPIRV; } - const std::shared_ptr& GetShaderResources() const { return m_pShaderResources; } + const SPIRVShaderResources& GetShaderResources() const { return *m_pShaderResources; } const char* GetEntryPoint() const { return m_EntryPoint.c_str(); } private: void MapHLSLVertexShaderInputs(); - // SPIRVShaderResources class instance must be referenced through the shared pointer, because - // it is referenced by ShaderResourceLayoutVk class instances + // AZ TODO: remove shared pointer std::shared_ptr m_pShaderResources; std::string m_EntryPoint; diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp index 5200d792..6e79a300 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp @@ -87,6 +87,7 @@ VkAccessFlags AccessFlagsToVkAccessFlags(ACCESS_FLAGS AccessFlags); VkShaderStageFlagBits ShaderTypeToVkShaderStageFlagBit(SHADER_TYPE ShaderType); +VkShaderStageFlags ShaderTypesToVkShaderStageFlags(SHADER_TYPE ShaderTypes); VkBuildAccelerationStructureFlagsKHR BuildASFlagsToVkBuildAccelerationStructureFlags(RAYTRACING_BUILD_AS_FLAGS Flags); VkGeometryFlagsKHR GeometryFlagsToVkGeometryFlags(RAYTRACING_GEOMETRY_FLAGS Flags); diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 8516a174..bea9f29b 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -231,6 +231,8 @@ 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; @@ -306,21 +308,84 @@ void DeviceContextVkImpl::SetPipelineState(IPipelineState* pPipelineState) default: UNEXPECTED("unknown pipeline type"); } +} + +static Uint32 PipelineTypeToBindPointIndex(PIPELINE_TYPE Type) +{ + const Uint8 Indices[] = { + 0, // PIPELINE_TYPE_GRAPHICS + 1, // PIPELINE_TYPE_COMPUTE + 0, // PIPELINE_TYPE_MESH + 2 // PIPELINE_TYPE_RAY_TRACING + }; + static_assert(_countof(Indices) == Uint32{PIPELINE_TYPE_LAST} + 1, "AZ TODO"); + return Indices[Uint32{Type}]; +} + +static VkPipelineBindPoint PipelineTypeToBindPoint(PIPELINE_TYPE Type) +{ + const VkPipelineBindPoint BindPoints[] = { + VK_PIPELINE_BIND_POINT_GRAPHICS, // PIPELINE_TYPE_GRAPHICS + VK_PIPELINE_BIND_POINT_COMPUTE, // PIPELINE_TYPE_COMPUTE + VK_PIPELINE_BIND_POINT_GRAPHICS, // PIPELINE_TYPE_MESH + VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR // PIPELINE_TYPE_RAY_TRACING + }; + static_assert(_countof(BindPoints) == Uint32{PIPELINE_TYPE_LAST} + 1, "AZ TODO"); + return BindPoints[Uint32{Type}]; +} + +void DeviceContextVkImpl::BindShaderResources(PipelineStateVkImpl* pPipelineStateVk) +{ + VERIFY_EXPR(pPipelineStateVk != nullptr); + + const auto& Layout = pPipelineStateVk->GetPipelineLayout(); + auto& Resources = m_ShaderResources[PipelineTypeToBindPointIndex(Layout.GetPipelineType())]; + auto& DescrSetBindInfo = m_DescrSetBindInfo[PipelineTypeToBindPointIndex(Layout.GetPipelineType())]; + +#ifdef DILIGENT_DEVELOPMENT + for (Uint32 i = 0, Count = Layout.GetSignatureCount(); i < Count; ++i) + { + auto* LayoutSign = Layout.GetSignature(i); + auto* ResSign = Resources[i] ? Resources[i]->GetPipelineResourceSignature() : nullptr; + + if (LayoutSign != ResSign) + { + 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; + } + } +#endif + + // 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); - m_DescrSetBindInfo.Reset(); + // 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()); } -void DeviceContextVkImpl::TransitionShaderResources(IPipelineState* pPipelineState, IShaderResourceBinding* pShaderResourceBinding) +void DeviceContextVkImpl::TransitionShaderResources(IPipelineState*, IShaderResourceBinding* pShaderResourceBinding) { - DEV_CHECK_ERR(pPipelineState != nullptr, "Pipeline state must mot be null"); +#ifdef DILIGENT_DEVELOPMENT if (m_pActiveRenderPass) { LOG_ERROR_MESSAGE("State transitions are not allowed inside a render pass."); return; } - auto* pPipelineStateVk = ValidatedCast(pPipelineState); - pPipelineStateVk->CommitAndTransitionShaderResources(pShaderResourceBinding, this, false, RESOURCE_STATE_TRANSITION_MODE_TRANSITION, nullptr); + if (pShaderResourceBinding == nullptr) + { + LOG_ERROR_MESSAGE("TODO"); + return; + } +#endif + + auto* pResBindingVkImpl = ValidatedCast(pShaderResourceBinding); + auto& ResourceCache = pResBindingVkImpl->GetResourceCache(); + + ResourceCache.TransitionResources(this); } void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) @@ -328,7 +393,32 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR if (!DeviceContextBase::CommitShaderResources(pShaderResourceBinding, StateTransitionMode, 0 /*Dummy*/)) return; - m_pPipelineState->CommitAndTransitionShaderResources(pShaderResourceBinding, this, true, StateTransitionMode, &m_DescrSetBindInfo); + auto* pResBindingVkImpl = ValidatedCast(pShaderResourceBinding); + auto& ResourceCache = pResBindingVkImpl->GetResourceCache(); + +#ifdef DILIGENT_DEBUG + ResourceCache.DbgVerifyDynamicBuffersCounter(); +#endif + + if (StateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION) + { + ResourceCache.TransitionResources(this); + } +#ifdef DILIGENT_DEVELOPMENT + else if (StateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_VERIFY) + { + ResourceCache.TransitionResources(this); + } +#endif + + Uint32 Index = PipelineTypeToBindPointIndex(pResBindingVkImpl->GetPipelineType()); + auto& DescrSetBindInfo = m_DescrSetBindInfo[Index]; + auto& ResourceBinding = m_ShaderResources[Index]; + + // AZ TODO: create dynamic descriptor set + (void)(DescrSetBindInfo); + + ResourceBinding[pResBindingVkImpl->GetBindingIndex()] = pResBindingVkImpl; } void DeviceContextVkImpl::SetStencilRef(Uint32 StencilRef) @@ -468,15 +558,17 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) } #endif - if (m_DescrSetBindInfo.DynamicOffsetCount != 0) + auto& DescrSetBindInfo = m_DescrSetBindInfo[0]; + + 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 (!m_DescrSetBindInfo.DynamicDescriptorsBound || - (m_DescrSetBindInfo.DynamicBuffersPresent && (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) == 0)) + if (!DescrSetBindInfo.DynamicDescriptorsBound || + (DescrSetBindInfo.DynamicBuffersPresent && (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) == 0)) { - m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, m_DescrSetBindInfo); + m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, DescrSetBindInfo); } } #if 0 @@ -621,11 +713,13 @@ void DeviceContextVkImpl::PrepareForDispatchCompute() if (m_CommandBuffer.GetState().RenderPass != VK_NULL_HANDLE) m_CommandBuffer.EndRenderPass(); - if (m_DescrSetBindInfo.DynamicOffsetCount != 0) + auto& DescrSetBindInfo = m_DescrSetBindInfo[1]; + + if (DescrSetBindInfo.DynamicOffsetCount != 0) { - if (!m_DescrSetBindInfo.DynamicDescriptorsBound || m_DescrSetBindInfo.DynamicBuffersPresent) + if (!DescrSetBindInfo.DynamicDescriptorsBound || DescrSetBindInfo.DynamicBuffersPresent) { - m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, m_DescrSetBindInfo); + m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, DescrSetBindInfo); } } #if 0 @@ -643,11 +737,13 @@ void DeviceContextVkImpl::PrepareForRayTracing() { EnsureVkCmdBuffer(); - if (m_DescrSetBindInfo.DynamicOffsetCount != 0) + auto& DescrSetBindInfo = m_DescrSetBindInfo[2]; + + if (DescrSetBindInfo.DynamicOffsetCount != 0) { - if (!m_DescrSetBindInfo.DynamicDescriptorsBound || m_DescrSetBindInfo.DynamicBuffersPresent) + if (!DescrSetBindInfo.DynamicDescriptorsBound || DescrSetBindInfo.DynamicBuffersPresent) { - m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, m_DescrSetBindInfo); + m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, DescrSetBindInfo); } } } @@ -1030,8 +1126,13 @@ void DeviceContextVkImpl::Flush() DisposeCurrentCmdBuffer(m_CommandQueueId, SubmittedFenceValue); } + for (auto& BindInfo : m_DescrSetBindInfo) + BindInfo.Reset(); + + for (auto& ResourcesPerBindPoint : m_ShaderResources) + ResourcesPerBindPoint.fill({}); + m_State = ContextState{}; - m_DescrSetBindInfo.Reset(); m_CommandBuffer.Reset(); m_pPipelineState = nullptr; m_pActiveRenderPass = nullptr; @@ -1067,7 +1168,13 @@ void DeviceContextVkImpl::InvalidateState() m_State = ContextState{}; m_vkRenderPass = VK_NULL_HANDLE; m_vkFramebuffer = VK_NULL_HANDLE; - m_DescrSetBindInfo.Reset(); + + 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(); } @@ -2091,8 +2198,7 @@ void DeviceContextVkImpl::FinishCommandList(class ICommandList** ppCommandList) pCmdListVk->QueryInterface(IID_CommandList, reinterpret_cast(ppCommandList)); m_CommandBuffer.Reset(); - m_State = ContextState{}; - m_DescrSetBindInfo.Reset(); + m_State = ContextState{}; m_pPipelineState = nullptr; InvalidateState(); diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp deleted file mode 100644 index e0196321..00000000 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp +++ /dev/null @@ -1,505 +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 "pch.h" - -#include "PipelineLayout.hpp" -#include "ShaderResourceLayoutVk.hpp" -#include "ShaderVkImpl.hpp" -#include "RenderDeviceVkImpl.hpp" -#include "DeviceContextVkImpl.hpp" -#include "TextureVkImpl.hpp" -#include "BufferVkImpl.hpp" -#include "VulkanTypeConversions.hpp" -#include "HashUtils.hpp" - -namespace Diligent -{ - -class ResourceTypeToVkDescriptorType -{ -public: - ResourceTypeToVkDescriptorType() - { - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please add the corresponding decriptor type"); - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::UniformBuffer}] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer}] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer}] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer}] = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer}] = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::StorageImage}] = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::SampledImage}] = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::AtomicCounter}] = VK_DESCRIPTOR_TYPE_MAX_ENUM; // atomic counter doesn't exist in Vulkan - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::SeparateImage}] = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::SeparateSampler}] = VK_DESCRIPTOR_TYPE_SAMPLER; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::InputAttachment}] = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - m_Map[Uint32{SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure}] = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; - } - - VkDescriptorType operator[](SPIRVShaderResourceAttribs::ResourceType ResType) const - { - return m_Map[static_cast(ResType)]; - } - -private: - std::array m_Map = {}; -}; - -VkDescriptorType PipelineLayout::GetVkDescriptorType(SPIRVShaderResourceAttribs::ResourceType Type) -{ - static const ResourceTypeToVkDescriptorType ResTypeToVkDescrType; - return ResTypeToVkDescrType[Type]; -} - -PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayoutManager(IMemoryAllocator& MemAllocator) : - m_MemAllocator{MemAllocator}, - m_LayoutBindings(STD_ALLOCATOR_RAW_MEM(VkDescriptorSetLayoutBinding, MemAllocator, "Allocator for Layout Bindings")) -{} - - -void PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::AddBinding(const VkDescriptorSetLayoutBinding& Binding, IMemoryAllocator& MemAllocator) -{ - VERIFY(VkLayout == VK_NULL_HANDLE, "Descriptor set must not be finalized"); - ReserveMemory(NumLayoutBindings + 1, MemAllocator); - pBindings[NumLayoutBindings++] = Binding; - TotalDescriptors += Binding.descriptorCount; - if (Binding.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || - Binding.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) - { - VERIFY(NumDynamicDescriptors + Binding.descriptorCount <= std::numeric_limits::max(), "Number of dynamic descriptors exceeds max representable value"); - NumDynamicDescriptors += static_cast(Binding.descriptorCount); - } -} - -size_t PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::GetMemorySize(Uint32 NumBindings) -{ - if (NumBindings == 0) - return 0; - - // Align up to the nearest power of two - size_t MemSize = 0; - if (NumBindings == 1) - MemSize = 1; - else if (NumBindings > 1) - { - // NumBindings = 2^n - // n n-1 2 1 0 - // 2^n = 1 0 ... 0 0 0 - // - // n n-1 2 1 0 - // 2^n-1 = 0 1 ... 1 1 1 - // msb = n-1 - // MemSize = 2^n - - - // NumBindings = 2^n + [1 .. 2^n-1] - // n n-1 - // 2^n = 1 0 ... 1 ... - // - // n n-1 - // 2^n-1 = 1 0 ... - // msb = n - // MemSize = 2^(n+1) - - MemSize = Uint32{2U << PlatformMisc::GetMSB(NumBindings - 1)}; - } - VERIFY_EXPR(((NumBindings & (NumBindings - 1)) == 0) && NumBindings == MemSize || NumBindings < MemSize); - -#ifdef DILIGENT_DEBUG - static constexpr size_t MinMemSize = 1; -#else - static constexpr size_t MinMemSize = 16; -#endif - MemSize = std::max(MemSize, MinMemSize); - return MemSize * sizeof(VkDescriptorSetLayoutBinding); -} - -void PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::ReserveMemory(Uint32 NumBindings, IMemoryAllocator& MemAllocator) -{ - size_t ReservedMemory = GetMemorySize(NumLayoutBindings); - size_t RequiredMemory = GetMemorySize(NumBindings); - if (RequiredMemory > ReservedMemory) - { - void* pNewBindings = ALLOCATE_RAW(MemAllocator, "Memory buffer for descriptor set layout bindings", RequiredMemory); - if (pBindings != nullptr) - { - memcpy(pNewBindings, pBindings, sizeof(VkDescriptorSetLayoutBinding) * NumLayoutBindings); - MemAllocator.Free(pBindings); - } - pBindings = reinterpret_cast(pNewBindings); - } -} - -void PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::Finalize(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice, - IMemoryAllocator& MemAllocator, - VkDescriptorSetLayoutBinding* pNewBindings) -{ - VERIFY_EXPR(memcmp(pBindings, pNewBindings, sizeof(VkDescriptorSetLayoutBinding) * NumLayoutBindings) == 0); - - VkDescriptorSetLayoutCreateInfo SetLayoutCI = {}; - - SetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - SetLayoutCI.pNext = nullptr; - SetLayoutCI.flags = 0; - SetLayoutCI.bindingCount = NumLayoutBindings; - SetLayoutCI.pBindings = pBindings; - VkLayout = LogicalDevice.CreateDescriptorSetLayout(SetLayoutCI); - - MemAllocator.Free(pBindings); - pBindings = pNewBindings; -} - -void PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::Release(RenderDeviceVkImpl* pRenderDeviceVk, IMemoryAllocator& MemAllocator, Uint64 CommandQueueMask) -{ - pRenderDeviceVk->SafeReleaseDeviceObject(std::move(VkLayout), CommandQueueMask); - for (uint32_t b = 0; b < NumLayoutBindings; ++b) - { - if (pBindings[b].pImmutableSamplers != nullptr) - MemAllocator.Free(const_cast(pBindings[b].pImmutableSamplers)); - } - pBindings = nullptr; - NumLayoutBindings = 0; -} - -PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::~DescriptorSetLayout() -{ - VERIFY(VkLayout == VK_NULL_HANDLE, "Vulkan descriptor set layout has not been released. Did you forget to call Release()?"); -} - -bool PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::operator==(const DescriptorSetLayout& rhs) const -{ - // clang-format off - if (TotalDescriptors != rhs.TotalDescriptors || - SetIndex != rhs.SetIndex || - NumDynamicDescriptors != rhs.NumDynamicDescriptors || - NumLayoutBindings != rhs.NumLayoutBindings) - return false; - // clang-format on - - for (uint32_t b = 0; b < NumLayoutBindings; ++b) - { - const auto& B0 = pBindings[b]; - const auto& B1 = rhs.pBindings[b]; - // clang-format off - if (B0.binding != B1.binding || - B0.descriptorType != B1.descriptorType || - B0.descriptorCount != B1.descriptorCount || - B0.stageFlags != B1.stageFlags) - return false; - - if ((B0.pImmutableSamplers != nullptr && B1.pImmutableSamplers == nullptr) || - (B0.pImmutableSamplers == nullptr && B1.pImmutableSamplers != nullptr)) - return false; - // Immutable samplers themselves should not affect compatibility - // clang-format on - } - return true; -} - -size_t PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::GetHash() const -{ - size_t Hash = ComputeHash(SetIndex, NumLayoutBindings, TotalDescriptors, NumDynamicDescriptors); - for (uint32_t b = 0; b < NumLayoutBindings; ++b) - { - const auto& B = pBindings[b]; - HashCombine(Hash, B.binding, static_cast(B.descriptorType), B.descriptorCount, static_cast(B.stageFlags), B.pImmutableSamplers != nullptr); - } - - return Hash; -} - -void PipelineLayout::DescriptorSetLayoutManager::Finalize(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice) -{ - size_t TotalBindings = 0; - for (const auto& Layout : m_DescriptorSetLayouts) - { - TotalBindings += Layout.NumLayoutBindings; - } - m_LayoutBindings.resize(TotalBindings); - size_t BindingOffset = 0; - - std::array ActiveDescrSetLayouts = {}; - for (auto& Layout : m_DescriptorSetLayouts) - { - if (Layout.SetIndex >= 0) - { - std::copy(Layout.pBindings, Layout.pBindings + Layout.NumLayoutBindings, m_LayoutBindings.begin() + BindingOffset); - Layout.Finalize(LogicalDevice, m_MemAllocator, &m_LayoutBindings[BindingOffset]); - BindingOffset += Layout.NumLayoutBindings; - ActiveDescrSetLayouts[Layout.SetIndex] = Layout.VkLayout; - } - } - VERIFY_EXPR(BindingOffset == TotalBindings); - // clang-format off - VERIFY_EXPR(m_ActiveSets == 0 && ActiveDescrSetLayouts[0] == VK_NULL_HANDLE && ActiveDescrSetLayouts[1] == VK_NULL_HANDLE || - m_ActiveSets == 1 && ActiveDescrSetLayouts[0] != VK_NULL_HANDLE && ActiveDescrSetLayouts[1] == VK_NULL_HANDLE || - m_ActiveSets == 2 && ActiveDescrSetLayouts[0] != VK_NULL_HANDLE && ActiveDescrSetLayouts[1] != VK_NULL_HANDLE); - // clang-format on - - VkPipelineLayoutCreateInfo PipelineLayoutCI = {}; - - PipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - PipelineLayoutCI.pNext = nullptr; - PipelineLayoutCI.flags = 0; // reserved for future use - PipelineLayoutCI.setLayoutCount = m_ActiveSets; - PipelineLayoutCI.pSetLayouts = PipelineLayoutCI.setLayoutCount != 0 ? ActiveDescrSetLayouts.data() : nullptr; - PipelineLayoutCI.pushConstantRangeCount = 0; - PipelineLayoutCI.pPushConstantRanges = nullptr; - m_VkPipelineLayout = LogicalDevice.CreatePipelineLayout(PipelineLayoutCI); - - VERIFY_EXPR(BindingOffset == TotalBindings); -} - -void PipelineLayout::DescriptorSetLayoutManager::Release(RenderDeviceVkImpl* pRenderDeviceVk, Uint64 CommandQueueMask) -{ - for (auto& Layout : m_DescriptorSetLayouts) - Layout.Release(pRenderDeviceVk, m_MemAllocator, CommandQueueMask); - - pRenderDeviceVk->SafeReleaseDeviceObject(std::move(m_VkPipelineLayout), CommandQueueMask); -} - -PipelineLayout::DescriptorSetLayoutManager::~DescriptorSetLayoutManager() -{ - VERIFY(m_VkPipelineLayout == VK_NULL_HANDLE, "Vulkan pipeline layout has not been released. Did you forget to call Release()?"); -} - -bool PipelineLayout::DescriptorSetLayoutManager::operator==(const DescriptorSetLayoutManager& rhs) const -{ - // Two pipeline layouts are defined to be "compatible for set N" if they were created with identically - // defined descriptor set layouts for sets zero through N, and if they were created with identical push - // constant ranges (13.2.2) - - if (m_ActiveSets != rhs.m_ActiveSets) - return false; - - for (size_t i = 0; i < m_DescriptorSetLayouts.size(); ++i) - if (m_DescriptorSetLayouts[i] != rhs.m_DescriptorSetLayouts[i]) - return false; - - return true; -} - -size_t PipelineLayout::DescriptorSetLayoutManager::GetHash() const -{ - size_t Hash = 0; - for (const auto& SetLayout : m_DescriptorSetLayouts) - HashCombine(Hash, SetLayout.GetHash()); - - return Hash; -} - -void PipelineLayout::DescriptorSetLayoutManager::AllocateResourceSlot(const SPIRVShaderResourceAttribs& ResAttribs, - SHADER_RESOURCE_VARIABLE_TYPE VariableType, - VkSampler vkImmutableSampler, - SHADER_TYPE ShaderType, - Uint32& DescriptorSet, - Uint32& Binding, - Uint32& OffsetInCache) -{ - auto& DescrSet = GetDescriptorSet(VariableType); - if (DescrSet.SetIndex < 0) - { - DescrSet.SetIndex = m_ActiveSets++; - } - DescriptorSet = DescrSet.SetIndex; - - VkDescriptorSetLayoutBinding VkBinding = {}; - - Binding = DescrSet.NumLayoutBindings; - - VkBinding.binding = Binding; - VkBinding.descriptorType = GetVkDescriptorType(ResAttribs.Type); - VkBinding.descriptorCount = ResAttribs.ArraySize; - // There are no limitations on what combinations of stages can use a descriptor binding (13.2.1) - VkBinding.stageFlags = ShaderTypeToVkShaderStageFlagBit(ShaderType); - if (vkImmutableSampler != VK_NULL_HANDLE) - { - // If descriptorType is VK_DESCRIPTOR_TYPE_SAMPLER or VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, and - // descriptorCount is not 0 and pImmutableSamplers is not NULL, pImmutableSamplers must be a valid pointer - // to an array of descriptorCount valid VkSampler handles (13.2.1) - auto* pImmutableSamplers = ALLOCATE(m_MemAllocator, "Memory buffer for immutable samplers", VkSampler, VkBinding.descriptorCount); - for (uint32_t s = 0; s < VkBinding.descriptorCount; ++s) - pImmutableSamplers[s] = vkImmutableSampler; - VkBinding.pImmutableSamplers = pImmutableSamplers; - } - else - VkBinding.pImmutableSamplers = nullptr; - - OffsetInCache = DescrSet.TotalDescriptors; - DescrSet.AddBinding(VkBinding, m_MemAllocator); -} - -PipelineLayout::PipelineLayout() : - m_MemAllocator{GetRawAllocator()}, - m_LayoutMgr{m_MemAllocator} -{ -} - -void PipelineLayout::Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask) -{ - m_LayoutMgr.Release(pDeviceVkImpl, CommandQueueMask); -} - -void PipelineLayout::AllocateResourceSlot(const SPIRVShaderResourceAttribs& ResAttribs, - SHADER_RESOURCE_VARIABLE_TYPE VariableType, - VkSampler vkImmutableSampler, - SHADER_TYPE ShaderType, - Uint32& DescriptorSet, // Output parameter - Uint32& Binding, // Output parameter - Uint32& OffsetInCache) -{ - VERIFY((ResAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || - ResAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler) || - vkImmutableSampler == VK_NULL_HANDLE, - "Immutable sampler should only be specified for combined image samplers or separate samplers"); - m_LayoutMgr.AllocateResourceSlot(ResAttribs, VariableType, vkImmutableSampler, ShaderType, DescriptorSet, Binding, OffsetInCache); -} - -void PipelineLayout::Finalize(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice) -{ - m_LayoutMgr.Finalize(LogicalDevice); -} - -std::array PipelineLayout::GetDescriptorSetSizes(Uint32& NumSets) const -{ - NumSets = 0; - std::array SetSizes = {}; - - const auto& StaticAndMutSet = m_LayoutMgr.GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); - if (StaticAndMutSet.SetIndex >= 0) - { - NumSets = std::max(NumSets, static_cast(StaticAndMutSet.SetIndex + 1)); - SetSizes[StaticAndMutSet.SetIndex] = StaticAndMutSet.TotalDescriptors; - } - - const auto& DynamicSet = m_LayoutMgr.GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC); - if (DynamicSet.SetIndex >= 0) - { - NumSets = std::max(NumSets, static_cast(DynamicSet.SetIndex + 1)); - SetSizes[DynamicSet.SetIndex] = DynamicSet.TotalDescriptors; - } - - return SetSizes; -} - -void PipelineLayout::InitResourceCache(RenderDeviceVkImpl* pDeviceVkImpl, - ShaderResourceCacheVk& ResourceCache, - IMemoryAllocator& CacheMemAllocator, - const char* DbgPipelineName) const -{ - Uint32 NumSets = 0; - auto SetSizes = GetDescriptorSetSizes(NumSets); - - // 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, NumSets, SetSizes.data()); - - const auto& StaticAndMutSet = m_LayoutMgr.GetDescriptorSet(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); - if (StaticAndMutSet.SetIndex >= 0) - { - const char* DescrSetName = "Static/Mutable Descriptor Set"; -#ifdef DILIGENT_DEVELOPMENT - std::string _DescrSetName(DbgPipelineName); - _DescrSetName.append(" - static/mutable set"); - DescrSetName = _DescrSetName.c_str(); -#endif - DescriptorSetAllocation SetAllocation = pDeviceVkImpl->AllocateDescriptorSet(~Uint64{0}, StaticAndMutSet.VkLayout, DescrSetName); - ResourceCache.GetDescriptorSet(StaticAndMutSet.SetIndex).AssignDescriptorSetAllocation(std::move(SetAllocation)); - } -} - -void PipelineLayout::PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, - VkPipelineBindPoint BindPoint, - const ShaderResourceCacheVk& ResourceCache, - DescriptorSetBindInfo& BindInfo, - VkDescriptorSet VkDynamicDescrSet) const -{ -#ifdef DILIGENT_DEBUG - BindInfo.vkSets.clear(); -#endif - - // Do not use vector::resize for BindInfo.vkSets and BindInfo.DynamicOffsets as this - // causes unnecessary work to zero-initialize new elements - - 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; - - 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)) - { - const auto& Set = m_LayoutMgr.GetDescriptorSet(VarType); - if (Set.SetIndex >= 0) - { - 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"); - } - TotalDynamicDescriptors += Set.NumDynamicDescriptors; - } - -#ifdef DILIGENT_DEBUG - for (const auto& set : BindInfo.vkSets) - VERIFY(set != VK_NULL_HANDLE, "Descriptor set must not be null"); -#endif - - 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 (TotalDynamicDescriptors == 0) - { - // 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); - } - - BindInfo.DynamicDescriptorsBound = false; -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp new file mode 100644 index 00000000..6ca53a44 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutCacheVk.cpp @@ -0,0 +1,80 @@ +/* + * 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 new file mode 100644 index 00000000..446c41bb --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp @@ -0,0 +1,170 @@ +/* + * 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 "pch.h" + +#include "PipelineLayoutVk.hpp" +#include "ShaderVkImpl.hpp" +#include "RenderDeviceVkImpl.hpp" +#include "DeviceContextVkImpl.hpp" +#include "TextureVkImpl.hpp" +#include "BufferVkImpl.hpp" +#include "VulkanTypeConversions.hpp" +#include "HashUtils.hpp" + +namespace Diligent +{ + +PipelineLayoutVk::PipelineLayoutVk(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, IPipelineResourceSignature** ppSignatures, Uint32 SignatureCount) : + ObjectBase{pRefCounters}, + m_pDeviceVk{pDeviceVk}, + m_SignatureCount{0} +{ + for (Uint32 i = 0; i < SignatureCount; ++i) + { + auto* pSign = ValidatedCast(ppSignatures[i]); + if (pSign) + { + const Uint8 Index = pSign->GetDesc().BindingIndex; + + 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(), ")."); + + 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, "'."); + + m_SignatureCount = std::max(m_SignatureCount, Uint8(Index + 1)); + m_Signatures[Index] = pSign; + + 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(); + } + } +} + +PipelineLayoutVk::~PipelineLayoutVk() +{ + m_pDeviceVk->GetPipelineLayoutCache().OnDestroyLayout(this); + m_pDeviceVk->SafeReleaseDeviceObject(std::move(m_VkPipelineLayout), ~0ull); +} + +void PipelineLayoutVk::Finalize() +{ + std::array DescSetLayouts; + + Uint32 DescSetLayoutCount = 0; + Uint32 DynamicBufferCount = 0; + + for (Uint32 i = 0; i < m_SignatureCount; ++i) + { + if (m_Signatures[i] == nullptr) + continue; + + m_DescSetOffset[i] = static_cast(DescSetLayoutCount); + m_DynBufOffset[i] = static_cast(DynamicBufferCount); + + auto StaticDSLayout = m_Signatures[i]->GetStaticVkDescriptorSetLayout(); + auto DynamicDSLayout = m_Signatures[i]->GetDynamicVkDescriptorSetLayout(); + + if (StaticDSLayout) DescSetLayouts[DescSetLayoutCount++] = StaticDSLayout; + if (DynamicDSLayout) DescSetLayouts[DescSetLayoutCount++] = DynamicDSLayout; + + DynamicBufferCount += m_Signatures[i]->GetDynamicBufferCount(); + } + + VkPipelineLayoutCreateInfo PipelineLayoutCI = {}; + + PipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + PipelineLayoutCI.pNext = nullptr; + PipelineLayoutCI.flags = 0; // reserved for future use + PipelineLayoutCI.setLayoutCount = DescSetLayoutCount; + PipelineLayoutCI.pSetLayouts = DescSetLayoutCount ? DescSetLayouts.data() : nullptr; + PipelineLayoutCI.pushConstantRangeCount = 0; + PipelineLayoutCI.pPushConstantRanges = nullptr; + m_VkPipelineLayout = m_pDeviceVk->GetLogicalDevice().CreatePipelineLayout(PipelineLayoutCI); + + const auto& Limits = m_pDeviceVk->GetPhysicalDevice().GetProperties().limits; + VERIFY_EXPR(DescSetLayoutCount <= Limits.maxBoundDescriptorSets); + VERIFY_EXPR(DynamicBufferCount <= (Limits.maxDescriptorSetUniformBuffersDynamic + Limits.maxDescriptorSetStorageBuffersDynamic)); // AZ TODO + + m_DescrSetCount = static_cast(DescSetLayoutCount); + m_DynamicOffsetCount = static_cast(DynamicBufferCount); +} + +size_t PipelineLayoutVk::GetHash() const +{ + size_t hash = 0; + 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); + } + return hash; +} + +bool PipelineLayoutVk::GetResourceInfo(const char* Name, SHADER_TYPE Stage, ResourceInfo& Info) const +{ + // AZ TODO: optimize + for (Uint32 i = 0, Count = GetSignatureCount(); i < Count; ++i) + { + auto* pSign = GetSignature(i); + if (pSign) + { + auto& Desc = pSign->GetDesc(); + Uint32 BindingCount[2] = {}; + + for (Uint32 j = 0; j < Desc.NumResources; ++j) + { + 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; + } + } + } + return false; +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp new file mode 100644 index 00000000..da646a96 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -0,0 +1,465 @@ +/* + * 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 "pch.h" +#include "PipelineResourceSignatureVkImpl.hpp" +#include "ShaderResourceBindingVkImpl.hpp" +#include "RenderDeviceVkImpl.hpp" +#include "VulkanTypeConversions.hpp" + +namespace Diligent +{ + +VkDescriptorType PipelineResourceSignatureVkImpl::GetVkDescriptorType(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) + { + // 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"); + // clang-format on + } + return VK_DESCRIPTOR_TYPE_MAX_ENUM; +} + +PipelineResourceSignatureVkImpl::PipelineResourceSignatureVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pDevice, + const PipelineResourceSignatureDesc& Desc, + bool bIsDeviceInternal) : + TPipelineResourceSignatureBase{pRefCounters, pDevice, Desc, bIsDeviceInternal}, + m_SRBMemAllocator{GetRawAllocator()} +{ +#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); + + 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); + } + } + + + MemPool.Reserve(); + + + m_pBindingIndices = MemPool.Allocate(m_Desc.NumResources); + + // The memory is now owned by PipelineResourceSignatureVkImpl and will be freed by Destruct(). + auto* Ptr = MemPool.ReleaseOwnership(); + VERIFY_EXPR(Ptr == m_pBindingIndices); + (void)Ptr; + + CopyDescription(MemPool, Desc); + + if (StaticVarStageCount > 0) + { + m_pResourceCache = MemPool.Construct(ShaderResourceCacheVk::StaticShaderResources); + m_StaticVarsMgrs = MemPool.Allocate(StaticVarStageCount); + + m_pResourceCache->InitializeSets(GetRawAllocator(), 1, &StaticVarCount); + + const SHADER_RESOURCE_VARIABLE_TYPE AllowedVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_STATIC}; + + for (Int8 i = 0; i < StaticVarStageCount; ++i) + { + new (m_StaticVarsMgrs + i) ShaderVariableManagerVk{*this, *m_pResourceCache}; + m_StaticVarsMgrs[i].Initialize(*this, GetRawAllocator(), AllowedVarTypes, _countof(AllowedVarTypes)); + } + } + + std::vector LayoutBindings[2]; + + for (Uint32 i = 0; i < m_Desc.NumResources; ++i) + { + const auto& Res = m_Desc.Resources[i]; + auto& Binding = m_pBindingIndices[i]; + + Binding.DescSet = (Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC ? 1 : 0); + Binding.Binding = static_cast(LayoutBindings[Binding.DescSet].size()); + Binding.SamplerInd = 0; // AZ TODO + + LayoutBindings[Binding.DescSet].emplace_back(); + auto& LayoutBinding = LayoutBindings[Binding.DescSet].back(); + + LayoutBinding.binding = Binding.Binding; + LayoutBinding.descriptorCount = Res.ArraySize; + LayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(Res.ShaderStages); + LayoutBinding.pImmutableSamplers = nullptr; + LayoutBinding.descriptorType = GetVkDescriptorType(Res); + + if (!(Res.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_OFFSETS)) + m_DynamicBufferCount += static_cast(Res.ArraySize); + + if (m_pResourceCache && Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + m_pResourceCache->InitializeResources(0, Binding.Binding, Res.ArraySize, LayoutBinding.descriptorType); + } + + if (m_Desc.SRBAllocationGranularity > 1) + { + const SHADER_RESOURCE_VARIABLE_TYPE AllowedVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; + + Uint32 UnusedNumVars = 0; + const size_t ShaderVariableDataSize = ShaderVariableManagerVk::GetRequiredMemorySize(*this, AllowedVarTypes, _countof(AllowedVarTypes), UnusedNumVars); + + 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); + + m_SRBMemAllocator.Initialize(m_Desc.SRBAllocationGranularity, 1, &ShaderVariableDataSize, 1, &CacheMemorySize); + } + + VkDescriptorSetLayoutCreateInfo SetLayoutCI = {}; + + SetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + SetLayoutCI.pNext = nullptr; + SetLayoutCI.flags = 0; + + const auto& LogicalDevice = pDevice->GetLogicalDevice(); + + for (Uint32 i = 0; i < _countof(LayoutBindings); ++i) + { + auto& LayoutBinding = LayoutBindings[i]; + if (LayoutBinding.empty()) + continue; + + SetLayoutCI.bindingCount = static_cast(LayoutBinding.size()); + SetLayoutCI.pBindings = LayoutBinding.data(); + m_VkDescSetLayouts[i] = LogicalDevice.CreateDescriptorSetLayout(SetLayoutCI); + } + } + catch (...) + { + Destruct(); + throw; + } +#undef LOG_PRS_ERROR_AND_THROW +} + +PipelineResourceSignatureVkImpl::~PipelineResourceSignatureVkImpl() +{ + Destruct(); +} + +void PipelineResourceSignatureVkImpl::Destruct() +{ + TPipelineResourceSignatureBase::Destruct(); + + for (auto& Layout : m_VkDescSetLayouts) + { + if (Layout) + m_pDevice->SafeReleaseDeviceObject(std::move(Layout), ~0ull); + } + + if (m_pBindingIndices == nullptr) + return; // memory is not allocated + + auto& RawAllocator = GetRawAllocator(); + + for (size_t i = 0; m_StaticVarsMgrs && i < m_StaticVarIndex.size(); ++i) + { + Int8 Idx = m_StaticVarIndex[i]; + if (Idx >= 0) + { + m_StaticVarsMgrs[Idx].DestroyVariables(RawAllocator); + m_StaticVarsMgrs[Idx].~ShaderVariableManagerVk(); + } + } + m_StaticVarsMgrs = nullptr; + + if (m_pResourceCache != nullptr) + { + m_pResourceCache->~ShaderResourceCacheVk(); + m_pResourceCache = nullptr; + } + + if (void* pRawMem = m_pBindingIndices) + { + RawAllocator.Free(pRawMem); + m_pBindingIndices = nullptr; + } +} + +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)); +} + +void PipelineResourceSignatureVkImpl::PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, + VkPipelineBindPoint BindPoint, + const ShaderResourceCacheVk& ResourceCache, + DescriptorSetBindInfo& BindInfo, + VkDescriptorSet VkDynamicDescrSet) const +{ + /* +#ifdef DILIGENT_DEBUG + BindInfo.vkSets.clear(); +#endif + + // Do not use vector::resize for BindInfo.vkSets and BindInfo.DynamicOffsets as this + // causes unnecessary work to zero-initialize new elements + + 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; + + 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)) + { + const auto& Set = m_LayoutMgr.GetDescriptorSet(VarType); + if (Set.SetIndex >= 0) + { + 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"); + } + TotalDynamicDescriptors += Set.NumDynamicDescriptors; + } + +#ifdef DILIGENT_DEBUG + for (const auto& set : BindInfo.vkSets) + VERIFY(set != VK_NULL_HANDLE, "Descriptor set must not be null"); +#endif + + 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 (TotalDynamicDescriptors == 0) + { + // 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); + } + + BindInfo.DynamicDescriptorsBound = false; + */ +} + +Uint32 PipelineResourceSignatureVkImpl::GetStaticVariableCount(SHADER_TYPE ShaderType) const +{ + const auto VarMngrInd = GetStaticVariableCountHelper(ShaderType, m_StaticVarIndex); + if (VarMngrInd < 0) + return 0; + + auto& StaticVarMgr = m_StaticVarsMgrs[VarMngrInd]; + return StaticVarMgr.GetVariableCount(); +} + +IShaderResourceVariable* PipelineResourceSignatureVkImpl::GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) +{ + const auto VarMngrInd = GetStaticVariableByNameHelper(ShaderType, Name, m_StaticVarIndex); + if (VarMngrInd < 0) + return nullptr; + + auto& StaticVarMgr = m_StaticVarsMgrs[VarMngrInd]; + return StaticVarMgr.GetVariable(Name); +} + +IShaderResourceVariable* PipelineResourceSignatureVkImpl::GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) +{ + const auto VarMngrInd = GetStaticVariableByIndexHelper(ShaderType, Index, m_StaticVarIndex); + if (VarMngrInd < 0) + return nullptr; + + const auto& StaticVarMgr = m_StaticVarsMgrs[VarMngrInd]; + return StaticVarMgr.GetVariable(Index); +} + +void PipelineResourceSignatureVkImpl::InitResourceCache(ShaderResourceCacheVk& ResourceCache, + IMemoryAllocator& CacheMemAllocator, + const char* DbgPipelineName) const +{ + std::array VarCount = {}; + for (Uint32 r = 0; r < m_Desc.NumResources; ++r) + { + const auto& Res = GetResource(r); + const auto& Bind = GetBinding(r); + VarCount[Bind.DescSet] += Res.ArraySize; + } + + // 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()); + + if (auto VkLayout = GetStaticVkDescriptorSetLayout()) + { + const char* DescrSetName = "Static/Mutable Descriptor Set"; +#ifdef DILIGENT_DEVELOPMENT + std::string _DescrSetName(DbgPipelineName); + _DescrSetName.append(" - static/mutable set"); + DescrSetName = _DescrSetName.c_str(); +#endif + DescriptorSetAllocation SetAllocation = GetDevice()->AllocateDescriptorSet(~Uint64{0}, VkLayout, DescrSetName); + ResourceCache.GetDescriptorSet(0).AssignDescriptorSetAllocation(std::move(SetAllocation)); + } +} + +SHADER_TYPE PipelineResourceSignatureVkImpl::GetShaderStageType(Uint32 StageIndex) const +{ + 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; + + if (Index == StageIndex) + return StageBit; + } + + UNEXPECTED("index is out of range"); + return SHADER_TYPE_UNKNOWN; +} + +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)); + } +} + +void PipelineResourceSignatureVkImpl::InitializeStaticSRBResources(ShaderResourceCacheVk& DstResourceCache) const +{ + const auto& SrcResourceCache = *m_pResourceCache; + (void)(SrcResourceCache); + + for (Uint32 s = 0; s < GetNumShaderStages(); ++s) + { + // AZ TODO + } +#ifdef DILIGENT_DEBUG + DstResourceCache.DbgVerifyDynamicBuffersCounter(); +#endif +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 338f0f50..225e3f82 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -76,7 +76,7 @@ bool StripReflection(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice, } void InitPipelineShaderStages(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice, - ShaderResourceLayoutVk::TShaderStages& ShaderStages, + PipelineStateVkImpl::TShaderStages& ShaderStages, std::vector& ShaderModules, std::vector& Stages) { @@ -131,7 +131,7 @@ void InitPipelineShaderStages(const VulkanUtilities::VulkanLogicalDevice& void CreateComputePipeline(RenderDeviceVkImpl* pDeviceVk, std::vector& Stages, - const PipelineLayout& Layout, + const PipelineLayoutVk& Layout, const PipelineStateDesc& PSODesc, VulkanUtilities::PipelineWrapper& Pipeline) { @@ -156,7 +156,7 @@ void CreateComputePipeline(RenderDeviceVkImpl* pDevice void CreateGraphicsPipeline(RenderDeviceVkImpl* pDeviceVk, std::vector& Stages, - const PipelineLayout& Layout, + const PipelineLayoutVk& Layout, const PipelineStateDesc& PSODesc, const GraphicsPipelineDesc& GraphicsPipeline, VulkanUtilities::PipelineWrapper& Pipeline, @@ -341,7 +341,7 @@ void CreateGraphicsPipeline(RenderDeviceVkImpl* pDevic void CreateRayTracingPipeline(RenderDeviceVkImpl* pDeviceVk, const std::vector& vkStages, const std::vector& vkShaderGroups, - const PipelineLayout& Layout, + const PipelineLayoutVk& Layout, const PipelineStateDesc& PSODesc, const RayTracingPipelineDesc& RayTracingPipeline, VulkanUtilities::PipelineWrapper& Pipeline) @@ -375,7 +375,7 @@ void CreateRayTracingPipeline(RenderDeviceVkImpl* std::vector BuildRTShaderGroupDescription( const RayTracingPipelineStateCreateInfo& CreateInfo, const std::unordered_map& NameToGroupIndex, - const ShaderResourceLayoutVk::TShaderStages& ShaderStages) + const PipelineStateVkImpl::TShaderStages& ShaderStages) { // Returns the shader module index in the PSO create info auto GetShaderModuleIndex = [&ShaderStages](const IShader* pShader) { @@ -505,6 +505,42 @@ std::vector BuildRTShaderGroupDescription( } // namespace +PipelineStateVkImpl::ShaderStageInfo::ShaderStageInfo(const ShaderVkImpl* pShader) : + Type{pShader->GetDesc().ShaderType}, + Shaders{pShader}, + SPIRVs{pShader->GetSPIRV()} +{ +} + +void PipelineStateVkImpl::ShaderStageInfo::Append(const ShaderVkImpl* pShader) +{ + VERIFY_EXPR(pShader != nullptr); + VERIFY(std::find(Shaders.begin(), Shaders.end(), pShader) == Shaders.end(), + "Shader '", pShader->GetDesc().Name, "' already exists in the stage. Shaders must be deduplicated."); + + const auto NewShaderType = pShader->GetDesc().ShaderType; + if (Type == SHADER_TYPE_UNKNOWN) + { + VERIFY_EXPR(Shaders.empty() && SPIRVs.empty()); + Type = NewShaderType; + } + else + { + VERIFY(Type == NewShaderType, "The type (", GetShaderTypeLiteralName(NewShaderType), + ") of shader '", pShader->GetDesc().Name, "' being added to the stage is incosistent with the stage type (", + GetShaderTypeLiteralName(Type), ")."); + } + Shaders.push_back(pShader); + SPIRVs.push_back(pShader->GetSPIRV()); +} + +size_t PipelineStateVkImpl::ShaderStageInfo::Count() const +{ + VERIFY_EXPR(Shaders.size() == SPIRVs.size()); + return Shaders.size(); +} + + RenderPassDesc PipelineStateVkImpl::GetImplicitRenderPassDesc( Uint32 NumRenderTargets, const TEXTURE_FORMAT RTVFormats[], @@ -589,65 +625,102 @@ RenderPassDesc PipelineStateVkImpl::GetImplicitRenderPassDesc( return RPDesc; } -void PipelineStateVkImpl::InitResourceLayouts(const PipelineStateCreateInfo& CreateInfo, - TShaderStages& ShaderStages) +void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& CreateInfo, + TShaderStages& ShaderStages) { - auto* const pDeviceVk = GetDevice(); - const auto& LogicalDevice = pDeviceVk->GetLogicalDevice(); - - for (size_t s = 0; s < ShaderStages.size(); ++s) - { - auto& StageInfo = ShaderStages[s]; - const auto ShaderType = StageInfo.Type; - const auto ShaderTypeInd = GetShaderTypePipelineIndex(ShaderType, m_Desc.PipelineType); - - m_ResourceLayoutIndex[ShaderTypeInd] = static_cast(s); + std::array Signatures; + RefCntAutoPtr TempSignature; - auto& StaticResLayout = m_ShaderResourceLayouts[GetNumShaderStages() + s]; - StaticResLayout.InitializeStaticResourceLayout(StageInfo.Shaders, GetRawAllocator(), m_Desc.ResourceLayout, m_StaticResCaches[s]); + Uint32 SignatureCount = CreateInfo.ResourceSignaturesCount; - m_StaticVarsMgrs[s].Initialize(StaticResLayout, GetRawAllocator(), nullptr, 0); + for (Uint32 i = 0; i < SignatureCount; ++i) + { + Signatures[i] = CreateInfo.ppResourceSignatures[i]; } - // Initialize shader resource layouts and assign bindings and descriptor sets in shader SPIRVs - ShaderResourceLayoutVk::Initialize(pDeviceVk, ShaderStages, m_ShaderResourceLayouts, GetRawAllocator(), - m_Desc.ResourceLayout, m_PipelineLayout, - (CreateInfo.Flags & PSO_CREATE_FLAG_IGNORE_MISSING_VARIABLES) == 0, - (CreateInfo.Flags & PSO_CREATE_FLAG_IGNORE_MISSING_IMMUTABLE_SAMPLERS) == 0); - m_PipelineLayout.Finalize(LogicalDevice); - - if (m_Desc.SRBAllocationGranularity > 1) + if (SignatureCount == 0 || CreateInfo.ppResourceSignatures == nullptr) { - std::array ShaderVariableDataSizes = {}; - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) - { - const SHADER_RESOURCE_VARIABLE_TYPE AllowedVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; + using ResourceNameToIndex_t = std::unordered_map; - Uint32 UnusedNumVars = 0; - ShaderVariableDataSizes[s] = ShaderVariableManagerVk::GetRequiredMemorySize(m_ShaderResourceLayouts[s], AllowedVarTypes, _countof(AllowedVarTypes), UnusedNumVars); + std::vector Resources; + ResourceNameToIndex_t UniqueNames; + const PipelineResourceLayoutDesc& LayoutDesc = CreateInfo.PSODesc.ResourceLayout; + + for (auto& Stage : ShaderStages) + { + UniqueNames.clear(); + for (auto* pShader : Stage.Shaders) + { + pShader->GetShaderResources().ProcessResources( + [&](const SPIRVShaderResourceAttribs& Res, Uint32) // + { + bool IsNewResource = UniqueNames.emplace(HashMapStringKey{Res.Name}, static_cast(Resources.size())).second; + if (IsNewResource) + { + Resources.emplace_back(Res.Name, Res.ArraySize, Res.GetShaderResourceType(Res.Type), Stage.Type, CreateInfo.PSODesc.ResourceLayout.DefaultVariableType); + } + }); + + 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; + } + } + } } - Uint32 NumSets = 0; - auto DescriptorSetSizes = m_PipelineLayout.GetDescriptorSetSizes(NumSets); - auto CacheMemorySize = ShaderResourceCacheVk::GetRequiredMemorySize(NumSets, DescriptorSetSizes.data()); + PipelineResourceSignatureDesc ResSignDesc; + ResSignDesc.Resources = Resources.data(); + ResSignDesc.NumResources = static_cast(Resources.size()); + ResSignDesc.ImmutableSamplers = LayoutDesc.ImmutableSamplers; + ResSignDesc.NumImmutableSamplers = LayoutDesc.NumImmutableSamplers; + ResSignDesc.BindingIndex = 0; - m_SRBMemAllocator.Initialize(m_Desc.SRBAllocationGranularity, GetNumShaderStages(), ShaderVariableDataSizes.data(), 1, &CacheMemorySize); + GetDevice()->CreatePipelineResourceSignature(ResSignDesc, &TempSignature); + if (TempSignature == nullptr) + LOG_ERROR_AND_THROW(""); + + Signatures[0] = TempSignature; + SignatureCount = 1; } - m_HasStaticResources = false; - m_HasNonStaticResources = false; - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) + m_PipelineLayout = GetDevice()->GetPipelineLayoutCache().GetLayout(Signatures.data(), SignatureCount); + + // verify that pipeline layout is compatible with shader resources and + // remap resource bindings + for (size_t s = 0; s < ShaderStages.size(); ++s) { - const auto& Layout = m_ShaderResourceLayouts[s]; - if (Layout.GetResourceCount(SHADER_RESOURCE_VARIABLE_TYPE_STATIC) != 0) - m_HasStaticResources = true; + const auto& Shaders = ShaderStages[s].Shaders; + auto& SPIRVs = ShaderStages[s].SPIRVs; + const auto ShaderType = ShaderStages[s].Type; - if (Layout.GetResourceCount(SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) != 0 || - Layout.GetResourceCount(SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) != 0) - m_HasNonStaticResources = true; - } + VERIFY_EXPR(Shaders.size() == SPIRVs.size()); - m_ShaderResourceLayoutHash = m_PipelineLayout.GetHash(); + for (size_t i = 0; i < Shaders.size(); ++i) + { + auto* pShader = Shaders[i]; + auto& SPIRV = SPIRVs[i]; + + pShader->GetShaderResources().ProcessResources( + [&](const SPIRVShaderResourceAttribs& Res, Uint32) // + { + PipelineLayoutVk::ResourceInfo 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 '", + m_Desc.Name, "'."); + } + SPIRV[Res.BindingDecorationOffset] = Info.BindingIndex; + SPIRV[Res.DescriptorSetDecorationOffset] = Info.DescrSetIndex; + }); + } + } } template @@ -656,19 +729,12 @@ PipelineStateVkImpl::TShaderStages PipelineStateVkImpl::InitInternalObjects( std::vector& vkShaderStages, std::vector& ShaderModules) { - m_ResourceLayoutIndex.fill(-1); - TShaderStages ShaderStages; ExtractShaders(CreateInfo, ShaderStages); - FixedLinearAllocator MemPool{GetRawAllocator()}; - - const auto NumShaderStages = GetNumShaderStages(); - VERIFY_EXPR(NumShaderStages > 0 && NumShaderStages == ShaderStages.size()); + InitPipelineLayout(CreateInfo, ShaderStages); - MemPool.AddSpace(NumShaderStages); - MemPool.AddSpace(NumShaderStages * 2); - MemPool.AddSpace(NumShaderStages); + FixedLinearAllocator MemPool{GetRawAllocator()}; ReserveSpaceForPipelineDesc(CreateInfo, MemPool); @@ -676,38 +742,23 @@ PipelineStateVkImpl::TShaderStages PipelineStateVkImpl::InitInternalObjects( const auto& LogicalDevice = GetDevice()->GetLogicalDevice(); - m_StaticResCaches = MemPool.ConstructArray(NumShaderStages, ShaderResourceCacheVk::DbgCacheContentType::StaticShaderResources); - - // The memory is now owned by PipelineStateVkImpl and will be freed by Destruct(). - auto* Ptr = MemPool.ReleaseOwnership(); - VERIFY_EXPR(Ptr == m_StaticResCaches); - (void)Ptr; - - m_ShaderResourceLayouts = MemPool.ConstructArray(NumShaderStages * 2, LogicalDevice); - - m_StaticVarsMgrs = MemPool.Allocate(NumShaderStages); - for (Uint32 s = 0; s < NumShaderStages; ++s) - new (m_StaticVarsMgrs + s) ShaderVariableManagerVk{*this, m_StaticResCaches[s]}; - 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); + //InitResourceLayouts(CreateInfo, ShaderStages); // Create shader modules and initialize shader stages InitPipelineShaderStages(LogicalDevice, ShaderStages, ShaderModules, vkShaderStages); + m_pRawMem = MemPool.ReleaseOwnership(); + return ShaderStages; } - -PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, - RenderDeviceVkImpl* pDeviceVk, - const GraphicsPipelineStateCreateInfo& CreateInfo) : - TPipelineStateBase{pRefCounters, pDeviceVk, CreateInfo}, - m_SRBMemAllocator{GetRawAllocator()} +PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, const GraphicsPipelineStateCreateInfo& CreateInfo) : + TPipelineStateBase{pRefCounters, pDeviceVk, CreateInfo} { try { @@ -716,7 +767,7 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* 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 (...) { @@ -725,12 +776,8 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* } } - -PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, - RenderDeviceVkImpl* pDeviceVk, - const ComputePipelineStateCreateInfo& CreateInfo) : - TPipelineStateBase{pRefCounters, pDeviceVk, CreateInfo}, - m_SRBMemAllocator{GetRawAllocator()} +PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, const ComputePipelineStateCreateInfo& CreateInfo) : + TPipelineStateBase{pRefCounters, pDeviceVk, CreateInfo} { try { @@ -739,7 +786,7 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* p InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules); - CreateComputePipeline(pDeviceVk, vkShaderStages, m_PipelineLayout, m_Desc, m_Pipeline); + CreateComputePipeline(pDeviceVk, vkShaderStages, *m_PipelineLayout, m_Desc, m_Pipeline); } catch (...) { @@ -748,11 +795,8 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* p } } -PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, - RenderDeviceVkImpl* pDeviceVk, - const RayTracingPipelineStateCreateInfo& CreateInfo) : - TPipelineStateBase{pRefCounters, pDeviceVk, CreateInfo}, - m_SRBMemAllocator{GetRawAllocator()} +PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, const RayTracingPipelineStateCreateInfo& CreateInfo) : + TPipelineStateBase{pRefCounters, pDeviceVk, CreateInfo} { try { @@ -765,7 +809,7 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* 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."); @@ -791,9 +835,16 @@ 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) + { + RawAllocator.Free(m_pRawMem); + m_pRawMem = nullptr; + } + + // AZ TODO: + /* for (Uint32 s = 0; s < GetNumShaderStages(); ++s) { if (m_StaticVarsMgrs != nullptr) @@ -802,253 +853,22 @@ void PipelineStateVkImpl::Destruct() m_StaticVarsMgrs[s].~ShaderVariableManagerVk(); } - if (m_ShaderResourceLayouts != nullptr) - { - m_ShaderResourceLayouts[s].~ShaderResourceLayoutVk(); - m_ShaderResourceLayouts[GetNumShaderStages() + s].~ShaderResourceLayoutVk(); - } - if (m_StaticResCaches != nullptr) { m_StaticResCaches[s].~ShaderResourceCacheVk(); } } - - // All internal objects are allocted in contiguous chunks of memory. - if (void* pRawMem = m_StaticResCaches) - { - RawAllocator.Free(pRawMem); - } -} - -void PipelineStateVkImpl::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)); + */ } bool PipelineStateVkImpl::IsCompatibleWith(const IPipelineState* pPSO) const { - VERIFY_EXPR(pPSO != nullptr); - - if (pPSO == this) - return true; - - const PipelineStateVkImpl* pPSOVk = ValidatedCast(pPSO); - if (m_ShaderResourceLayoutHash != pPSOVk->m_ShaderResourceLayoutHash) - return false; - - auto IsSamePipelineLayout = m_PipelineLayout.IsSameAs(pPSOVk->m_PipelineLayout); -#ifdef DILIGENT_DEBUG - { - bool IsCompatibleShaders = true; - if (GetNumShaderStages() != pPSOVk->GetNumShaderStages()) - IsCompatibleShaders = false; - - if (IsCompatibleShaders) - { - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) - { - if (GetShaderStageType(s) != pPSOVk->GetShaderStageType(s)) - { - IsCompatibleShaders = false; - break; - } - - const auto& Res0 = GetShaderResLayout(s); - const auto& Res1 = pPSOVk->GetShaderResLayout(s); - if (!Res0.IsCompatibleWith(Res1)) - { - IsCompatibleShaders = false; - break; - } - } - } - - if (IsCompatibleShaders) - VERIFY(IsSamePipelineLayout, "Compatible shaders must have same pipeline layouts"); - } -#endif - - return IsSamePipelineLayout; -} - - -void PipelineStateVkImpl::CommitAndTransitionShaderResources(IShaderResourceBinding* pShaderResourceBinding, - DeviceContextVkImpl* pCtxVkImpl, - bool CommitResources, - RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, - PipelineLayout::DescriptorSetBindInfo* pDescrSetBindInfo) const -{ - VERIFY(CommitResources || StateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION, "Resources should be transitioned or committed or both"); - - if (!m_HasStaticResources && !m_HasNonStaticResources) - return; - -#ifdef DILIGENT_DEVELOPMENT - if (pShaderResourceBinding == nullptr) - { - LOG_ERROR_MESSAGE("Pipeline state '", m_Desc.Name, "' requires shader resource binding object to ", - (CommitResources ? "commit" : "transition"), " resources, but none is provided."); - return; - } -#endif - - auto* pResBindingVkImpl = ValidatedCast(pShaderResourceBinding); - auto& ResourceCache = pResBindingVkImpl->GetResourceCache(); - -#ifdef DILIGENT_DEVELOPMENT - { - auto* pRefPSO = pResBindingVkImpl->GetPipelineState(); - if (IsIncompatibleWith(pRefPSO)) - { - LOG_ERROR_MESSAGE("Shader resource binding is incompatible with the pipeline state '", m_Desc.Name, "'. Operation will be ignored."); - return; - } - } - - if (CommitResources) - { - if (m_HasStaticResources && !pResBindingVkImpl->StaticResourcesInitialized()) - { - LOG_ERROR_MESSAGE("Static resources have not been initialized in the shader resource binding object being committed for PSO '", m_Desc.Name, "'. Please call IShaderResourceBinding::InitializeStaticResources()."); - } - - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) - { - m_ShaderResourceLayouts[s].dvpVerifyBindings(ResourceCache); - } - } -#endif -#ifdef DILIGENT_DEBUG - ResourceCache.DbgVerifyDynamicBuffersCounter(); -#endif - - if (StateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION) - { - ResourceCache.TransitionResources(pCtxVkImpl); - } -#ifdef DILIGENT_DEVELOPMENT - else if (StateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_VERIFY) - { - ResourceCache.TransitionResources(pCtxVkImpl); - } -#endif - - if (CommitResources) - { - VkDescriptorSet DynamicDescrSet = VK_NULL_HANDLE; - auto DynamicDescriptorSetVkLayout = m_PipelineLayout.GetDynamicDescriptorSetVkLayout(); - if (DynamicDescriptorSetVkLayout != VK_NULL_HANDLE) - { - const char* DynamicDescrSetName = "Dynamic Descriptor Set"; -#ifdef DILIGENT_DEVELOPMENT - std::string _DynamicDescrSetName(m_Desc.Name); - _DynamicDescrSetName.append(" - dynamic set"); - DynamicDescrSetName = _DynamicDescrSetName.c_str(); -#endif - // Allocate vulkan descriptor set for dynamic resources - DynamicDescrSet = pCtxVkImpl->AllocateDynamicDescriptorSet(DynamicDescriptorSetVkLayout, DynamicDescrSetName); - // Commit all dynamic resource descriptors - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) - { - const auto& Layout = m_ShaderResourceLayouts[s]; - if (Layout.GetResourceCount(SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) != 0) - Layout.CommitDynamicResources(ResourceCache, DynamicDescrSet); - } - } - - - VkPipelineBindPoint BindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM; - switch (m_Desc.PipelineType) - { - // clang-format off - case PIPELINE_TYPE_GRAPHICS: - case PIPELINE_TYPE_MESH: BindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; break; - case PIPELINE_TYPE_COMPUTE: BindPoint = VK_PIPELINE_BIND_POINT_COMPUTE; break; - case PIPELINE_TYPE_RAY_TRACING: BindPoint = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR; break; - // clang-format on - default: UNEXPECTED("Unknown pipeline type"); - } - - VERIFY_EXPR(pDescrSetBindInfo != nullptr); - // Prepare descriptor sets, and also bind them if there are no dynamic descriptors - m_PipelineLayout.PrepareDescriptorSets(pCtxVkImpl, BindPoint, ResourceCache, *pDescrSetBindInfo, DynamicDescrSet); - // Dynamic descriptor sets are not released individually. Instead, all dynamic descriptor pools - // are released at the end of the frame by DeviceContextVkImpl::FinishFrame(). - } + return m_PipelineLayout == ValidatedCast(pPSO)->m_PipelineLayout; } void PipelineStateVkImpl::BindStaticResources(Uint32 ShaderFlags, IResourceMapping* pResourceMapping, Uint32 Flags) { - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) - { - auto ShaderType = GetStaticShaderResLayout(s).GetShaderType(); - if ((ShaderType & ShaderFlags) != 0) - { - auto& StaticVarMgr = GetStaticVarMgr(s); - StaticVarMgr.BindResources(pResourceMapping, Flags); - } - } -} - -Uint32 PipelineStateVkImpl::GetStaticVariableCount(SHADER_TYPE ShaderType) const -{ - const auto LayoutInd = GetStaticVariableCountHelper(ShaderType, m_ResourceLayoutIndex); - if (LayoutInd < 0) - return 0; - - auto& StaticVarMgr = GetStaticVarMgr(LayoutInd); - return StaticVarMgr.GetVariableCount(); -} - -IShaderResourceVariable* PipelineStateVkImpl::GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) -{ - const auto LayoutInd = GetStaticVariableByNameHelper(ShaderType, Name, m_ResourceLayoutIndex); - if (LayoutInd < 0) - return nullptr; - - auto& StaticVarMgr = GetStaticVarMgr(LayoutInd); - return StaticVarMgr.GetVariable(Name); -} - -IShaderResourceVariable* PipelineStateVkImpl::GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) -{ - const auto LayoutInd = GetStaticVariableByIndexHelper(ShaderType, Index, m_ResourceLayoutIndex); - if (LayoutInd < 0) - return nullptr; - - const auto& StaticVarMgr = GetStaticVarMgr(LayoutInd); - return StaticVarMgr.GetVariable(Index); -} - - -void PipelineStateVkImpl::InitializeStaticSRBResources(ShaderResourceCacheVk& ResourceCache) const -{ - for (Uint32 s = 0; s < GetNumShaderStages(); ++s) - { - const auto& StaticResLayout = GetStaticShaderResLayout(s); - const auto& StaticResCache = GetStaticResCache(s); - -#ifdef DILIGENT_DEVELOPMENT - if (!StaticResLayout.dvpVerifyBindings(StaticResCache)) - { - LOG_ERROR_MESSAGE("Static resources in SRB of PSO '", GetDesc().Name, - "' will not be successfully initialized because not all static resource bindings in shader '", - GetShaderTypeLiteralName(GetShaderStageType(s)), - "' are valid. Please make sure you bind all static resources to PSO before calling InitializeStaticResources() " - "directly or indirectly by passing InitStaticResources=true to CreateShaderResourceBinding() method."); - } -#endif - const auto& ShaderResourceLayouts = GetShaderResLayout(s); - ShaderResourceLayouts.InitializeStaticResources(StaticResLayout, StaticResCache, ResourceCache); - } -#ifdef DILIGENT_DEBUG - ResourceCache.DbgVerifyDynamicBuffersCounter(); -#endif + //m_StaticVarsMgr.BindResources(ShaderFlags, pResourceMapping, Flags); } } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp index 90c1a88e..cc1a444c 100644 --- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp @@ -42,6 +42,7 @@ #include "BottomLevelASVkImpl.hpp" #include "TopLevelASVkImpl.hpp" #include "ShaderBindingTableVkImpl.hpp" +#include "PipelineResourceSignatureVkImpl.hpp" #include "EngineMemory.h" namespace Diligent @@ -82,6 +83,7 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* sizeof(BottomLevelASVkImpl), sizeof(TopLevelASVkImpl), sizeof(ShaderBindingTableVkImpl), + sizeof(PipelineResourceSignatureVkImpl), } }, m_VulkanInstance {Instance }, @@ -89,6 +91,7 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* m_LogicalVkDevice {std::move(LogicalDevice) }, m_EngineAttribs {EngineCI }, m_FramebufferCache {*this }, + m_PipelineLayoutCache {*this }, m_ImplicitRenderPassCache{*this }, m_DescriptorSetAllocator { @@ -168,11 +171,12 @@ 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"); - static_assert(sizeof(DeviceObjectSizes) == sizeof(size_t) * 15, "Please add new objects to DeviceObjectSizes constructor"); + static_assert(sizeof(DeviceObjectSizes) == sizeof(size_t) * 16, "Please add new objects to DeviceObjectSizes constructor"); // set device properties { @@ -827,4 +831,22 @@ void RenderDeviceVkImpl::CreateSBT(const ShaderBindingTableDesc& Desc, }); } +void RenderDeviceVkImpl::CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature) +{ + CreateDeviceObject("PipelineResourceSignature", Desc, ppSignature, + [&]() // + { + PipelineResourceSignatureVkImpl* pPRSVk(NEW_RC_OBJ(m_PipeResSignAllocator, "PipelineResourceSignatureVkImpl instance", PipelineResourceSignatureVkImpl)(this, Desc)); + 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 289ca960..3672853f 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp @@ -35,14 +35,14 @@ namespace Diligent { -ShaderResourceBindingVkImpl::ShaderResourceBindingVkImpl(IReferenceCounters* pRefCounters, - PipelineStateVkImpl* pPSO, - bool IsPSOInternal) : +ShaderResourceBindingVkImpl::ShaderResourceBindingVkImpl(IReferenceCounters* pRefCounters, + PipelineResourceSignatureVkImpl* pPRS, + bool IsPSOInternal) : // clang-format off TBase { pRefCounters, - pPSO, + pPRS, IsPSOInternal }, m_ShaderResourceCache{ShaderResourceCacheVk::DbgCacheContentType::SRBResources} @@ -50,9 +50,9 @@ ShaderResourceBindingVkImpl::ShaderResourceBindingVkImpl(IReferenceCounters* pR { try { - m_ResourceLayoutIndex.fill(-1); + m_ShaderVarIndex.fill(-1); - m_NumShaders = static_cast(pPSO->GetNumShaderStages()); + m_NumShaders = static_cast(pPRS->GetNumShaderStages()); FixedLinearAllocator MemPool{GetRawAllocator()}; MemPool.AddSpace(m_NumShaders); @@ -67,29 +67,28 @@ ShaderResourceBindingVkImpl::ShaderResourceBindingVkImpl(IReferenceCounters* pR // It is important to construct all objects before initializing them because if an exception is thrown, // destructors will be called for all objects - auto* pRenderDeviceVkImpl = pPSO->GetDevice(); // This will only allocate memory and initialize descriptor sets in the resource cache // Resources will be initialized by InitializeResourceMemoryInCache() - auto& ResourceCacheDataAllocator = pPSO->GetSRBMemoryAllocator().GetResourceCacheDataAllocator(0); - pPSO->GetPipelineLayout().InitResourceCache(pRenderDeviceVkImpl, m_ShaderResourceCache, ResourceCacheDataAllocator, pPSO->GetDesc().Name); + auto& SRBMemAllocator = pPRS->GetSRBMemoryAllocator(); + auto& ResourceCacheDataAllocator = SRBMemAllocator.GetResourceCacheDataAllocator(0); + pPRS->InitResourceCache(m_ShaderResourceCache, ResourceCacheDataAllocator, pPRS->GetDesc().Name); for (Uint32 s = 0; s < m_NumShaders; ++s) { - auto ShaderInd = GetShaderTypePipelineIndex(pPSO->GetShaderStageType(s), pPSO->GetDesc().PipelineType); + auto ShaderInd = GetShaderTypePipelineIndex(pPRS->GetShaderStageType(s), pPRS->GetPipelineType()); - m_ResourceLayoutIndex[ShaderInd] = static_cast(s); + m_ShaderVarIndex[ShaderInd] = static_cast(s); - auto& VarDataAllocator = pPSO->GetSRBMemoryAllocator().GetShaderVariableDataAllocator(s); + auto& VarDataAllocator = SRBMemAllocator.GetShaderVariableDataAllocator(s); - const auto& SrcLayout = pPSO->GetShaderResLayout(s); // Use source layout to initialize resource memory in the cache - SrcLayout.InitializeResourceMemoryInCache(m_ShaderResourceCache); + 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(SrcLayout, VarDataAllocator, VarTypes, _countof(VarTypes)); + m_pShaderVarMgrs[s].Initialize(*pPRS, VarDataAllocator, VarTypes, _countof(VarTypes)); } #ifdef DILIGENT_DEBUG m_ShaderResourceCache.DbgVerifyResourceInitialization(); @@ -111,7 +110,7 @@ void ShaderResourceBindingVkImpl::Destruct() { if (m_pShaderVarMgrs != nullptr) { - auto& SRBMemAllocator = m_pPSO->GetSRBMemoryAllocator(); + auto& SRBMemAllocator = GetSignature()->GetSRBMemoryAllocator(); for (Uint32 s = 0; s < m_NumShaders; ++s) { auto& VarDataAllocator = SRBMemAllocator.GetShaderVariableDataAllocator(s); @@ -125,50 +124,37 @@ void ShaderResourceBindingVkImpl::Destruct() void ShaderResourceBindingVkImpl::BindResources(Uint32 ShaderFlags, IResourceMapping* pResMapping, Uint32 Flags) { - const auto PipelineType = m_pPSO->GetDesc().PipelineType; - for (Int32 ShaderInd = 0; ShaderInd < static_cast(m_ResourceLayoutIndex.size()); ++ShaderInd) - { - auto ResLayoutInd = m_ResourceLayoutIndex[ShaderInd]; - if (ResLayoutInd >= 0) - { - // ShaderInd is the shader type pipeline index here - const auto ShaderType = GetShaderTypeFromPipelineIndex(ShaderInd, PipelineType); - if (ShaderFlags & ShaderType) - { - m_pShaderVarMgrs[ResLayoutInd].BindResources(pResMapping, Flags); - } - } - } + // AZ TODO } -IShaderResourceVariable* ShaderResourceBindingVkImpl::GetVariableByName(SHADER_TYPE ShaderType, const char* Name) +Uint32 ShaderResourceBindingVkImpl::GetVariableCount(SHADER_TYPE ShaderType) const { - auto ResLayoutInd = GetVariableByNameHelper(ShaderType, Name, m_ResourceLayoutIndex); - if (ResLayoutInd < 0) - return nullptr; + const auto VarMngrInd = GetVariableCountHelper(ShaderType, m_ShaderVarIndex); + if (VarMngrInd < 0) + return 0; - VERIFY_EXPR(static_cast(ResLayoutInd) < Uint32{m_NumShaders}); - return m_pShaderVarMgrs[ResLayoutInd].GetVariable(Name); + auto& ShaderVarMgr = m_pShaderVarMgrs[VarMngrInd]; + return ShaderVarMgr.GetVariableCount(); } -Uint32 ShaderResourceBindingVkImpl::GetVariableCount(SHADER_TYPE ShaderType) const +IShaderResourceVariable* ShaderResourceBindingVkImpl::GetVariableByName(SHADER_TYPE ShaderType, const Char* Name) { - auto ResLayoutInd = GetVariableCountHelper(ShaderType, m_ResourceLayoutIndex); - if (ResLayoutInd < 0) - return 0; + const auto VarMngrInd = GetVariableByNameHelper(ShaderType, Name, m_ShaderVarIndex); + if (VarMngrInd < 0) + return nullptr; - VERIFY_EXPR(static_cast(ResLayoutInd) < Uint32{m_NumShaders}); - return m_pShaderVarMgrs[ResLayoutInd].GetVariableCount(); + auto& ShaderVarMgr = m_pShaderVarMgrs[VarMngrInd]; + return ShaderVarMgr.GetVariable(Name); } IShaderResourceVariable* ShaderResourceBindingVkImpl::GetVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) { - auto ResLayoutInd = GetVariableByIndexHelper(ShaderType, Index, m_ResourceLayoutIndex); - if (ResLayoutInd < 0) + const auto VarMngrInd = GetVariableByIndexHelper(ShaderType, Index, m_ShaderVarIndex); + if (VarMngrInd < 0) return nullptr; - VERIFY_EXPR(static_cast(ResLayoutInd) < Uint32{m_NumShaders}); - return m_pShaderVarMgrs[ResLayoutInd].GetVariable(Index); + const auto& ShaderVarMgr = m_pShaderVarMgrs[VarMngrInd]; + return ShaderVarMgr.GetVariable(Index); } void ShaderResourceBindingVkImpl::InitializeStaticResources(const IPipelineState* pPipelineState) @@ -181,17 +167,28 @@ void ShaderResourceBindingVkImpl::InitializeStaticResources(const IPipelineState if (pPipelineState == nullptr) { - pPipelineState = GetPipelineState(); + InitializeStaticResourcesWithSignature(nullptr); } else { - DEV_CHECK_ERR(pPipelineState->IsCompatibleWith(GetPipelineState()), "The pipeline state '", pPipelineState->GetDesc().Name, - "' is not compatible with the pipeline state '", m_pPSO->GetDesc().Name, - "' this SRB was created from and cannot be used to initialize static resources."); + auto* pSign = pPipelineState->GetResourceSignature(GetBindingIndex()); + if (pSign == nullptr) + { + LOG_ERROR_MESSAGE("Shader resource binding is not compatible with pipeline state."); + return; + } + + InitializeStaticResourcesWithSignature(pSign); } +} + +void ShaderResourceBindingVkImpl::InitializeStaticResourcesWithSignature(const IPipelineResourceSignature* pResourceSignature) +{ + if (pResourceSignature == nullptr) + pResourceSignature = GetPipelineResourceSignature(); - auto* pPSOVK = ValidatedCast(pPipelineState); - pPSOVK->InitializeStaticSRBResources(m_ShaderResourceCache); + auto* pPRSVk = ValidatedCast(pResourceSignature); + pPRSVk->InitializeStaticSRBResources(m_ShaderResourceCache); m_bStaticResourcesInitialized = true; } diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index f7da5b14..820d1e9a 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -39,7 +39,7 @@ namespace Diligent { -size_t ShaderResourceCacheVk::GetRequiredMemorySize(Uint32 NumSets, Uint32 SetSizes[]) +size_t ShaderResourceCacheVk::GetRequiredMemorySize(Uint32 NumSets, const Uint32* SetSizes) { Uint32 TotalResources = 0; for (Uint32 t = 0; t < NumSets; ++t) @@ -48,7 +48,7 @@ size_t ShaderResourceCacheVk::GetRequiredMemorySize(Uint32 NumSets, Uint32 SetSi return MemorySize; } -void ShaderResourceCacheVk::InitializeSets(IMemoryAllocator& MemAllocator, Uint32 NumSets, Uint32 SetSizes[]) +void ShaderResourceCacheVk::InitializeSets(IMemoryAllocator& MemAllocator, Uint32 NumSets, const Uint32* SetSizes) { // Memory layout: // @@ -89,7 +89,7 @@ void ShaderResourceCacheVk::InitializeSets(IMemoryAllocator& MemAllocator, Uint3 } } -void ShaderResourceCacheVk::InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, SPIRVShaderResourceAttribs::ResourceType Type) +void ShaderResourceCacheVk::InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, VkDescriptorType Type) { auto& DescrSet = GetDescriptorSet(Set); for (Uint32 res = 0; res < ArraySize; ++res) @@ -117,15 +117,16 @@ void ShaderResourceCacheVk::DbgVerifyDynamicBuffersCounter() const for (Uint32 res = 0; res < m_TotalResources; ++res) { const auto& Res = pResources[res]; - if (Res.Type == SPIRVShaderResourceAttribs::ResourceType::UniformBuffer) + 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 == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer || - Res.Type == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer || - Res.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer || - Res.Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer) + 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) { if (Res.pObject && Res.pObject.RawPtr()->GetBuffer()->GetDesc().Usage == USAGE_DYNAMIC) ++NumDynamicBuffers; @@ -156,10 +157,10 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) for (Uint32 res = 0; res < m_TotalResources; ++res) { auto& Res = pResources[res]; - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type below"); switch (Res.Type) { - case SPIRVShaderResourceAttribs::ResourceType::UniformBuffer: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { auto* pBufferVk = Res.pObject.RawPtr(); if (pBufferVk != nullptr && pBufferVk->IsInKnownState()) @@ -191,18 +192,17 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer: - case SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer: - case SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer: - case SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer: + 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: { auto* pBuffViewVk = Res.pObject.RawPtr(); auto* pBufferVk = pBuffViewVk != nullptr ? ValidatedCast(pBuffViewVk->GetBuffer()) : nullptr; if (pBufferVk != nullptr && pBufferVk->IsInKnownState()) { const RESOURCE_STATE RequiredState = - (Res.Type == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer || - Res.Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer) ? + Res.Type != VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ? // AZ TODO: read-only buffer ??? RESOURCE_STATE_UNORDERED_ACCESS : RESOURCE_STATE_SHADER_RESOURCE; #ifdef DILIGENT_DEBUG @@ -239,9 +239,9 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case SPIRVShaderResourceAttribs::ResourceType::SeparateImage: - case SPIRVShaderResourceAttribs::ResourceType::SampledImage: - case SPIRVShaderResourceAttribs::ResourceType::StorageImage: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: { auto* pTextureViewVk = Res.pObject.RawPtr(); auto* pTextureVk = pTextureViewVk != nullptr ? ValidatedCast(pTextureViewVk->GetTexture()) : nullptr; @@ -253,7 +253,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 == SPIRVShaderResourceAttribs::ResourceType::StorageImage) + if (Res.Type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { RequiredState = RESOURCE_STATE_UNORDERED_ACCESS; VERIFY_EXPR(ResourceStateToVkImageLayout(RequiredState) == VK_IMAGE_LAYOUT_GENERAL); @@ -302,19 +302,13 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case SPIRVShaderResourceAttribs::ResourceType::AtomicCounter: - { - // Nothing to do with atomic counters - } - break; - - case SPIRVShaderResourceAttribs::ResourceType::SeparateSampler: + case VK_DESCRIPTOR_TYPE_SAMPLER: { // Nothing to do with samplers } break; - case SPIRVShaderResourceAttribs::ResourceType::InputAttachment: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { // 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 @@ -322,7 +316,7 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; - case SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure: + case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { auto* pTLASVk = Res.pObject.RawPtr(); if (pTLASVk != nullptr && pTLASVk->IsInKnownState()) @@ -367,7 +361,11 @@ template void ShaderResourceCacheVk::TransitionResources(DeviceContextVkIm VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetUniformBufferDescriptorWriteInfo() const { - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::UniformBuffer, "Uniform buffer resource is expected"); + // clang-format off + VERIFY(Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || + Type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + "Uniform buffer resource is expected"); + // clang-format on DEV_CHECK_ERR(pObject != nullptr, "Unable to get uniform buffer write info: cached object is null"); auto* pBuffVk = pObject.RawPtr(); @@ -387,8 +385,10 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetUniformBufferDescript VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescriptorWriteInfo() const { // clang-format off - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer || - Type == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer, + 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, "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 +399,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) + /*if (Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer) // AZ TODO { // 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 +407,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 == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer) + else if (Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || Type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) { 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 +417,7 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescript else { UNEXPECTED("Unexpected resource type"); - } + }*/ VkDescriptorBufferInfo DescrBuffInfo; DescrBuffInfo.buffer = pBuffVk->GetVkBuffer(); @@ -431,23 +431,23 @@ VkDescriptorBufferInfo ShaderResourceCacheVk::Resource::GetStorageBufferDescript VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetImageDescriptorWriteInfo(bool IsImmutableSampler) const { // clang-format off - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::StorageImage || - Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage || - Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage, + VERIFY(Type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || + Type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || + Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, "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 == SPIRVShaderResourceAttribs::ResourceType::StorageImage; + bool IsStorageImage = Type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; 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 == SPIRVShaderResourceAttribs::ResourceType::SampledImage || !IsImmutableSampler, + VERIFY(Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || !IsImmutableSampler, "Immutable sampler can't be assigned to separarate image or storage image"); - if (Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage && !IsImmutableSampler) + if (Type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER && !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 +488,8 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetImageDescriptorWriteIn VkBufferView ShaderResourceCacheVk::Resource::GetBufferViewWriteInfo() const { // clang-format off - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer || - Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer, + VERIFY(Type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || + Type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, "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 +503,7 @@ VkBufferView ShaderResourceCacheVk::Resource::GetBufferViewWriteInfo() const VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWriteInfo() const { - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler, "Separate sampler resource is expected"); + VERIFY(Type == VK_DESCRIPTOR_TYPE_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 +518,7 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWrite VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetInputAttachmentDescriptorWriteInfo() const { - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::InputAttachment, "Input attachment resource is expected"); + VERIFY(Type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, "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 +534,7 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetInputAttachmentDescrip VkWriteDescriptorSetAccelerationStructureKHR ShaderResourceCacheVk::Resource::GetAccelerationStructureWriteInfo() const { - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure, "Acceleration structure resource is expected"); + VERIFY(Type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, "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/ShaderResourceLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp deleted file mode 100644 index 1c6d5f24..00000000 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp +++ /dev/null @@ -1,1598 +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 "pch.h" - -#include "ShaderResourceLayoutVk.hpp" -#include "ShaderResourceCacheVk.hpp" -#include "BufferVkImpl.hpp" -#include "BufferViewVk.h" -#include "TextureVkImpl.hpp" -#include "TextureViewVkImpl.hpp" -#include "SamplerVkImpl.hpp" -#include "ShaderVkImpl.hpp" -#include "PipelineLayout.hpp" -#include "ShaderResourceVariableBase.hpp" -#include "StringTools.hpp" -#include "PipelineStateVkImpl.hpp" -#include "TopLevelASVkImpl.hpp" - -namespace Diligent -{ - -static constexpr auto RAY_TRACING_SHADER_TYPES = - SHADER_TYPE_RAY_GEN | - SHADER_TYPE_RAY_MISS | - SHADER_TYPE_RAY_CLOSEST_HIT | - SHADER_TYPE_RAY_ANY_HIT | - SHADER_TYPE_RAY_INTERSECTION | - SHADER_TYPE_CALLABLE; - -static Int32 FindImmutableSampler(SHADER_TYPE ShaderType, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - const SPIRVShaderResourceAttribs& Attribs, - const char* SamplerSuffix) -{ - if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage) - { - SamplerSuffix = nullptr; - } - else if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler) - { - // 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 < ResourceLayoutDesc.NumImmutableSamplers; ++s) - { - const auto& ImtblSam = ResourceLayoutDesc.ImmutableSamplers[s]; - if (((ImtblSam.ShaderStages & ShaderType) != 0) && StreqSuff(Attribs.Name, ImtblSam.SamplerOrTextureName, SamplerSuffix)) - return s; - } - - return -1; -} - -static SHADER_RESOURCE_VARIABLE_TYPE FindShaderVariableType(SHADER_TYPE ShaderType, - const SPIRVShaderResourceAttribs& Attribs, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - const char* CombinedSamplerSuffix) -{ - if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler) - { - // Use texture or sampler name to derive separate sampler type - // When HLSL-style combined image samplers are not used, CombinedSamplerSuffix is null - return GetShaderVariableType(ShaderType, ResourceLayoutDesc.DefaultVariableType, ResourceLayoutDesc.Variables, ResourceLayoutDesc.NumVariables, - [&](const char* VarName) { - return StreqSuff(Attribs.Name, VarName, CombinedSamplerSuffix); - }); - } - else - { - return GetShaderVariableType(ShaderType, Attribs.Name, ResourceLayoutDesc); - } -} - - -ShaderResourceLayoutVk::ShaderStageInfo::ShaderStageInfo(const ShaderVkImpl* pShader) : - Type{pShader->GetDesc().ShaderType}, - Shaders{pShader}, - SPIRVs{pShader->GetSPIRV()} -{ -} - -void ShaderResourceLayoutVk::ShaderStageInfo::Append(const ShaderVkImpl* pShader) -{ - VERIFY_EXPR(pShader != nullptr); - VERIFY(std::find(Shaders.begin(), Shaders.end(), pShader) == Shaders.end(), - "Shader '", pShader->GetDesc().Name, "' already exists in the stage. Shaders must be deduplicated."); - - const auto NewShaderType = pShader->GetDesc().ShaderType; - if (Type == SHADER_TYPE_UNKNOWN) - { - VERIFY_EXPR(Shaders.empty() && SPIRVs.empty()); - Type = NewShaderType; - } - else - { - VERIFY(Type == NewShaderType, "The type (", GetShaderTypeLiteralName(NewShaderType), - ") of shader '", pShader->GetDesc().Name, "' being added to the stage is incosistent with the stage type (", - GetShaderTypeLiteralName(Type), ")."); - } - Shaders.push_back(pShader); - SPIRVs.push_back(pShader->GetSPIRV()); -} - -size_t ShaderResourceLayoutVk::ShaderStageInfo::Count() const -{ - VERIFY_EXPR(Shaders.size() == SPIRVs.size()); - return Shaders.size(); -} - - -ShaderResourceLayoutVk::~ShaderResourceLayoutVk() -{ - for (Uint32 r = 0; r < GetTotalResourceCount(); ++r) - GetResource(r).~VkResource(); - - for (Uint32 s = 0; s < m_NumImmutableSamplers; ++s) - GetImmutableSampler(s).~ImmutableSamplerPtrType(); -} - -StringPool ShaderResourceLayoutVk::AllocateMemory(const std::vector& Shaders, - IMemoryAllocator& Allocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - ResourceNameToIndex_t& UniqueNames, - bool AllocateImmutableSamplers) -{ - VERIFY(!m_ResourceBuffer, "Memory has already been initialized"); - VERIFY_EXPR(Shaders.size() > 0); - VERIFY_EXPR(m_ShaderType == SHADER_TYPE_UNKNOWN); - - m_ShaderType = Shaders[0]->GetDesc().ShaderType; - m_IsUsingSeparateSamplers = !Shaders[0]->GetShaderResources()->IsUsingCombinedSamplers(); - const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); - - // Construct shader or shader group name - const auto ShaderName = GetShaderGroupName(Shaders); - - size_t StringPoolSize = StringPool::GetRequiredReserveSize(ShaderName); - - // Count the number of resources to allocate all needed memory - for (size_t s = 0; s < Shaders.size(); ++s) - { - const auto& Resources = *Shaders[s]->GetShaderResources(); - const auto* CombinedSamplerSuffix = Resources.GetCombinedSamplerSuffix(); - VERIFY(Resources.GetShaderType() == m_ShaderType, "Unexpected shader type"); - VERIFY(m_IsUsingSeparateSamplers == !Resources.IsUsingCombinedSamplers(), "All shaders in the stage must either use or not use combined image samplers"); - - Resources.ProcessResources( - [&](const SPIRVShaderResourceAttribs& ResAttribs, Uint32) // - { - auto VarType = FindShaderVariableType(m_ShaderType, ResAttribs, ResourceLayoutDesc, CombinedSamplerSuffix); - if (IsAllowedType(VarType, AllowedTypeBits)) - { - bool IsNewResource = UniqueNames.emplace(HashMapStringKey{ResAttribs.Name}, Uint32{InvalidResourceIndex}).second; - if (IsNewResource) - { - StringPoolSize += StringPool::GetRequiredReserveSize(ResAttribs.Name); - - // For immutable separate samplers we still allocate VkResource instances, but they are never exposed to the app - - VERIFY(Uint32{m_NumResources[VarType]} + 1 <= Uint32{std::numeric_limits::max()}, "Number of resources exceeds Uint16 maximum representable value"); - ++m_NumResources[VarType]; - } - } - } // - ); - } - - Uint32 TotalResources = 0; - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast(VarType + 1)) - { - TotalResources += m_NumResources[VarType]; - } - VERIFY(TotalResources <= Uint32{std::numeric_limits::max()}, "Total number of resources exceeds Uint16 maximum representable value"); - m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES] = static_cast(TotalResources); - - m_NumImmutableSamplers = 0; - if (AllocateImmutableSamplers) - { - // Reserve space for all immutable samplers that may potentially be used in this - // shader stage. Note that not all samplers may actually be used/initialized. - for (Uint32 s = 0; s < ResourceLayoutDesc.NumImmutableSamplers; ++s) - { - const auto& ImtblSamDesc = ResourceLayoutDesc.ImmutableSamplers[s]; - if ((ImtblSamDesc.ShaderStages & m_ShaderType) != 0) - ++m_NumImmutableSamplers; - } - } - - FixedLinearAllocator MemPool{Allocator}; - - MemPool.AddSpace(TotalResources); - MemPool.AddSpace(m_NumImmutableSamplers); - MemPool.AddSpace(StringPoolSize); - - MemPool.Reserve(); - - auto* pResources = MemPool.Allocate(TotalResources); - auto* pImtblSamplers = MemPool.ConstructArray(m_NumImmutableSamplers); - auto* pStringData = MemPool.ConstructArray(StringPoolSize); - - m_ResourceBuffer = std::unique_ptr>(MemPool.Release(), Allocator); - - VERIFY_EXPR(pResources == nullptr || m_ResourceBuffer.get() == pResources); - VERIFY_EXPR(pImtblSamplers == nullptr || pImtblSamplers == std::addressof(GetImmutableSampler(0))); - VERIFY_EXPR(pStringData == GetStringPoolData()); - - StringPool stringPool; - stringPool.AssignMemory(pStringData, StringPoolSize); - stringPool.CopyString(ShaderName); - return stringPool; -} - - -static Uint32 FindAssignedSampler(const ShaderResourceLayoutVk& Layout, - const SPIRVShaderResources& Resources, - const SPIRVShaderResourceAttribs& SepImg, - Uint32 CurrResourceCount, - SHADER_RESOURCE_VARIABLE_TYPE ImgVarType) -{ - using VkResource = ShaderResourceLayoutVk::VkResource; - VERIFY_EXPR(SepImg.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage); - - Uint32 SamplerInd = VkResource::InvalidSamplerInd; - if (Resources.IsUsingCombinedSamplers() && SepImg.IsValidSepSamplerAssigned()) - { - const auto& SepSampler = Resources.GetAssignedSepSampler(SepImg); - for (SamplerInd = 0; SamplerInd < CurrResourceCount; ++SamplerInd) - { - const auto& Res = Layout.GetResource(ImgVarType, SamplerInd); - if (Res.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && - strcmp(Res.Name, SepSampler.Name) == 0) - { - VERIFY(ImgVarType == Res.GetVariableType(), - "The type (", GetShaderVariableTypeLiteralName(ImgVarType), ") of separate image variable '", SepImg.Name, - "' is not consistent with the type (", GetShaderVariableTypeLiteralName(Res.GetVariableType()), - ") of the separate sampler '", SepSampler.Name, - "' that is assigned to it. " - "This should never happen as when HLSL-style combined texture samplers are used, the type of the sampler " - "is derived from the type of the corresponding separate image."); - break; - } - } - if (SamplerInd == CurrResourceCount) - { - LOG_ERROR("Unable to find separate sampler '", SepSampler.Name, "' assigned to separate image '", - SepImg.Name, "' in the list of already created resources. This seems to be a bug."); - SamplerInd = VkResource::InvalidSamplerInd; - } - } - return SamplerInd; -} - -static void VerifyResourceMerge(const ShaderResourceLayoutVk::VkResource& ExistingRes, - const SPIRVShaderResourceAttribs& NewResAttribs, - SHADER_RESOURCE_VARIABLE_TYPE VarType) -{ - VERIFY(ExistingRes.VariableType == VarType, - "The type of variable '", NewResAttribs.Name, "' does not match the type determined for previous shaders. This appears to be a bug."); - - 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."); -} - -void ShaderResourceLayoutVk::InitializeStaticResourceLayout(const std::vector& Shaders, - IMemoryAllocator& LayoutDataAllocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - ShaderResourceCacheVk& StaticResourceCache) -{ - const auto AllowedVarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; - const Uint32 AllowedTypeBits = GetAllowedTypeBits(&AllowedVarType, 1); - - // A mapping from the resource name to its index in m_ResourceBuffer that is used - // to de-duplicate shader resources. - ResourceNameToIndex_t ResourceNameToIndex; - - // We do not need immutable samplers in static shader resource layout as they - // are relevant only when the main layout is initialized - constexpr bool AllocateImmutableSamplers = false; - - auto stringPool = AllocateMemory(Shaders, LayoutDataAllocator, ResourceLayoutDesc, &AllowedVarType, 1, ResourceNameToIndex, AllocateImmutableSamplers); - - std::array CurrResInd = {}; - - Uint32 StaticResCacheSize = 0; - for (const auto* pShader : Shaders) - { - const auto& Resources = *pShader->GetShaderResources(); - const auto* CombinedSamplerSuffix = Resources.GetCombinedSamplerSuffix(); - Resources.ProcessResources( - [&](const SPIRVShaderResourceAttribs& Attribs, Uint32) // - { - auto VarType = FindShaderVariableType(m_ShaderType, Attribs, ResourceLayoutDesc, CombinedSamplerSuffix); - if (!IsAllowedType(VarType, AllowedTypeBits)) - return; - - auto ResIter = ResourceNameToIndex.find(HashMapStringKey{Attribs.Name}); - VERIFY_EXPR(ResIter != ResourceNameToIndex.end()); - - if (ResIter->second == InvalidResourceIndex) - { - Int32 SrcImmutableSamplerInd = -1; - if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || - Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler) - { - // Only search for the immutable sampler for combined image samplers and separate samplers - SrcImmutableSamplerInd = FindImmutableSampler(m_ShaderType, ResourceLayoutDesc, Attribs, CombinedSamplerSuffix); - // NB: for immutable separate samplers we still allocate VkResource instances to be compliant with the main - // layout, but they are never initialized or exposed to the app. - } - - Uint32 Binding = Uint32{Attribs.Type}; - Uint32 DescriptorSet = 0; - Uint32 CacheOffset = StaticResCacheSize; - StaticResCacheSize += Attribs.ArraySize; - - Uint32 SamplerInd = VkResource::InvalidSamplerInd; - if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage) - { - // Separate samplers are enumerated before separate images, so the sampler - // assigned to this separate image must have already been created. - SamplerInd = FindAssignedSampler(*this, Resources, Attribs, CurrResInd[VarType], VarType); - } - - // add new resource - ResIter->second = CurrResInd[VarType]; - ::new (&GetResource(VarType, CurrResInd[VarType]++)) VkResource // - { - *this, - stringPool.CopyString(Attribs.Name), - Attribs, - VarType, - Binding, - DescriptorSet, - CacheOffset, - SamplerInd, - SrcImmutableSamplerInd >= 0 // - }; - } - else - { - // Merge with existing - VerifyResourceMerge(GetResource(VarType, ResIter->second), Attribs, VarType); - } - } // - ); - } - -#ifdef DILIGENT_DEBUG - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast(VarType + 1)) - { - VERIFY(CurrResInd[VarType] == m_NumResources[VarType], "Not all resources have been initialized, which will cause a crash when dtor is called"); - } - - VERIFY_EXPR(stringPool.GetRemainingSize() == 0); -#endif - - StaticResourceCache.InitializeSets(GetRawAllocator(), 1, &StaticResCacheSize); - InitializeResourceMemoryInCache(StaticResourceCache); -#ifdef DILIGENT_DEBUG - StaticResourceCache.DbgVerifyResourceInitialization(); -#endif -} - -#ifdef DILIGENT_DEVELOPMENT -void ShaderResourceLayoutVk::dvpVerifyResourceLayoutDesc(const TShaderStages& ShaderStages, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - bool VerifyVariables, - bool VerifyImmutableSamplers) -{ - auto GetAllowedShadersString = [&](SHADER_TYPE Stages) // - { - std::string ShadersStr; - while (Stages != SHADER_TYPE_UNKNOWN) - { - const auto ShaderType = Stages & static_cast(~(static_cast(Stages) - 1)); - String ShaderName; - - for (const auto& StageInfo : ShaderStages) - { - if ((Stages & StageInfo.Type) != 0) - { - ShaderName = GetShaderGroupName(StageInfo.Shaders); - break; - } - } - - if (!ShadersStr.empty()) - ShadersStr.append(", "); - ShadersStr.append(GetShaderTypeLiteralName(ShaderType)); - ShadersStr.append(" ("); - if (ShaderName.size()) - { - ShadersStr.push_back('\''); - ShadersStr.append(ShaderName); - ShadersStr.push_back('\''); - } - else - { - ShadersStr.append("Not enabled in PSO"); - } - ShadersStr.append(")"); - - Stages &= ~ShaderType; - } - return ShadersStr; - }; - - if (VerifyVariables) - { - for (Uint32 v = 0; v < ResourceLayoutDesc.NumVariables; ++v) - { - const auto& VarDesc = ResourceLayoutDesc.Variables[v]; - if (VarDesc.ShaderStages == SHADER_TYPE_UNKNOWN) - { - LOG_WARNING_MESSAGE("No allowed shader stages are specified for ", GetShaderVariableTypeLiteralName(VarDesc.Type), " variable '", VarDesc.Name, "'."); - continue; - } - - bool VariableFound = false; - for (size_t s = 0; s < ShaderStages.size() && !VariableFound; ++s) - { - const auto& Stage = ShaderStages[s]; - if ((Stage.Type & VarDesc.ShaderStages) == 0) - continue; - - for (size_t i = 0; i < Stage.Shaders.size() && !VariableFound; ++i) - { - const auto& Resources = *Stage.Shaders[i]->GetShaderResources(); - VERIFY_EXPR(Resources.GetShaderType() == Stage.Type); - - for (Uint32 res = 0; res < Resources.GetTotalResources() && !VariableFound; ++res) - { - const auto& ResAttribs = Resources.GetResource(res); - VariableFound = (strcmp(ResAttribs.Name, VarDesc.Name) == 0); - } - } - } - if (!VariableFound) - { - LOG_WARNING_MESSAGE(GetShaderVariableTypeLiteralName(VarDesc.Type), " variable '", VarDesc.Name, - "' is not found in any of the designated shader stages: ", - GetAllowedShadersString(VarDesc.ShaderStages)); - } - } - } - - if (VerifyImmutableSamplers) - { - for (Uint32 sam = 0; sam < ResourceLayoutDesc.NumImmutableSamplers; ++sam) - { - const auto& ImtblSamDesc = ResourceLayoutDesc.ImmutableSamplers[sam]; - if (ImtblSamDesc.ShaderStages == SHADER_TYPE_UNKNOWN) - { - LOG_WARNING_MESSAGE("No allowed shader stages are specified for immutable sampler '", ImtblSamDesc.SamplerOrTextureName, "'."); - continue; - } - - bool SamplerFound = false; - for (size_t s = 0; s < ShaderStages.size() && !SamplerFound; ++s) - { - const auto& Stage = ShaderStages[s]; - if ((Stage.Type & ImtblSamDesc.ShaderStages) == 0) - continue; - - for (size_t j = 0; j < Stage.Shaders.size() && !SamplerFound; ++j) - { - const auto& Resources = *Stage.Shaders[j]->GetShaderResources(); - VERIFY_EXPR(Resources.GetShaderType() == Stage.Type); - - // Irrespective of whether HLSL-style combined image samplers are used, - // an immutable sampler can be assigned to GLSL sampled image (i.e. sampler2D g_tex) - for (Uint32 i = 0; i < Resources.GetNumSmpldImgs() && !SamplerFound; ++i) - { - const auto& SmplImg = Resources.GetSmpldImg(i); - SamplerFound = (strcmp(SmplImg.Name, ImtblSamDesc.SamplerOrTextureName) == 0); - } - - if (!SamplerFound) - { - // Check if immutable is assigned to a separate sampler. - // In case HLSL-style combined image samplers are used, the condition is SepSmpl.Name == "g_Texture" + "_sampler". - // Otherwise the condition is SepSmpl.Name == "g_Texture_sampler" + "". - const auto* CombinedSamplerSuffix = Resources.GetCombinedSamplerSuffix(); - for (Uint32 i = 0; i < Resources.GetNumSepSmplrs() && !SamplerFound; ++i) - { - const auto& SepSmpl = Resources.GetSepSmplr(i); - SamplerFound = StreqSuff(SepSmpl.Name, ImtblSamDesc.SamplerOrTextureName, CombinedSamplerSuffix); - } - } - } - } - - if (!SamplerFound) - { - LOG_WARNING_MESSAGE("Immutable sampler '", ImtblSamDesc.SamplerOrTextureName, - "' is not found in any of the designated shader stages: ", - GetAllowedShadersString(ImtblSamDesc.ShaderStages)); - } - } - } -} -#endif - -void ShaderResourceLayoutVk::Initialize(IRenderDevice* pRenderDevice, - TShaderStages& ShaderStages, - ShaderResourceLayoutVk Layouts[], - IMemoryAllocator& LayoutDataAllocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - class PipelineLayout& PipelineLayout, - bool VerifyVariables, - bool VerifyImmutableSamplers) -{ -#ifdef DILIGENT_DEVELOPMENT - dvpVerifyResourceLayoutDesc(ShaderStages, ResourceLayoutDesc, VerifyVariables, VerifyImmutableSamplers); -#endif - - // Mappings from resource name to its index, for every shader stage - std::array ResourceNameToIndexArray; - - constexpr bool AllocateImmutableSamplers = true; - - std::vector stringPools; - stringPools.reserve(ShaderStages.size()); - for (size_t s = 0; s < ShaderStages.size(); ++s) - { - stringPools.emplace_back( - Layouts[s].AllocateMemory(ShaderStages[s].Shaders, LayoutDataAllocator, ResourceLayoutDesc, - nullptr, 0, ResourceNameToIndexArray[s], AllocateImmutableSamplers)); - } - - // Current resource index, for every variable type in every shader stage - std::array, MAX_SHADERS_IN_PIPELINE> CurrResInd = {}; - // Current immutable sampler index, for every shader stage - std::array CurrImmutableSamplerInd = {}; - -#ifdef DILIGENT_DEBUG - std::unordered_map> dbgBindings_CacheOffsets; -#endif - - auto AddResource = [&](const Uint32 ShaderStageInd, - const SPIRVShaderResources& Resources, - const SPIRVShaderResourceAttribs& Attribs, - std::vector& SPIRV) // - { - auto& ResourceNameToIndex = ResourceNameToIndexArray[ShaderStageInd]; - - auto ResIter = ResourceNameToIndex.find(HashMapStringKey{Attribs.Name}); - VERIFY(ResIter != ResourceNameToIndex.end(), "Resource '", Attribs.Name, - "' is not found in ResourceNameToIndex map. This is a bug as the resource must have been processed by AllocateMemory and added to the map."); - - const auto ShaderType = Resources.GetShaderType(); - const auto VarType = FindShaderVariableType(ShaderType, Attribs, ResourceLayoutDesc, Resources.GetCombinedSamplerSuffix()); - - auto& ResLayout = Layouts[ShaderStageInd]; - - const VkResource* pResource = nullptr; - if (ResIter->second == InvalidResourceIndex) - { - // add new resource - Uint32 Binding = 0; - Uint32 DescriptorSet = 0; - Uint32 CacheOffset = 0; - Uint32 SamplerInd = VkResource::InvalidSamplerInd; - - if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage) - { - // Separate samplers are enumerated before separate images, so the sampler - // assigned to this separate image must have already been created. - SamplerInd = FindAssignedSampler(ResLayout, Resources, Attribs, CurrResInd[ShaderStageInd][VarType], VarType); - } - - VkSampler vkImmutableSampler = VK_NULL_HANDLE; - if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || - Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler) - { - // Only search for the immutable sampler for combined image samplers and separate samplers - Int32 SrcImmutableSamplerInd = FindImmutableSampler(ShaderType, ResourceLayoutDesc, Attribs, Resources.GetCombinedSamplerSuffix()); - if (SrcImmutableSamplerInd >= 0) - { - // NB: for immutable separate samplers we still allocate VkResource instances, but they are never exposed to the app - - // We reserve enough space for the maximum number of immutable samplers that may be used in the stage, - // but not all of them will necessarily be initialized. - auto& ImmutableSampler = ResLayout.GetImmutableSampler(CurrImmutableSamplerInd[ShaderStageInd]++); - VERIFY(!ImmutableSampler, "Immutable sampler has already been initialized. This is unexpected " - "as all resources are deduplicated and should only be initialized once."); - const auto& ImmutableSamplerDesc = ResourceLayoutDesc.ImmutableSamplers[SrcImmutableSamplerInd].Desc; - pRenderDevice->CreateSampler(ImmutableSamplerDesc, &ImmutableSampler); - vkImmutableSampler = ImmutableSampler.RawPtr()->GetVkSampler(); - } - } - - PipelineLayout.AllocateResourceSlot(Attribs, VarType, vkImmutableSampler, Resources.GetShaderType(), DescriptorSet, Binding, CacheOffset); - VERIFY(DescriptorSet <= std::numeric_limits::max(), "Descriptor set (", DescriptorSet, ") excceeds maximum representable value"); - VERIFY(Binding <= std::numeric_limits::max(), "Binding (", Binding, ") excceeds maximum representable value"); - -#ifdef DILIGENT_DEBUG - // Verify that bindings and cache offsets monotonically increase in every descriptor set - auto Binding_OffsetIt = dbgBindings_CacheOffsets.find(DescriptorSet); - if (Binding_OffsetIt != dbgBindings_CacheOffsets.end()) - { - VERIFY(Binding > Binding_OffsetIt->second.first, "Binding for descriptor set ", DescriptorSet, " is not strictly monotonic"); - VERIFY(CacheOffset > Binding_OffsetIt->second.second, "Cache offset for descriptor set ", DescriptorSet, " is not strictly monotonic"); - } - dbgBindings_CacheOffsets[DescriptorSet] = std::make_pair(Binding, CacheOffset); -#endif - - auto& ResInd = CurrResInd[ShaderStageInd][VarType]; - ResIter->second = ResInd; - - pResource = ::new (&ResLayout.GetResource(VarType, ResInd++)) VkResource // - { - ResLayout, - stringPools[ShaderStageInd].CopyString(Attribs.Name), - Attribs, - VarType, - Binding, - DescriptorSet, - CacheOffset, - SamplerInd, - vkImmutableSampler != VK_NULL_HANDLE // - }; - } - else - { - // merge with existing - pResource = &ResLayout.GetResource(VarType, ResIter->second); - VerifyResourceMerge(*pResource, Attribs, VarType); - } - VERIFY_EXPR(pResource != nullptr); - SPIRV[Attribs.BindingDecorationOffset] = pResource->Binding; - SPIRV[Attribs.DescriptorSetDecorationOffset] = pResource->DescriptorSet; - }; - - // First process uniform buffers for ALL shader stages to make sure all UBs go first in every descriptor set - for (size_t s = 0; s < ShaderStages.size(); ++s) - { - auto& Shaders = ShaderStages[s].Shaders; - for (size_t i = 0; i < Shaders.size(); ++i) - { - auto& SPIRV = ShaderStages[s].SPIRVs[i]; - auto& Resources = *Shaders[i]->GetShaderResources(); - for (Uint32 n = 0; n < Resources.GetNumUBs(); ++n) - { - const auto& UB = Resources.GetUB(n); - AddResource(static_cast(s), Resources, UB, SPIRV); - } - } - } - - // Second, process all storage buffers in all shader stages - for (size_t s = 0; s < ShaderStages.size(); ++s) - { - auto& Shaders = ShaderStages[s].Shaders; - for (size_t i = 0; i < Shaders.size(); ++i) - { - auto& Resources = *Shaders[i]->GetShaderResources(); - auto& SPIRV = ShaderStages[s].SPIRVs[i]; - for (Uint32 n = 0; n < Resources.GetNumSBs(); ++n) - { - const auto& SB = Resources.GetSB(n); - AddResource(static_cast(s), Resources, SB, SPIRV); - } - } - } - - // Finally, process all other resource types - for (size_t s = 0; s < ShaderStages.size(); ++s) - { - auto& Shaders = ShaderStages[s].Shaders; - for (size_t i = 0; i < Shaders.size(); ++i) - { - auto& Resources = *Shaders[i]->GetShaderResources(); - auto& SPIRV = ShaderStages[s].SPIRVs[i]; - // clang-format off - Resources.ProcessResources( - [&](const SPIRVShaderResourceAttribs& UB, Uint32) - { - VERIFY_EXPR(UB.Type == SPIRVShaderResourceAttribs::ResourceType::UniformBuffer); - // Skip - }, - [&](const SPIRVShaderResourceAttribs& SB, Uint32) - { - VERIFY_EXPR(SB.Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer || SB.Type == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer); - // Skip - }, - [&](const SPIRVShaderResourceAttribs& Img, Uint32) - { - VERIFY_EXPR(Img.Type == SPIRVShaderResourceAttribs::ResourceType::StorageImage || Img.Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer); - AddResource(static_cast(s), Resources, Img, SPIRV); - }, - [&](const SPIRVShaderResourceAttribs& SmplImg, Uint32) - { - VERIFY_EXPR(SmplImg.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || SmplImg.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer); - AddResource(static_cast(s), Resources, SmplImg, SPIRV); - }, - [&](const SPIRVShaderResourceAttribs& AC, Uint32) - { - VERIFY_EXPR(AC.Type == SPIRVShaderResourceAttribs::ResourceType::AtomicCounter); - AddResource(static_cast(s), Resources, AC, SPIRV); - }, - [&](const SPIRVShaderResourceAttribs& SepSmpl, Uint32) - { - VERIFY_EXPR(SepSmpl.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler); - AddResource(static_cast(s), Resources, SepSmpl, SPIRV); - }, - [&](const SPIRVShaderResourceAttribs& SepImg, Uint32) - { - VERIFY_EXPR(SepImg.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage || SepImg.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer); - AddResource(static_cast(s), Resources, SepImg, SPIRV); - }, - [&](const SPIRVShaderResourceAttribs& InputAtt, Uint32) - { - VERIFY_EXPR(InputAtt.Type == SPIRVShaderResourceAttribs::ResourceType::InputAttachment); - AddResource(static_cast(s), Resources, InputAtt, SPIRV); - }, - [&](const SPIRVShaderResourceAttribs& AccelStruct, Uint32) - { - VERIFY_EXPR(AccelStruct.Type == SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure); - AddResource(static_cast(s), Resources, AccelStruct, SPIRV); - } - ); - // clang-format on - } - } - -#ifdef DILIGENT_DEBUG - for (size_t s = 0; s < ShaderStages.size(); ++s) - { - auto& Layout = Layouts[s]; - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast(VarType + 1)) - { - VERIFY(CurrResInd[s][VarType] == Layout.m_NumResources[VarType], "Not all resources have been initialized, which will cause a crash when dtor is called. This is a bug."); - } - // Some immutable samplers may never be initialized if they are not present in shaders - VERIFY_EXPR(CurrImmutableSamplerInd[s] <= Layout.m_NumImmutableSamplers); - - VERIFY_EXPR(stringPools[s].GetRemainingSize() == 0); - } -#endif -} - - -void ShaderResourceLayoutVk::VkResource::UpdateDescriptorHandle(VkDescriptorSet vkDescrSet, - uint32_t ArrayElement, - 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 = Binding; - WriteDescrSet.dstArrayElement = ArrayElement; - 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 = PipelineLayout::GetVkDescriptorType(Type); - WriteDescrSet.pImageInfo = pImageInfo; - WriteDescrSet.pBufferInfo = pBufferInfo; - WriteDescrSet.pTexelBufferView = pTexelBufferView; - - ParentResLayout.m_LogicalDevice.UpdateDescriptorSets(1, &WriteDescrSet, 0, nullptr); -} - -template -bool ShaderResourceLayoutVk::VkResource::UpdateCachedResource(ShaderResourceCacheVk::Resource& DstRes, - 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 (GetVariableType() != 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 ShaderResourceLayoutVk::VkResource::CacheUniformBuffer(IDeviceObject* pBuffer, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - Uint16& DynamicBuffersCounter) const -{ - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::UniformBuffer, "Uniform buffer resource is expected"); - RefCntAutoPtr pBufferVk{pBuffer, IID_BufferVk}; -#ifdef DILIGENT_DEVELOPMENT - VerifyConstantBufferBinding(*this, GetVariableType(), ArrayInd, pBuffer, pBufferVk.RawPtr(), DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); - - 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 '", - Name, "' in shader '", ParentResLayout.GetShaderName(), "': 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(DstRes, 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 && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorBufferInfo DescrBuffInfo = DstRes.GetUniformBufferDescriptorWriteInfo(); - UpdateDescriptorHandle(vkDescrSet, ArrayInd, nullptr, &DescrBuffInfo, nullptr); - } - } -} - -void ShaderResourceLayoutVk::VkResource::CacheStorageBuffer(IDeviceObject* pBufferView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - Uint16& DynamicBuffersCounter) const -{ - // clang-format off - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer || - Type == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer, - "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 = Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer ? BUFFER_VIEW_SHADER_RESOURCE : BUFFER_VIEW_UNORDERED_ACCESS; - VerifyResourceViewBinding(*this, GetVariableType(), ArrayInd, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); - 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 '", - Name, "' in shader '", ParentResLayout.GetShaderName(), "': 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, "' in shader '", ParentResLayout.GetShaderName(), "': 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, "' in shader '", ParentResLayout.GetShaderName(), "': 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(DstRes, 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 && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorBufferInfo DescrBuffInfo = DstRes.GetStorageBufferDescriptorWriteInfo(); - UpdateDescriptorHandle(vkDescrSet, ArrayInd, nullptr, &DescrBuffInfo, nullptr); - } - } -} - -void ShaderResourceLayoutVk::VkResource::CacheTexelBuffer(IDeviceObject* pBufferView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - Uint16& DynamicBuffersCounter) const -{ - // clang-format off - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer || - Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer, - "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 = Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer ? BUFFER_VIEW_UNORDERED_ACCESS : BUFFER_VIEW_SHADER_RESOURCE; - VerifyResourceViewBinding(*this, GetVariableType(), ArrayInd, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); - 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 '", - Name, "' in shader '", ParentResLayout.GetShaderName(), "': 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(DstRes, 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 && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkBufferView BuffView = DstRes.pObject.RawPtr()->GetVkBufferView(); - UpdateDescriptorHandle(vkDescrSet, ArrayInd, nullptr, nullptr, &BuffView); - } - } -} - -template -void ShaderResourceLayoutVk::VkResource::CacheImage(IDeviceObject* pTexView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd, - TCacheSampler CacheSampler) const -{ - // clang-format off - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::StorageImage || - Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage || - Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage, - "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 = Type == SPIRVShaderResourceAttribs::ResourceType::StorageImage ? TEXTURE_VIEW_UNORDERED_ACCESS : TEXTURE_VIEW_SHADER_RESOURCE; - VerifyResourceViewBinding(*this, GetVariableType(), ArrayInd, pTexView, pTexViewVk0.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); - } -#endif - if (UpdateCachedResource(DstRes, 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 (Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage && !IsImmutableSamplerAssigned()) - { - if (pTexViewVk->GetSampler() == nullptr) - { - LOG_ERROR_MESSAGE("Error binding texture view '", pTexViewVk->GetDesc().Name, "' to variable '", GetPrintName(ArrayInd), - "' in shader '", ParentResLayout.GetShaderName(), "'. 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 && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorImageInfo DescrImgInfo = DstRes.GetImageDescriptorWriteInfo(IsImmutableSamplerAssigned()); - UpdateDescriptorHandle(vkDescrSet, ArrayInd, &DescrImgInfo, nullptr, nullptr); - } - - if (SamplerInd != InvalidSamplerInd) - { - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::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."); - const auto& SamplerAttribs = ParentResLayout.GetResource(GetVariableType(), SamplerInd); - VERIFY_EXPR(SamplerAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler); - 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.Name, - "' assigned to separate image '", GetPrintName(ArrayInd), "' in shader '", - ParentResLayout.GetShaderName(), "': no sampler is set in texture view '", pTexViewVk->GetDesc().Name, '\''); - } - } - } - } -} - -void ShaderResourceLayoutVk::VkResource::CacheSeparateSampler(IDeviceObject* pSampler, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd) const -{ - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler, "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(ArrayInd), - "' in shader '", ParentResLayout.GetShaderName(), "'. Unexpected object type: sampler is expected"); - } - if (GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && DstRes.pObject != nullptr && DstRes.pObject != pSamplerVk) - { - auto VarTypeStr = GetShaderVariableTypeLiteralName(GetVariableType()); - LOG_ERROR_MESSAGE("Non-null sampler is already bound to ", VarTypeStr, " shader variable '", GetPrintName(ArrayInd), - "' in shader '", ParentResLayout.GetShaderName(), - "'. 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(DstRes, 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 && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorImageInfo DescrImgInfo = DstRes.GetSamplerDescriptorWriteInfo(); - UpdateDescriptorHandle(vkDescrSet, ArrayInd, &DescrImgInfo, nullptr, nullptr); - } - } -} - -void ShaderResourceLayoutVk::VkResource::CacheInputAttachment(IDeviceObject* pTexView, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd) const -{ - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::InputAttachment, "Input attachment resource is expected"); - RefCntAutoPtr pTexViewVk0{pTexView, IID_TextureViewVk}; -#ifdef DILIGENT_DEVELOPMENT - VerifyResourceViewBinding(*this, GetVariableType(), ArrayInd, pTexView, pTexViewVk0.RawPtr(), {TEXTURE_VIEW_SHADER_RESOURCE}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); -#endif - if (UpdateCachedResource(DstRes, 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 && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkDescriptorImageInfo DescrImgInfo = DstRes.GetInputAttachmentDescriptorWriteInfo(); - UpdateDescriptorHandle(vkDescrSet, ArrayInd, &DescrImgInfo, nullptr, nullptr); - } - // - } -} - -void ShaderResourceLayoutVk::VkResource::CacheAccelerationStructure(IDeviceObject* pTLAS, - ShaderResourceCacheVk::Resource& DstRes, - VkDescriptorSet vkDescrSet, - Uint32 ArrayInd) const -{ - VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure, "Acceleration Structure resource is expected"); - RefCntAutoPtr pTLASVk{pTLAS, IID_TopLevelASVk}; -#ifdef DILIGENT_DEVELOPMENT - VerifyTLASResourceBinding(*this, GetVariableType(), ArrayInd, pTLASVk.RawPtr(), DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); -#endif - if (UpdateCachedResource(DstRes, 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 && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VkWriteDescriptorSetAccelerationStructureKHR DescrASInfo = DstRes.GetAccelerationStructureWriteInfo(); - UpdateDescriptorHandle(vkDescrSet, ArrayInd, nullptr, nullptr, nullptr, &DescrASInfo); - } - // - } -} - -void ShaderResourceLayoutVk::VkResource::BindResource(IDeviceObject* pObj, Uint32 ArrayIndex, ShaderResourceCacheVk& ResourceCache) const -{ - VERIFY_EXPR(ArrayIndex < ArraySize); - - auto& DstDescrSet = ResourceCache.GetDescriptorSet(DescriptorSet); - auto vkDescrSet = DstDescrSet.GetVkDescriptorSet(); -#ifdef DILIGENT_DEBUG - if (ResourceCache.DbgGetContentType() == ShaderResourceCacheVk::DbgCacheContentType::SRBResources) - { - if (VariableType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC || VariableType == 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 - auto& DstRes = DstDescrSet.GetResource(CacheOffset + ArrayIndex); - VERIFY(DstRes.Type == Type, "Inconsistent types"); - - if (pObj) - { - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type below"); - switch (Type) - { - case SPIRVShaderResourceAttribs::ResourceType::UniformBuffer: - CacheUniformBuffer(pObj, DstRes, vkDescrSet, ArrayIndex, ResourceCache.GetDynamicBuffersCounter()); - break; - - case SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer: - case SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer: - CacheStorageBuffer(pObj, DstRes, vkDescrSet, ArrayIndex, ResourceCache.GetDynamicBuffersCounter()); - break; - - case SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer: - case SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer: - CacheTexelBuffer(pObj, DstRes, vkDescrSet, ArrayIndex, ResourceCache.GetDynamicBuffersCounter()); - break; - - case SPIRVShaderResourceAttribs::ResourceType::StorageImage: - case SPIRVShaderResourceAttribs::ResourceType::SeparateImage: - case SPIRVShaderResourceAttribs::ResourceType::SampledImage: - CacheImage(pObj, DstRes, vkDescrSet, ArrayIndex, - [&](const VkResource& SeparateSampler, ISampler* pSampler) { - VERIFY(!SeparateSampler.IsImmutableSamplerAssigned(), "Separate sampler '", SeparateSampler.Name, "' is assigned an immutable sampler"); - VERIFY_EXPR(Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage); - DEV_CHECK_ERR(SeparateSampler.ArraySize == 1 || SeparateSampler.ArraySize == ArraySize, - "Array size (", SeparateSampler.ArraySize, - ") of separate sampler variable '", - SeparateSampler.Name, - "' must be one or the same as the array size (", ArraySize, - ") of separate image variable '", Name, "' it is assigned to"); - Uint32 SamplerArrInd = SeparateSampler.ArraySize == 1 ? 0 : ArrayIndex; - SeparateSampler.BindResource(pSampler, SamplerArrInd, ResourceCache); - }); - break; - - case SPIRVShaderResourceAttribs::ResourceType::SeparateSampler: - if (!IsImmutableSamplerAssigned()) - { - CacheSeparateSampler(pObj, DstRes, vkDescrSet, ArrayIndex); - } - 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 '", Name, '\''); - } - break; - - case SPIRVShaderResourceAttribs::ResourceType::InputAttachment: - CacheInputAttachment(pObj, DstRes, vkDescrSet, ArrayIndex); - break; - - case SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure: - CacheAccelerationStructure(pObj, DstRes, vkDescrSet, ArrayIndex); - break; - - default: UNEXPECTED("Unknown resource type ", static_cast(Type)); - } - } - else - { - if (DstRes.pObject && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - LOG_ERROR_MESSAGE("Shader variable '", Name, "' in shader '", ParentResLayout.GetShaderName(), - "' 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(); - } -} - -bool ShaderResourceLayoutVk::VkResource::IsBound(Uint32 ArrayIndex, const ShaderResourceCacheVk& ResourceCache) const -{ - VERIFY_EXPR(ArrayIndex < ArraySize); - - if (DescriptorSet < ResourceCache.GetNumDescriptorSets()) - { - const auto& Set = ResourceCache.GetDescriptorSet(DescriptorSet); - if (CacheOffset + ArrayIndex < Set.GetSize()) - { - const auto& CachedRes = Set.GetResource(CacheOffset + ArrayIndex); - return CachedRes.pObject != nullptr; - } - } - - return false; -} - - -void ShaderResourceLayoutVk::InitializeStaticResources(const ShaderResourceLayoutVk& SrcLayout, - const ShaderResourceCacheVk& SrcResourceCache, - ShaderResourceCacheVk& DstResourceCache) const -{ - auto NumStaticResources = m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_STATIC]; - VERIFY(NumStaticResources == SrcLayout.m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_STATIC], "Inconsistent number of static resources"); - VERIFY(SrcLayout.GetShaderType() == GetShaderType(), "Incosistent shader types"); - - // Static shader resources are stored in one large continuous descriptor set - for (Uint32 r = 0; r < NumStaticResources; ++r) - { - // Get resource attributes. Resources have the same index in both layouts. - const auto& DstRes = GetResource(SHADER_RESOURCE_VARIABLE_TYPE_STATIC, r); - const auto& SrcRes = SrcLayout.GetResource(SHADER_RESOURCE_VARIABLE_TYPE_STATIC, r); - VERIFY(strcmp(SrcRes.Name, DstRes.Name) == 0, "Src resource name ('", SrcRes.Name, "') does match the dst resource name '(", DstRes.Name, "'). This is a bug."); - VERIFY(SrcRes.Type == DstRes.Type, "Src and dst resource types are incompatible. This is a bug."); - VERIFY(SrcRes.ResourceDim == DstRes.ResourceDim, "Src and dst resource dimensions are incompatible. This is a bug."); - VERIFY(SrcRes.Binding == Uint32{SrcRes.Type}, "Unexpected binding"); - VERIFY(SrcRes.ArraySize == DstRes.ArraySize, "Src and dst resource array sizes are not identical. This is a bug."); - VERIFY(SrcRes.IsImmutableSamplerAssigned() == DstRes.IsImmutableSamplerAssigned(), "Src and dst resource immutable sampler flags are not identical. This is a bug."); - - if (DstRes.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && - DstRes.IsImmutableSamplerAssigned()) - continue; // Skip immutable separate samplers - - for (Uint32 ArrInd = 0; ArrInd < DstRes.ArraySize; ++ArrInd) - { - auto SrcOffset = SrcRes.CacheOffset + ArrInd; - const auto& SrcCachedSet = SrcResourceCache.GetDescriptorSet(SrcRes.DescriptorSet); - const auto& SrcCachedRes = SrcCachedSet.GetResource(SrcOffset); - IDeviceObject* pObject = SrcCachedRes.pObject.RawPtr(); - if (!pObject) - LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", SrcRes.GetPrintName(ArrInd), "' in shader '", GetShaderName(), "'."); - - auto DstOffset = DstRes.CacheOffset + ArrInd; - IDeviceObject* pCachedResource = DstResourceCache.GetDescriptorSet(DstRes.DescriptorSet).GetResource(DstOffset).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"); - DstRes.BindResource(pObject, ArrInd, DstResourceCache); - } - } - } -} - - -#ifdef DILIGENT_DEVELOPMENT -bool ShaderResourceLayoutVk::dvpVerifyBindings(const ShaderResourceCacheVk& ResourceCache) const -{ - bool BindingsOK = true; - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast(VarType + 1)) - { - for (Uint32 r = 0; r < m_NumResources[VarType]; ++r) - { - const auto& Res = GetResource(VarType, r); - VERIFY(Res.GetVariableType() == VarType, "Unexpected variable type"); - for (Uint32 ArrInd = 0; ArrInd < Res.ArraySize; ++ArrInd) - { - const auto& CachedDescrSet = ResourceCache.GetDescriptorSet(Res.DescriptorSet); - const auto& CachedRes = CachedDescrSet.GetResource(Res.CacheOffset + ArrInd); - VERIFY(CachedRes.Type == Res.Type, "Inconsistent types"); - if (CachedRes.pObject == nullptr && - !(Res.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && Res.IsImmutableSamplerAssigned())) - { - LOG_ERROR_MESSAGE("No resource is bound to ", GetShaderVariableTypeLiteralName(Res.GetVariableType()), " variable '", Res.GetPrintName(ArrInd), "' in shader '", GetShaderName(), "'"); - BindingsOK = false; - } -# ifdef DILIGENT_DEBUG - auto vkDescSet = CachedDescrSet.GetVkDescriptorSet(); - auto dbgCacheContentType = ResourceCache.DbgGetContentType(); - if (dbgCacheContentType == ShaderResourceCacheVk::DbgCacheContentType::StaticShaderResources) - VERIFY(vkDescSet == VK_NULL_HANDLE, "Static resource cache should never have vulkan descriptor set"); - else if (dbgCacheContentType == ShaderResourceCacheVk::DbgCacheContentType::SRBResources) - { - if (VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC || VarType == SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) - { - VERIFY(vkDescSet != VK_NULL_HANDLE, "Static and mutable variables must have valid vulkan descriptor set assigned"); - } - else if (VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - { - VERIFY(vkDescSet == VK_NULL_HANDLE, "Dynamic variables must not be assigned a vulkan descriptor set"); - } - } - else - UNEXPECTED("Unexpected cache content type"); -# endif - } - } - } - return BindingsOK; -} -#endif - -void ShaderResourceLayoutVk::InitializeResourceMemoryInCache(ShaderResourceCacheVk& ResourceCache) const -{ - auto TotalResources = GetTotalResourceCount(); - for (Uint32 r = 0; r < TotalResources; ++r) - { - const auto& Res = GetResource(r); - ResourceCache.InitializeResources(Res.DescriptorSet, Res.CacheOffset, Res.ArraySize, Res.Type); - } -} - -void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& ResourceCache, - VkDescriptorSet vkDynamicDescriptorSet) const -{ - Uint32 NumDynamicResources = m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC]; - VERIFY(NumDynamicResources != 0, "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; - - Uint32 ResNum = 0, ArrElem = 0; - auto DescrImgIt = DescrImgInfoArr.begin(); - auto DescrBuffIt = DescrBuffInfoArr.begin(); - auto BuffViewIt = DescrBuffViewArr.begin(); - auto AccelStructIt = DescrAccelStructArr.begin(); - auto WriteDescrSetIt = WriteDescrSetArr.begin(); - -#ifdef DILIGENT_DEBUG - Int32 DynamicDescrSetIndex = -1; -#endif - - while (ResNum < NumDynamicResources) - { - const auto& Res = GetResource(SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC, ResNum); - VERIFY_EXPR(Res.GetVariableType() == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC); -#ifdef DILIGENT_DEBUG - if (DynamicDescrSetIndex < 0) - DynamicDescrSetIndex = Res.DescriptorSet; - else - VERIFY(DynamicDescrSetIndex == Res.DescriptorSet, "Inconsistent dynamic resource desriptor set index"); -#endif - const auto& SetResources = ResourceCache.GetDescriptorSet(Res.DescriptorSet); - 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 = Res.Binding; - 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 = PipelineLayout::GetVkDescriptorType(Res.Type); - - // For every resource type, try to batch as many descriptor updates as we can - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type below"); - switch (Res.Type) - { - case SPIRVShaderResourceAttribs::ResourceType::UniformBuffer: - WriteDescrSetIt->pBufferInfo = &(*DescrBuffIt); - while (ArrElem < Res.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) - { - const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); - *DescrBuffIt = CachedRes.GetUniformBufferDescriptorWriteInfo(); - ++DescrBuffIt; - ++ArrElem; - } - break; - - case SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer: - case SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer: - WriteDescrSetIt->pBufferInfo = &(*DescrBuffIt); - while (ArrElem < Res.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) - { - const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); - *DescrBuffIt = CachedRes.GetStorageBufferDescriptorWriteInfo(); - ++DescrBuffIt; - ++ArrElem; - } - break; - - case SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer: - case SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer: - WriteDescrSetIt->pTexelBufferView = &(*BuffViewIt); - while (ArrElem < Res.ArraySize && BuffViewIt != DescrBuffViewArr.end()) - { - const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); - *BuffViewIt = CachedRes.GetBufferViewWriteInfo(); - ++BuffViewIt; - ++ArrElem; - } - break; - - case SPIRVShaderResourceAttribs::ResourceType::SeparateImage: - case SPIRVShaderResourceAttribs::ResourceType::StorageImage: - case SPIRVShaderResourceAttribs::ResourceType::SampledImage: - WriteDescrSetIt->pImageInfo = &(*DescrImgIt); - while (ArrElem < Res.ArraySize && DescrImgIt != DescrImgInfoArr.end()) - { - const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); - *DescrImgIt = CachedRes.GetImageDescriptorWriteInfo(Res.IsImmutableSamplerAssigned()); - ++DescrImgIt; - ++ArrElem; - } - break; - - case SPIRVShaderResourceAttribs::ResourceType::AtomicCounter: - // Do nothing - break; - - - case SPIRVShaderResourceAttribs::ResourceType::SeparateSampler: - // 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 (!Res.IsImmutableSamplerAssigned()) - { - WriteDescrSetIt->pImageInfo = &(*DescrImgIt); - while (ArrElem < Res.ArraySize && DescrImgIt != DescrImgInfoArr.end()) - { - const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); - *DescrImgIt = CachedRes.GetSamplerDescriptorWriteInfo(); - ++DescrImgIt; - ++ArrElem; - } - } - else - { - ArrElem = Res.ArraySize; - WriteDescrSetIt->dstArrayElement = Res.ArraySize; - } - break; - - case SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure: - WriteDescrSetIt->pNext = &(*AccelStructIt); - while (ArrElem < Res.ArraySize && AccelStructIt != DescrAccelStructArr.end()) - { - const auto& CachedRes = SetResources.GetResource(Res.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 (ResNum == NumDynamicResources || - 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) - m_LogicalDevice.UpdateDescriptorSets(DescrWriteCount, WriteDescrSetArr.data(), 0, nullptr); - - DescrImgIt = DescrImgInfoArr.begin(); - DescrBuffIt = DescrBuffInfoArr.begin(); - BuffViewIt = DescrBuffViewArr.begin(); - AccelStructIt = DescrAccelStructArr.begin(); - WriteDescrSetIt = WriteDescrSetArr.begin(); - } - } -} - -bool ShaderResourceLayoutVk::IsCompatibleWith(const ShaderResourceLayoutVk& ResLayout) const -{ - if (m_NumResources != ResLayout.m_NumResources) - return false; - - for (Uint32 i = 0, Cnt = GetTotalResourceCount(); i < Cnt; ++i) - { - const auto& lhs = this->GetResource(i); - const auto& rhs = ResLayout.GetResource(i); - - if (!lhs.IsCompatibleWith(rhs)) - return false; - } - - return true; -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp index 24f9342f..a93c4482 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp @@ -29,51 +29,57 @@ #include "ShaderVariableVk.hpp" #include "ShaderResourceVariableBase.hpp" +#include "PipelineResourceSignatureVkImpl.hpp" +#include "SamplerVkImpl.hpp" +#include "TextureViewVkImpl.hpp" +#include "TopLevelASVkImpl.hpp" namespace Diligent { -size_t ShaderVariableManagerVk::GetRequiredMemorySize(const ShaderResourceLayoutVk& Layout, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - Uint32& NumVariables) +size_t ShaderVariableManagerVk::GetRequiredMemorySize(const PipelineResourceSignatureVkImpl& Layout, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + Uint32& NumVariables) { NumVariables = 0; const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); - const bool UsingSeparateSamplers = Layout.IsUsingSeparateSamplers(); - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast(VarType + 1)) + const bool UsingSeparateSamplers = true; //Layout.IsUsingSeparateSamplers(); + const auto& Desc = Layout.GetDesc(); + + for (Uint32 r = 0; r < Desc.NumResources; ++r) { - if (IsAllowedType(VarType, AllowedTypeBits)) - { - auto NumResources = Layout.GetResourceCount(VarType); - for (Uint32 r = 0; r < NumResources; ++r) - { - const auto& SrcRes = Layout.GetResource(VarType, r); + const auto& SrcRes = Desc.Resources[r]; - // 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 && - (!UsingSeparateSamplers || SrcRes.IsImmutableSamplerAssigned())) - continue; + if (!IsAllowedType(SrcRes.VarType, AllowedTypeBits)) + continue; - ++NumVariables; - } - } + // 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())) + continue; + */ + ++NumVariables; } return NumVariables * sizeof(ShaderVariableVkImpl); } // Creates shader variable for every resource from SrcLayout whose type is one AllowedVarTypes -void ShaderVariableManagerVk::Initialize(const ShaderResourceLayoutVk& SrcLayout, - IMemoryAllocator& Allocator, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes) +void ShaderVariableManagerVk::Initialize(const PipelineResourceSignatureVkImpl& SrcLayout, + IMemoryAllocator& Allocator, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes) { #ifdef DILIGENT_DEBUG m_pDbgAllocator = &Allocator; #endif + VERIFY_EXPR(m_pSignature == nullptr); + const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); VERIFY_EXPR(m_NumVariables == 0); auto MemSize = GetRequiredMemorySize(SrcLayout, AllowedVarTypes, NumAllowedTypes, m_NumVariables); @@ -84,27 +90,30 @@ void ShaderVariableManagerVk::Initialize(const ShaderResourceLayoutVk& Sr auto* pRawMem = ALLOCATE_RAW(Allocator, "Raw memory buffer for shader variables", MemSize); m_pVariables = reinterpret_cast(pRawMem); - Uint32 VarInd = 0; - const bool UsingSeparateSamplers = SrcLayout.IsUsingSeparateSamplers(); - for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast(VarType + 1)) + Uint32 VarInd = 0; + const bool UsingSeparateSamplers = false; //SrcLayout.IsUsingSeparateSamplers(); + const auto& Desc = SrcLayout.GetDesc(); + + for (Uint32 r = 0; r < Desc.NumResources; ++r) { - if (!IsAllowedType(VarType, AllowedTypeBits)) - continue; + const auto& SrcRes = Desc.Resources[r]; - Uint32 NumResources = SrcLayout.GetResourceCount(VarType); - for (Uint32 r = 0; r < NumResources; ++r) - { - const auto& SrcRes = SrcLayout.GetResource(VarType, r); - // 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())) - continue; + if (!IsAllowedType(SrcRes.VarType, AllowedTypeBits)) + continue; - ::new (m_pVariables + VarInd) ShaderVariableVkImpl(*this, SrcRes); - ++VarInd; - } + // 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())) + continue; + */ + ::new (m_pVariables + VarInd) ShaderVariableVkImpl(*this); + ++VarInd; } VERIFY_EXPR(VarInd == m_NumVariables); + + m_pSignature = &SrcLayout; } ShaderVariableManagerVk::~ShaderVariableManagerVk() @@ -131,7 +140,7 @@ ShaderVariableVkImpl* ShaderVariableManagerVk::GetVariable(const Char* Name) con for (Uint32 v = 0; v < m_NumVariables; ++v) { auto& Var = m_pVariables[v]; - const auto& Res = Var.m_Resource; + const auto& Res = Var.GetDesc(); if (strcmp(Res.Name, Name) == 0) { pVar = &Var; @@ -183,7 +192,7 @@ 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]; @@ -218,7 +227,554 @@ void ShaderVariableManagerVk::BindResources(IResourceMapping* pResourceMapping, } } } + }*/ +} + +void ShaderVariableVkImpl::Set(IDeviceObject* pObject) +{ + BindResource(pObject, 0); +} + +void ShaderVariableVkImpl::SetArray(IDeviceObject* const* ppObjects, + Uint32 FirstElement, + Uint32 NumElements) +{ + const auto& ResDesc = GetDesc(); + VerifyAndCorrectSetArrayArguments(ResDesc.Name, ResDesc.ArraySize, FirstElement, NumElements); + + for (Uint32 Elem = 0; Elem < NumElements; ++Elem) + BindResource(ppObjects[Elem], FirstElement + Elem); +} + +bool ShaderVariableVkImpl::IsBound(Uint32 ArrayIndex) const +{ + auto& ResourceCache = m_ParentManager.m_ResourceCache; + const auto& ResDesc = GetDesc(); + const auto& Binding = GetBinding(); + Uint32 CacheOffset = GetIndex(); + + VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize); + + if (Binding.DescSet < ResourceCache.GetNumDescriptorSets()) + { + const auto& Set = ResourceCache.GetDescriptorSet(Binding.DescSet); + if (CacheOffset + ArrayIndex < Set.GetSize()) + { + const auto& CachedRes = Set.GetResource(CacheOffset + ArrayIndex); + return CachedRes.pObject != nullptr; + } + } + + return false; +} + +void ShaderVariableVkImpl::BindResource(IDeviceObject* pObj, Uint32 ArrayIndex) const +{ + auto CacheOffset = GetIndex(); + auto& ResDesc = GetDesc(); + auto& Bindings = GetBinding(); + 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 } } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp index 38320b47..264d49a5 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp @@ -1632,6 +1632,18 @@ VkShaderStageFlagBits ShaderTypeToVkShaderStageFlagBit(SHADER_TYPE ShaderType) } } +VkShaderStageFlags ShaderTypesToVkShaderStageFlags(SHADER_TYPE ShaderTypes) +{ + VkShaderStageFlags Result = 0; + while (ShaderTypes != SHADER_TYPE_UNKNOWN) + { + auto Type = static_cast(1u << PlatformMisc::GetLSB(Uint32{ShaderTypes})); + Result |= ShaderTypeToVkShaderStageFlagBit(Type); + ShaderTypes = ShaderTypes & ~Type; + } + return Result; +} + VkBuildAccelerationStructureFlagsKHR BuildASFlagsToVkBuildAccelerationStructureFlags(RAYTRACING_BUILD_AS_FLAGS Flags) { static_assert(RAYTRACING_BUILD_AS_FLAGS_LAST == RAYTRACING_BUILD_AS_LOW_MEMORY, @@ -1640,7 +1652,7 @@ VkBuildAccelerationStructureFlagsKHR BuildASFlagsToVkBuildAccelerationStructureF VkBuildAccelerationStructureFlagsKHR Result = 0; while (Flags != RAYTRACING_BUILD_AS_NONE) { - auto FlagBit = static_cast(1 << PlatformMisc::GetLSB(Uint32{Flags})); + auto FlagBit = static_cast(1u << PlatformMisc::GetLSB(Uint32{Flags})); switch (FlagBit) { // clang-format off -- cgit v1.2.3