diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2018-06-12 16:45:57 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2018-06-12 16:45:57 +0000 |
| commit | 4027a75f1f7bd089fb3f1b39ce29e413d8b23a62 (patch) | |
| tree | 3f5ba7d069466c8744948128c22b8755b9a81d71 /Graphics/GraphicsEngineVulkan | |
| parent | Updated static shader resource cache to use one descriptor set instead of ind... (diff) | |
| download | DiligentCore-4027a75f1f7bd089fb3f1b39ce29e413d8b23a62.tar.gz DiligentCore-4027a75f1f7bd089fb3f1b39ce29e413d8b23a62.zip | |
Made uniform and storage buffer descriptors dynamic in pipeline layout
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
10 files changed, 366 insertions, 237 deletions
diff --git a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h index 1ac1e384..cc797677 100644 --- a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h @@ -33,6 +33,7 @@ #include "VulkanDynamicHeap.h" #include "VulkanUtilities/VulkanObjectWrappers.h" #include "VulkanUtilities/VulkanMemoryManager.h" +#include "VulkanDynamicHeap.h" namespace Diligent { @@ -67,6 +68,7 @@ public: // void DbgVerifyDynamicAllocation(Uint32 ContextId); //#endif + Uint32 GetDynamicOffset(Uint32 CtxId)const{return 0;} VkBuffer GetVkBuffer()const override final { diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayout.h b/Graphics/GraphicsEngineVulkan/include/PipelineLayout.h index b8bdddfc..556fab20 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayout.h +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayout.h @@ -99,10 +99,11 @@ private: DescriptorSetLayout& operator = (const DescriptorSetLayout&) = delete; DescriptorSetLayout& operator = (DescriptorSetLayout&&) = delete; - uint32_t TotalDescriptors = 0; - int8_t SetIndex = -1; - uint16_t NumLayoutBindings = 0; - VkDescriptorSetLayoutBinding* pBindings = nullptr; + 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(); @@ -138,12 +139,13 @@ private: size_t GetHash()const; VkPipelineLayout GetVkPipelineLayout()const{return m_VkPipelineLayout;} - void AllocateResourceSlot(const SPIRVShaderResourceAttribs &ResAttribs, - VkSampler vkStaticSampler, - SHADER_TYPE ShaderType, - Uint32 &DescriptorSet, - Uint32 &Binding, - Uint32 &OffsetInCache); + void AllocateResourceSlot(const SPIRVShaderResourceAttribs& ResAttribs, + VkSampler vkStaticSampler, + SHADER_TYPE ShaderType, + Uint32& DescriptorSet, + Uint32& Binding, + Uint32& OffsetInCache); + private: IMemoryAllocator &m_MemAllocator; VulkanUtilities::PipelineLayoutWrapper m_VkPipelineLayout; diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h index 44950419..0b0393dd 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h @@ -91,8 +91,6 @@ private: void CreateRenderPass(const VulkanUtilities::VulkanLogicalDevice &LogicalDevice); - void ParseResourceLayoutAndCreateShader(IShader *pShader, Uint32 LayoutInd); - DummyShaderVariable m_DummyVar; // Looks like there may be a bug in msvc: when allocators are declared as diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h index 192e06cc..b5dbb2b7 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h @@ -173,8 +173,15 @@ public: template<bool VerifyOnly> void TransitionResources(DeviceContextVkImpl *pCtxVkImpl); + void GetDynamicBufferOffset(Uint32 CtxId, std::vector<uint32_t>& Offsets)const; + private: + Resource* GetFirstResourcePtr() + { + return reinterpret_cast<Resource*>(reinterpret_cast<DescriptorSet*>(m_pMemory) + m_NumSets); + } + IMemoryAllocator* m_pAllocator = nullptr; void* m_pMemory = nullptr; Uint32 m_NumSets = 0; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.h b/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.h index bd843797..3f6e335e 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.h +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.h @@ -126,19 +126,20 @@ public: ~ShaderResourceLayoutVk(); - // The method is called by - // - ShaderVkImpl class instance to initialize static resource layout and initialize shader resource cache - // to hold static resources - // - PipelineStateVkImpl class instance to reference all types of resources (static, mutable, dynamic). - // Descriptor sets, bindings and cache offsets are assigned during the initialization; - // no shader resource cache must be provided - void Initialize(const std::shared_ptr<const SPIRVShaderResources>& pSrcResources, - IMemoryAllocator& LayoutDataAllocator, - const SHADER_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - ShaderResourceCacheVk* pStaticResourceCache, - std::vector<uint32_t>* pSPIRV, - class PipelineLayout* pPipelineLayout); + // This method is called by ShaderVkImpl class instance to initialize static + // shader resource layout and cache + void InitializeStaticResourceLayout(std::shared_ptr<const SPIRVShaderResources> pSrcResources, + IMemoryAllocator& LayoutDataAllocator, + ShaderResourceCacheVk& StaticResourceCache); + + // This method is called by PipelineStateVkImpl class instance to initialize resource + // layouts for all shader stages in a pipeline. + static void Initialize(Uint32 NumShaders, + ShaderResourceLayoutVk Layouts[], + std::shared_ptr<const SPIRVShaderResources> pShaderResources[], + IMemoryAllocator& LayoutDataAllocator, + std::vector<uint32_t> SPIRVs[], + class PipelineLayout& PipelineLayout); // sizeof(VkResource) == 24 (x64) struct VkResource @@ -272,11 +273,13 @@ private: Uint32 GetTotalResourceCount()const { - static_assert(SHADER_VARIABLE_TYPE_NUM_TYPES == 3, "Did you add new variable type?"); - return m_NumResources[SHADER_VARIABLE_TYPE_STATIC] + m_NumResources[SHADER_VARIABLE_TYPE_MUTABLE] + m_NumResources[SHADER_VARIABLE_TYPE_DYNAMIC]; + return m_NumResources[SHADER_VARIABLE_TYPE_NUM_TYPES]; } - void AllocateMemory(IMemoryAllocator &Allocator); + void AllocateMemory(std::shared_ptr<const SPIRVShaderResources> pSrcResources, + IMemoryAllocator& Allocator, + const SHADER_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes); IObject& m_Owner; @@ -287,7 +290,7 @@ private: // there may be multiple objects referencing the same set of resources std::shared_ptr<const SPIRVShaderResources> m_pResources; - std::array<Uint16, SHADER_VARIABLE_TYPE_NUM_TYPES> m_NumResources = {}; + std::array<Uint16, SHADER_VARIABLE_TYPE_NUM_TYPES+1> m_NumResources = {}; }; } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp index e5d356f9..f3e0ac2b 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp @@ -61,8 +61,8 @@ public: ResourceTypeToVkDescriptorType() { static_assert(SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes == 9, "Please add corresponding decriptor type"); - m_Map[SPIRVShaderResourceAttribs::ResourceType::UniformBuffer] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - m_Map[SPIRVShaderResourceAttribs::ResourceType::StorageBuffer] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + m_Map[SPIRVShaderResourceAttribs::ResourceType::UniformBuffer] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + m_Map[SPIRVShaderResourceAttribs::ResourceType::StorageBuffer] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; m_Map[SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer] = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; m_Map[SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer] = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; m_Map[SPIRVShaderResourceAttribs::ResourceType::StorageImage] = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; @@ -99,6 +99,12 @@ void PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::AddBinding 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<decltype(NumDynamicDescriptors)>::max(), "Number of dynamic descriptors exceeds max representable value"); + NumDynamicDescriptors += static_cast<decltype(NumDynamicDescriptors)>(Binding.descriptorCount); + } } size_t PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::GetMemorySize(Uint32 NumBindings) @@ -197,9 +203,10 @@ PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::~DescriptorSetL bool PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::operator == (const DescriptorSetLayout& rhs)const { - if(TotalDescriptors != rhs.TotalDescriptors || - SetIndex != rhs.SetIndex || - NumLayoutBindings != rhs.NumLayoutBindings) + if(TotalDescriptors != rhs.TotalDescriptors || + SetIndex != rhs.SetIndex || + NumDynamicDescriptors != rhs.NumDynamicDescriptors || + NumLayoutBindings != rhs.NumLayoutBindings) return false; for(uint32_t b=0; b < NumLayoutBindings; ++b) @@ -222,7 +229,7 @@ bool PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::operator = size_t PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayout::GetHash()const { - size_t Hash = ComputeHash(SetIndex, NumLayoutBindings, TotalDescriptors); + size_t Hash = ComputeHash(SetIndex, NumLayoutBindings, TotalDescriptors, NumDynamicDescriptors); for (uint32_t b = 0; b < NumLayoutBindings; ++b) { const auto &B = pBindings[b]; @@ -308,12 +315,12 @@ size_t PipelineLayout::DescriptorSetLayoutManager::GetHash()const return Hash; } -void PipelineLayout::DescriptorSetLayoutManager::AllocateResourceSlot(const SPIRVShaderResourceAttribs &ResAttribs, - VkSampler vkStaticSampler, - SHADER_TYPE ShaderType, - Uint32 &DescriptorSet, - Uint32 &Binding, - Uint32 &OffsetInCache) +void PipelineLayout::DescriptorSetLayoutManager::AllocateResourceSlot(const SPIRVShaderResourceAttribs& ResAttribs, + VkSampler vkStaticSampler, + SHADER_TYPE ShaderType, + Uint32& DescriptorSet, + Uint32& Binding, + Uint32& OffsetInCache) { auto& DescrSet = GetDescriptorSet(ResAttribs.VarType); if (DescrSet.SetIndex < 0) @@ -427,6 +434,9 @@ void PipelineLayout::BindDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, VERIFY(m_LayoutMgr.GetDescriptorSet(SHADER_VARIABLE_TYPE_STATIC).SetIndex == m_LayoutMgr.GetDescriptorSet(SHADER_VARIABLE_TYPE_MUTABLE).SetIndex, "Static and mutable variables are expected to share the same descriptor set"); +#ifdef _DEBUG + Uint32 TotalDynamicDescriptors = 0; +#endif for(SHADER_VARIABLE_TYPE VarType = SHADER_VARIABLE_TYPE_MUTABLE; VarType <= SHADER_VARIABLE_TYPE_DYNAMIC; VarType = static_cast<SHADER_VARIABLE_TYPE>(VarType+1)) { const auto &Set = m_LayoutMgr.GetDescriptorSet(VarType); @@ -437,6 +447,9 @@ void PipelineLayout::BindDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, vkSets[Set.SetIndex] = ResourceCache.GetDescriptorSet(Set.SetIndex).GetVkDescriptorSet(); VERIFY(vkSets[Set.SetIndex] != VK_NULL_HANDLE, "Descriptor set must not be null"); } +#ifdef _DEBUG + TotalDynamicDescriptors += Set.NumDynamicDescriptors; +#endif } #ifdef _DEBUG @@ -444,6 +457,10 @@ void PipelineLayout::BindDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, VERIFY(vkSets[i] != VK_NULL_HANDLE, "Descriptor set must not be null"); #endif + std::vector<uint32_t> DynamicOffsets; + ResourceCache.GetDynamicBufferOffset(pCtxVkImpl->GetContextId(), DynamicOffsets); + VERIFY(DynamicOffsets.size() == TotalDynamicDescriptors, "Incorrect size of dynamic descriptor array"); + auto& CmdBuffer = pCtxVkImpl->GetCommandBuffer(); // vkCmdBindDescriptorSets causes the sets numbered [firstSet .. firstSet+descriptorSetCount-1] to use the // bindings stored in pDescriptorSets[0 .. descriptorSetCount-1] for subsequent rendering commands @@ -453,7 +470,10 @@ void PipelineLayout::BindDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, m_LayoutMgr.GetVkPipelineLayout(), 0, // First set SetCount, - vkSets.data()); + vkSets.data(), + // dynamicOffsetCount must equal the total number of dynamic descriptors in the sets being bound (13.2.5) + static_cast<uint32_t>(DynamicOffsets.size()), + DynamicOffsets.data()); } } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 8874b0b7..357c8b12 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -36,28 +36,6 @@ namespace Diligent { -void PipelineStateVkImpl::ParseResourceLayoutAndCreateShader(IShader *pShader, Uint32 LayoutInd) -{ - VERIFY_EXPR(pShader); - - auto *pShaderVk = ValidatedCast<ShaderVkImpl>(pShader); - - auto pDeviceVkImpl = ValidatedCast<RenderDeviceVkImpl>(pShaderVk->GetDevice()); - const auto &LogicalDevice = pDeviceVkImpl->GetLogicalDevice(); - - std::vector<uint32_t> SPIRV = pShaderVk->GetSPIRV(); - m_ShaderResourceLayouts[LayoutInd].Initialize(pShaderVk->GetShaderResources(), GetRawAllocator(), nullptr, 0, nullptr, &SPIRV, &m_PipelineLayout); - - VkShaderModuleCreateInfo ShaderModuleCI = {}; - ShaderModuleCI.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - ShaderModuleCI.pNext = NULL; - ShaderModuleCI.flags = 0; - ShaderModuleCI.codeSize = SPIRV.size() * sizeof(uint32_t); - ShaderModuleCI.pCode = SPIRV.data(); - m_ShaderModules[LayoutInd] = LogicalDevice.CreateShaderModule(ShaderModuleCI, m_Desc.Name); -} - - void PipelineStateVkImpl::CreateRenderPass(const VulkanUtilities::VulkanLogicalDevice &LogicalDevice) { // NOTE: framebuffer cache and clear commands assume that depth buffer @@ -153,12 +131,61 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren m_pDefaultShaderResBinding(nullptr, STDDeleter<ShaderResourceBindingVkImpl, FixedBlockMemoryAllocator>(pDeviceVk->GetSRBAllocator()) ) { const auto &LogicalDevice = pDeviceVk->GetLogicalDevice(); + + // Initialize shader resource layouts auto &ShaderResLayoutAllocator = GetRawAllocator(); + std::array<std::shared_ptr<const SPIRVShaderResources>, MaxShadersInPipeline> ShaderResources; + std::array<std::vector<uint32_t>, MaxShadersInPipeline> ShaderSPIRVs; auto *pRawMem = ALLOCATE(ShaderResLayoutAllocator, "Raw memory for ShaderResourceLayoutVk", sizeof(ShaderResourceLayoutVk) * m_NumShaders); m_ShaderResourceLayouts = reinterpret_cast<ShaderResourceLayoutVk*>(pRawMem); for(Uint32 s=0; s < m_NumShaders; ++s) + { new (m_ShaderResourceLayouts + s) ShaderResourceLayoutVk(*this, LogicalDevice, GetRawAllocator()); + auto *pShaderVk = ValidatedCast<ShaderVkImpl>(m_ppShaders[s]); + ShaderResources[s] = pShaderVk->GetShaderResources(); + ShaderSPIRVs[s] = pShaderVk->GetSPIRV(); + } + ShaderResourceLayoutVk::Initialize(m_NumShaders, m_ShaderResourceLayouts, ShaderResources.data(), GetRawAllocator(), ShaderSPIRVs.data(), m_PipelineLayout); + m_PipelineLayout.Finalize(LogicalDevice); + + // Create shader modules and initialize shader stages + std::array<VkPipelineShaderStageCreateInfo, MaxShadersInPipeline> ShaderStages = {}; + for (Uint32 s = 0; s < m_NumShaders; ++s) + { + auto *pShader = m_ppShaders[s]; + auto ShaderType = pShader->GetDesc().ShaderType; + + auto &StageCI = ShaderStages[s]; + StageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + StageCI.pNext = nullptr; + StageCI.flags = 0; // reserved for future use + switch (ShaderType) + { + case SHADER_TYPE_VERTEX: StageCI.stage = VK_SHADER_STAGE_VERTEX_BIT; break; + case SHADER_TYPE_HULL: StageCI.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; break; + case SHADER_TYPE_DOMAIN: StageCI.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; break; + case SHADER_TYPE_GEOMETRY: StageCI.stage = VK_SHADER_STAGE_GEOMETRY_BIT; break; + case SHADER_TYPE_PIXEL: StageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; break; + case SHADER_TYPE_COMPUTE: StageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT; break; + default: UNEXPECTED("Unknown shader type"); + } + + VkShaderModuleCreateInfo ShaderModuleCI = {}; + ShaderModuleCI.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + ShaderModuleCI.pNext = nullptr; + ShaderModuleCI.flags = 0; + const auto& SPIRV = ShaderSPIRVs[s]; + ShaderModuleCI.codeSize = SPIRV.size() * sizeof(uint32_t); + ShaderModuleCI.pCode = SPIRV.data(); + m_ShaderModules[s] = LogicalDevice.CreateShaderModule(ShaderModuleCI, m_Desc.Name); + + StageCI.module = m_ShaderModules[s]; + StageCI.pName = "main"; // entry point + StageCI.pSpecializationInfo = nullptr; + } + + // Create pipeline if (m_Desc.IsComputePipeline) { auto &ComputePipeline = m_Desc.ComputePipeline; @@ -175,21 +202,9 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren PipelineCI.basePipelineHandle = VK_NULL_HANDLE; // a pipeline to derive from PipelineCI.basePipelineIndex = 0; // an index into the pCreateInfos parameter to use as a pipeline to derive from - auto &CSStage = PipelineCI.stage; - CSStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - CSStage.pNext = nullptr; - CSStage.flags = 0; // reserved for future use - CSStage.stage = VK_SHADER_STAGE_COMPUTE_BIT; - - ParseResourceLayoutAndCreateShader(ComputePipeline.pCS, 0); - CSStage.module = m_ShaderModules[0]; - - m_PipelineLayout.Finalize(LogicalDevice); + PipelineCI.stage = ShaderStages[0]; PipelineCI.layout = m_PipelineLayout.GetVkPipelineLayout(); - CSStage.pName = "main"; - CSStage.pSpecializationInfo = nullptr; - m_Pipeline = LogicalDevice.CreateComputePipeline(PipelineCI, VK_NULL_HANDLE, m_Desc.Name); } else @@ -208,37 +223,8 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren #endif PipelineCI.stageCount = m_NumShaders; - std::vector<VkPipelineShaderStageCreateInfo> Stages(PipelineCI.stageCount); - for (Uint32 s = 0; s < m_NumShaders; ++s) - { - auto *pShader = m_ppShaders[s]; - auto ShaderType = pShader->GetDesc().ShaderType; - - auto &StageCI = Stages[s]; - StageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - StageCI.pNext = nullptr; - StageCI.flags = 0; // reserved for future use - switch(ShaderType) - { - case SHADER_TYPE_VERTEX: StageCI.stage = VK_SHADER_STAGE_VERTEX_BIT; break; - case SHADER_TYPE_HULL: StageCI.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; break; - case SHADER_TYPE_DOMAIN: StageCI.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; break; - case SHADER_TYPE_GEOMETRY: StageCI.stage = VK_SHADER_STAGE_GEOMETRY_BIT; break; - case SHADER_TYPE_PIXEL: StageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; break; - default: UNEXPECTED("Unknown shader type"); - } - - ParseResourceLayoutAndCreateShader(pShader, s); - - StageCI.module = m_ShaderModules[s]; - StageCI.pName = "main"; // entry point - StageCI.pSpecializationInfo = nullptr; - } - - m_PipelineLayout.Finalize(LogicalDevice); + PipelineCI.pStages = ShaderStages.data(); PipelineCI.layout = m_PipelineLayout.GetVkPipelineLayout(); - - PipelineCI.pStages = Stages.data(); VkPipelineVertexInputStateCreateInfo VertexInputStateCI = {}; std::array<VkVertexInputBindingDescription, iMaxLayoutElements> BindingDescriptions; diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index e6297171..74a20524 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -78,7 +78,7 @@ ShaderResourceCacheVk::~ShaderResourceCacheVk() { if (m_pMemory) { - auto *pResources = reinterpret_cast<Resource*>( reinterpret_cast<DescriptorSet*>(m_pMemory) + m_NumSets); + auto *pResources = GetFirstResourcePtr(); for(Uint32 res=0; res < m_TotalResources; ++res) pResources[res].~Resource(); for (Uint32 t = 0; t < m_NumSets; ++t) @@ -91,7 +91,7 @@ ShaderResourceCacheVk::~ShaderResourceCacheVk() template<bool VerifyOnly> void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl *pCtxVkImpl) { - auto *pResources = reinterpret_cast<Resource*>(reinterpret_cast<DescriptorSet*>(m_pMemory) + m_NumSets); + auto *pResources = GetFirstResourcePtr(); for (Uint32 res = 0; res < m_TotalResources; ++res) { auto &Res = pResources[res]; @@ -284,4 +284,60 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWrite return DescrImgInfo; } +void ShaderResourceCacheVk::GetDynamicBufferOffset(Uint32 CtxId, std::vector<uint32_t>& Offsets)const +{ + Offsets.clear(); + + // If any of the sets being bound include dynamic uniform or storage buffers, then + // pDynamicOffsets includes one element for each array element in each dynamic descriptor + // type binding in each set. Values are taken from pDynamicOffsets in an order such that + // all entries for set N come before set N+1; within a set, entries are ordered by the binding + // numbers (unclear if this is SPIRV binding or VkDescriptorSetLayoutBinding number) in the + // descriptor set layouts; and within a binding array, elements are in order. (13.2.5) + + // In each descriptor set, all uniform buffers for every shader stage come first, + // followed by all storage buffers for every shader stage, followed by all other resources + for(Uint32 set=0; set < m_NumSets; ++set) + { + 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<const BufferVkImpl>(); + auto Offset = pBufferVk->GetDynamicOffset(CtxId); + Offsets.emplace_back(Offset); + + ++res; + } + + while (res < DescrSet.GetSize()) + { + const auto& Res = DescrSet.GetResource(res); + if (Res.Type != SPIRVShaderResourceAttribs::ResourceType::StorageBuffer) + break; + + const auto* pBufferVkView = Res.pObject.RawPtr<const BufferViewVkImpl>(); + const auto* pBufferVk = pBufferVkView->GetBufferVk(); + auto Offset = pBufferVk->GetDynamicOffset(CtxId); + Offsets.emplace_back(Offset); + + ++res; + } + +#ifdef _DEBUG + for(; res < DescrSet.GetSize(); ++res) + { + const auto& Res = DescrSet.GetResource(res); + VERIFY(Res.Type != SPIRVShaderResourceAttribs::ResourceType::UniformBuffer && + Res.Type != SPIRVShaderResourceAttribs::ResourceType::StorageBuffer, + "All uniform and storage buffers are expected to go first in the beginning of each descriptor set"); + } +#endif + } +} + } diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp index 288270b0..f98e7b16 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp @@ -53,11 +53,41 @@ ShaderResourceLayoutVk::~ShaderResourceLayoutVk() Resources[r].~VkResource(); } -void ShaderResourceLayoutVk::AllocateMemory(IMemoryAllocator &Allocator) +void ShaderResourceLayoutVk::AllocateMemory(std::shared_ptr<const SPIRVShaderResources> pSrcResources, + IMemoryAllocator& Allocator, + const SHADER_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes) { + VERIFY(!m_ResourceBuffer, "Memory has already been initialized"); + VERIFY_EXPR(!m_pResources); + VERIFY_EXPR(pSrcResources); + + m_pResources = std::move(pSrcResources); + + Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); + + // Count number of resources to allocate all needed memory + m_pResources->ProcessResources( + AllowedVarTypes, NumAllowedTypes, + + [&](const SPIRVShaderResourceAttribs &ResAttribs, Uint32) + { + VERIFY_EXPR(IsAllowedType(ResAttribs.VarType, AllowedTypeBits)); + VERIFY( Uint32{m_NumResources[ResAttribs.VarType]} + 1 <= Uint32{std::numeric_limits<Uint16>::max()}, "Number of resources exceeds max representable value"); + ++m_NumResources[ResAttribs.VarType]; + } + ); + + Uint32 TotalResources = 0; + for(SHADER_VARIABLE_TYPE VarType = SHADER_VARIABLE_TYPE_STATIC; VarType < SHADER_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast<SHADER_VARIABLE_TYPE>(VarType+1)) + { + TotalResources += m_NumResources[VarType]; + } + VERIFY(TotalResources <= Uint32{std::numeric_limits<Uint16>::max()}, "Total number of resources exceeds Uint16 max representable value" ); + m_NumResources[SHADER_VARIABLE_TYPE_NUM_TYPES] = static_cast<Uint16>(TotalResources); + VERIFY( &m_ResourceBuffer.get_deleter().m_Allocator == &Allocator, "Inconsistent memory allocators" ); - Uint32 TotalResource = GetTotalResourceCount(); - size_t MemSize = TotalResource * sizeof(VkResource); + size_t MemSize = TotalResources * sizeof(VkResource); if(MemSize == 0) return; @@ -65,156 +95,185 @@ void ShaderResourceLayoutVk::AllocateMemory(IMemoryAllocator &Allocator) m_ResourceBuffer.reset(pRawMem); } -void ShaderResourceLayoutVk::Initialize(const std::shared_ptr<const SPIRVShaderResources>& pSrcResources, - IMemoryAllocator& LayoutDataAllocator, - const SHADER_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - ShaderResourceCacheVk* pStaticResourceCache, - std::vector<uint32_t>* pSPIRV, - PipelineLayout* pPipelineLayout) +void ShaderResourceLayoutVk::InitializeStaticResourceLayout(std::shared_ptr<const SPIRVShaderResources> pSrcResources, + IMemoryAllocator& LayoutDataAllocator, + ShaderResourceCacheVk& StaticResourceCache) { - m_pResources = pSrcResources; + auto AllowedVarType = SHADER_VARIABLE_TYPE_STATIC; + AllocateMemory(std::move(pSrcResources), LayoutDataAllocator, &AllowedVarType, 1); - VERIFY_EXPR( (pStaticResourceCache != nullptr) ^ (pPipelineLayout != nullptr && pSPIRV != nullptr) ); - - Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); + std::array<Uint32, SHADER_VARIABLE_TYPE_NUM_TYPES> CurrResInd = {}; + Uint32 StaticResCacheSize = 0; - // Count number of resources to allocate all needed memory m_pResources->ProcessResources( - AllowedVarTypes, NumAllowedTypes, - - [&](const SPIRVShaderResourceAttribs &UB, Uint32) - { - VERIFY_EXPR(IsAllowedType(UB.VarType, AllowedTypeBits)); - ++m_NumResources[UB.VarType]; - }, - [&](const SPIRVShaderResourceAttribs& SB, Uint32) + &AllowedVarType, 1, + [&](const SPIRVShaderResourceAttribs &Attribs, Uint32) { - VERIFY_EXPR(IsAllowedType(SB.VarType, AllowedTypeBits)); - ++m_NumResources[SB.VarType]; - }, - [&](const SPIRVShaderResourceAttribs &Img, Uint32) - { - VERIFY_EXPR(IsAllowedType(Img.VarType, AllowedTypeBits)); - ++m_NumResources[Img.VarType]; - }, - [&](const SPIRVShaderResourceAttribs &SmplImg, Uint32) - { - VERIFY_EXPR(IsAllowedType(SmplImg.VarType, AllowedTypeBits)); - ++m_NumResources[SmplImg.VarType]; - }, - [&](const SPIRVShaderResourceAttribs &AC, Uint32) - { - VERIFY_EXPR(IsAllowedType(AC.VarType, AllowedTypeBits)); - ++m_NumResources[AC.VarType]; - }, - [&](const SPIRVShaderResourceAttribs &SepImg, Uint32) - { - VERIFY_EXPR(IsAllowedType(SepImg.VarType, AllowedTypeBits)); - ++m_NumResources[SepImg.VarType]; - }, - [&](const SPIRVShaderResourceAttribs &SepSmpl, Uint32) - { - VERIFY_EXPR(IsAllowedType(SepSmpl.VarType, AllowedTypeBits)); - ++m_NumResources[SepSmpl.VarType]; + Uint32 Binding = Attribs.Type; + Uint32 DescriptorSet = 0; + Uint32 CacheOffset = StaticResCacheSize; + StaticResCacheSize += Attribs.ArraySize; + ::new (&GetResource(Attribs.VarType, CurrResInd[Attribs.VarType]++)) VkResource(*this, Attribs, Binding, DescriptorSet, CacheOffset); } ); +#ifdef _DEBUG + for (SHADER_VARIABLE_TYPE VarType = SHADER_VARIABLE_TYPE_STATIC; VarType < SHADER_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast<SHADER_VARIABLE_TYPE>(VarType + 1)) + { + VERIFY(CurrResInd[VarType] == m_NumResources[VarType], "Not all resources are initialized, which will cause a crash when dtor is called"); + } +#endif + + StaticResourceCache.InitializeSets(GetRawAllocator(), 1, &StaticResCacheSize); + InitializeResourceMemoryInCache(StaticResourceCache); +} - AllocateMemory(LayoutDataAllocator); - std::array<Uint32, SHADER_VARIABLE_TYPE_NUM_TYPES> CurrResInd = {}; - Uint32 StaticResCacheSize = 0; +void ShaderResourceLayoutVk::Initialize(Uint32 NumShaders, + ShaderResourceLayoutVk Layouts[], + std::shared_ptr<const SPIRVShaderResources> pShaderResources[], + IMemoryAllocator& LayoutDataAllocator, + std::vector<uint32_t> SPIRVs[], + class PipelineLayout& PipelineLayout) +{ + SHADER_VARIABLE_TYPE* AllowedVarTypes = nullptr; + Uint32 NumAllowedTypes = 0; + Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); - auto AddResource = [&](const SPIRVShaderResourceAttribs &Attribs) + for(Uint32 s=0; s < NumShaders; ++s) + { + Layouts[s].AllocateMemory(std::move(pShaderResources[s]), LayoutDataAllocator, AllowedVarTypes, NumAllowedTypes); + } + + VERIFY_EXPR(NumShaders <= MaxShadersInPipeline); + std::array<std::array<Uint32, SHADER_VARIABLE_TYPE_NUM_TYPES>, MaxShadersInPipeline> CurrResInd = {}; +#ifdef _DEBUG + std::unordered_map<Uint32, std::pair<Uint32, Uint32>> dbgBindings_CacheOffsets; +#endif + + auto AddResource = [&](Uint32 ShaderInd, + ShaderResourceLayoutVk& ResLayout, + const SPIRVShaderResources& Resources, + const SPIRVShaderResourceAttribs& Attribs) { Uint32 Binding = 0; Uint32 DescriptorSet = 0; Uint32 CacheOffset = 0; - if (pPipelineLayout) - { - VERIFY_EXPR(pSPIRV != nullptr); - auto *pStaticSampler = m_pResources->GetStaticSampler(Attribs); - VkSampler vkStaticSampler = VK_NULL_HANDLE; - if(pStaticSampler != nullptr) - vkStaticSampler = ValidatedCast<SamplerVkImpl>(pStaticSampler)->GetVkSampler(); - pPipelineLayout->AllocateResourceSlot(Attribs, vkStaticSampler, m_pResources->GetShaderType(), DescriptorSet, Binding, CacheOffset, *pSPIRV); - VERIFY(DescriptorSet <= std::numeric_limits<decltype(VkResource::DescriptorSet)>::max(), "Descriptor set (", DescriptorSet, ") excceeds representable max value"); - VERIFY(Binding <= std::numeric_limits<decltype(VkResource::Binding)>::max(), "Binding (", Binding, ") excceeds representable max value"); - } - else - { - // If pipeline layout is not provided - use artifial layout to store - // static shader resources in one large continuous descriptor set - VERIFY_EXPR(pStaticResourceCache != nullptr); - DescriptorSet = 0; - CacheOffset = StaticResCacheSize; - Binding = Attribs.Type; - StaticResCacheSize += Attribs.ArraySize; + auto* pStaticSampler = Resources.GetStaticSampler(Attribs); + VkSampler vkStaticSampler = VK_NULL_HANDLE; + if (pStaticSampler != nullptr) + vkStaticSampler = ValidatedCast<SamplerVkImpl>(pStaticSampler)->GetVkSampler(); + + auto& ShaderSPIRV = SPIRVs[ShaderInd]; + PipelineLayout.AllocateResourceSlot(Attribs, vkStaticSampler, Resources.GetShaderType(), DescriptorSet, Binding, CacheOffset, ShaderSPIRV); + VERIFY(DescriptorSet <= std::numeric_limits<decltype(VkResource::DescriptorSet)>::max(), "Descriptor set (", DescriptorSet, ") excceeds max representable value"); + VERIFY(Binding <= std::numeric_limits<decltype(VkResource::Binding)>::max(), "Binding (", Binding, ") excceeds max representable value"); + +#ifdef _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 - ::new (&GetResource(Attribs.VarType, CurrResInd[Attribs.VarType]++)) VkResource( *this, Attribs, Binding, DescriptorSet, CacheOffset); + auto& ResInd = CurrResInd[ShaderInd][Attribs.VarType]; + ::new (&ResLayout.GetResource(Attribs.VarType, ResInd++)) VkResource(ResLayout, Attribs, Binding, DescriptorSet, CacheOffset); }; - m_pResources->ProcessResources( - AllowedVarTypes, NumAllowedTypes, - - [&](const SPIRVShaderResourceAttribs &UB, Uint32) - { - VERIFY_EXPR(IsAllowedType(UB.VarType, AllowedTypeBits)); - AddResource(UB); - }, - [&](const SPIRVShaderResourceAttribs& SB, Uint32) - { - VERIFY_EXPR(IsAllowedType(SB.VarType, AllowedTypeBits)); - AddResource(SB); - }, - [&](const SPIRVShaderResourceAttribs &Img, Uint32) - { - VERIFY_EXPR(IsAllowedType(Img.VarType, AllowedTypeBits)); - AddResource(Img); - }, - [&](const SPIRVShaderResourceAttribs &SmplImg, Uint32) - { - VERIFY_EXPR(IsAllowedType(SmplImg.VarType, AllowedTypeBits)); - AddResource(SmplImg); - }, - [&](const SPIRVShaderResourceAttribs &AC, Uint32) - { - VERIFY_EXPR(IsAllowedType(AC.VarType, AllowedTypeBits)); - AddResource(AC); - }, - [&](const SPIRVShaderResourceAttribs &SepImg, Uint32) + // First process uniform buffers for all shader stages to make sure all UBs go first in every descriptor set + for (Uint32 s = 0; s < NumShaders; ++s) + { + auto& Layout = Layouts[s]; + const auto& Resources = *Layout.m_pResources; + for (Uint32 n = 0; n < Resources.GetNumUBs(); ++n) { - VERIFY_EXPR(IsAllowedType(SepImg.VarType, AllowedTypeBits)); - AddResource(SepImg); - }, + const auto& UB = Resources.GetUB(n); + if (IsAllowedType(UB.VarType, AllowedTypeBits)) + { + AddResource(s, Layout, Resources, UB); + } + } + } - [&](const SPIRVShaderResourceAttribs &SepSmpl, Uint32) + // Second, process all storage buffers + for (Uint32 s = 0; s < NumShaders; ++s) + { + auto& Layout = Layouts[s]; + const auto& Resources = *Layout.m_pResources; + for (Uint32 n = 0; n < Resources.GetNumSBs(); ++n) { - VERIFY_EXPR(IsAllowedType(SepSmpl.VarType, AllowedTypeBits)); - AddResource(SepSmpl); + const auto& SB = Resources.GetSB(n); + if (IsAllowedType(SB.VarType, AllowedTypeBits)) + { + AddResource(s, Layout, Resources, SB); + } } - ); + } -#ifdef _DEBUG - for(SHADER_VARIABLE_TYPE VarType = SHADER_VARIABLE_TYPE_STATIC; VarType < SHADER_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast<SHADER_VARIABLE_TYPE>(VarType+1)) + // Finally, process all other resource types + for (Uint32 s = 0; s < NumShaders; ++s) { - VERIFY( CurrResInd[VarType] == m_NumResources[VarType], "Not all resources are initialized, which will cause a crash when dtor is called" ); + auto& Layout = Layouts[s]; + const auto& Resources = *Layout.m_pResources; + Resources.ProcessResources( + AllowedVarTypes, NumAllowedTypes, + + [&](const SPIRVShaderResourceAttribs &UB, Uint32) + { + VERIFY_EXPR(IsAllowedType(UB.VarType, AllowedTypeBits)); + // Skip + }, + [&](const SPIRVShaderResourceAttribs& SB, Uint32) + { + VERIFY_EXPR(IsAllowedType(SB.VarType, AllowedTypeBits)); + // Skip + }, + [&](const SPIRVShaderResourceAttribs &Img, Uint32) + { + VERIFY_EXPR(IsAllowedType(Img.VarType, AllowedTypeBits)); + AddResource(s, Layout, Resources, Img); + }, + [&](const SPIRVShaderResourceAttribs &SmplImg, Uint32) + { + VERIFY_EXPR(IsAllowedType(SmplImg.VarType, AllowedTypeBits)); + AddResource(s, Layout, Resources, SmplImg); + }, + [&](const SPIRVShaderResourceAttribs &AC, Uint32) + { + VERIFY_EXPR(IsAllowedType(AC.VarType, AllowedTypeBits)); + AddResource(s, Layout, Resources, AC); + }, + [&](const SPIRVShaderResourceAttribs &SepImg, Uint32) + { + VERIFY_EXPR(IsAllowedType(SepImg.VarType, AllowedTypeBits)); + AddResource(s, Layout, Resources, SepImg); + }, + + [&](const SPIRVShaderResourceAttribs &SepSmpl, Uint32) + { + VERIFY_EXPR(IsAllowedType(SepSmpl.VarType, AllowedTypeBits)); + AddResource(s, Layout, Resources, SepSmpl); + } + ); } -#endif - if(pStaticResourceCache) +#ifdef _DEBUG + for (Uint32 s = 0; s < NumShaders; ++s) { - // Initialize resource cache to store static resources - VERIFY_EXPR(pPipelineLayout == nullptr && pSPIRV == nullptr); - pStaticResourceCache->InitializeSets(GetRawAllocator(), 1, &StaticResCacheSize); - InitializeResourceMemoryInCache(*pStaticResourceCache); + auto& Layout = Layouts[s]; + for (SHADER_VARIABLE_TYPE VarType = SHADER_VARIABLE_TYPE_STATIC; VarType < SHADER_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast<SHADER_VARIABLE_TYPE>(VarType + 1)) + { + VERIFY(CurrResInd[s][VarType] == Layout.m_NumResources[VarType], "Not all resources are initialized, which will cause a crash when dtor is called"); + } } +#endif } - #define LOG_RESOURCE_BINDING_ERROR(ResType, pResource, VarName, ShaderName, ...)\ { \ const auto &ResName = pResource->GetDesc().Name; \ diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp index bfcf2a4a..ed1eabc0 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp @@ -59,13 +59,9 @@ ShaderVkImpl::ShaderVkImpl(IReferenceCounters *pRefCounters, RenderDeviceVkImpl auto *pResources = new (pRawMem) SPIRVShaderResources(Allocator, pRenderDeviceVk, m_SPIRV, m_Desc); m_pShaderResources.reset(pResources, STDDeleterRawMem<SPIRVShaderResources>(Allocator)); - // Clone only static resources that will be set directly through the shader object - std::array<SHADER_VARIABLE_TYPE, 1> VarTypes = {SHADER_VARIABLE_TYPE_STATIC}; - m_StaticResLayout.Initialize(m_pShaderResources, GetRawAllocator(), - VarTypes.data(), static_cast<Uint32>(VarTypes.size()), - &m_StaticResCache, nullptr, nullptr); - m_StaticVarsMgr.Initialize(m_StaticResLayout, GetRawAllocator(), VarTypes.data(), - static_cast<Uint32>(VarTypes.size()), m_StaticResCache); + m_StaticResLayout.InitializeStaticResourceLayout(m_pShaderResources, GetRawAllocator(), m_StaticResCache); + // m_StaticResLayout only contains static resources, so reference all of them + m_StaticVarsMgr.Initialize(m_StaticResLayout, GetRawAllocator(), nullptr, 0, m_StaticResCache); } ShaderVkImpl::~ShaderVkImpl() |
