diff options
| author | assiduous <assiduous@diligentgraphics.com> | 2020-12-15 19:32:55 +0000 |
|---|---|---|
| committer | assiduous <assiduous@diligentgraphics.com> | 2020-12-15 19:32:55 +0000 |
| commit | 2cbe12c8541f63719ae3662cda827ed53004c0b2 (patch) | |
| tree | 6ff68d7ee7d386d1c0e30c00fcd551fcbf819c6c /Graphics/GraphicsEngineVulkan | |
| parent | Math Lib: added Matrix4x4 constructor from float4 rows (diff) | |
| parent | BasicMath: minor code formatting (diff) | |
| download | DiligentCore-2cbe12c8541f63719ae3662cda827ed53004c0b2.tar.gz DiligentCore-2cbe12c8541f63719ae3662cda827ed53004c0b2.zip | |
Merge branch 'azhirnov-ray_tracing_2'
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
55 files changed, 3878 insertions, 785 deletions
diff --git a/Graphics/GraphicsEngineVulkan/CMakeLists.txt b/Graphics/GraphicsEngineVulkan/CMakeLists.txt index 1701c341..e5cd319c 100644 --- a/Graphics/GraphicsEngineVulkan/CMakeLists.txt +++ b/Graphics/GraphicsEngineVulkan/CMakeLists.txt @@ -36,6 +36,9 @@ set(INCLUDE include/VulkanErrors.hpp include/VulkanTypeConversions.hpp include/VulkanUploadHeap.hpp + include/BottomLevelASVkImpl.hpp + include/TopLevelASVkImpl.hpp + include/ShaderBindingTableVkImpl.hpp ) set(VULKAN_UTILS_INCLUDE @@ -70,6 +73,9 @@ set(INTERFACE interface/SwapChainVk.h interface/TextureVk.h interface/TextureViewVk.h + interface/BottomLevelASVk.h + interface/TopLevelASVk.h + interface/ShaderBindingTableVk.h ) @@ -104,6 +110,9 @@ set(SRC src/TextureViewVkImpl.cpp src/VulkanTypeConversions.cpp src/VulkanUploadHeap.cpp + src/BottomLevelASVkImpl.cpp + src/TopLevelASVkImpl.cpp + src/ShaderBindingTableVkImpl.cpp ) set(VULKAN_UTILS_SRC diff --git a/Graphics/GraphicsEngineVulkan/include/BottomLevelASVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/BottomLevelASVkImpl.hpp new file mode 100644 index 00000000..d67fe937 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/BottomLevelASVkImpl.hpp @@ -0,0 +1,80 @@ +/* + * Copyright 2019-2020 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 +/// Definition of the Diligent::BottomLevelASVkImpl class + +#include "RenderDeviceVk.h" +#include "RenderDeviceVkImpl.hpp" +#include "BottomLevelASVk.h" +#include "BottomLevelASBase.hpp" +#include "VulkanUtilities/VulkanObjectWrappers.hpp" + +namespace Diligent +{ + +class BottomLevelASVkImpl final : public BottomLevelASBase<IBottomLevelASVk, RenderDeviceVkImpl> +{ +public: + using TBottomLevelASBase = BottomLevelASBase<IBottomLevelASVk, RenderDeviceVkImpl>; + + BottomLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const BottomLevelASDesc& Desc); + BottomLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const BottomLevelASDesc& Desc, + RESOURCE_STATE InitialState, + VkAccelerationStructureKHR vkBLAS); + ~BottomLevelASVkImpl(); + + /// Implementation of IBottomLevelAS::GetNativeHandle() in Vulkan backend. + virtual void* DILIGENT_CALL_TYPE GetNativeHandle() override final + { + auto Handle = GetVkBLAS(); + return reinterpret_cast<void*>(Handle); + } + + /// Implementation of IBottomLevelASVk::GetVkBLAS(). + virtual VkAccelerationStructureKHR DILIGENT_CALL_TYPE GetVkBLAS() const override { return m_VulkanBLAS; } + + /// Implementation of IBottomLevelASVk::GetVkDeviceAddress(). + virtual VkDeviceAddress DILIGENT_CALL_TYPE GetVkDeviceAddress() const override { return m_DeviceAddress; } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_BottomLevelASVk, TBottomLevelASBase); + +private: + VkDeviceAddress m_DeviceAddress = 0; + VulkanUtilities::AccelStructWrapper m_VulkanBLAS; + VulkanUtilities::BufferWrapper m_VulkanBuffer; + VulkanUtilities::VulkanMemoryAllocation m_MemoryAllocation; + VkDeviceSize m_MemoryAlignedOffset = 0; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.hpp index de95de16..90bfb884 100644 --- a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.hpp @@ -106,6 +106,9 @@ public: /// Implementation of IBufferVk::GetAccessFlags(). virtual VkAccessFlags DILIGENT_CALL_TYPE GetAccessFlags() const override final; + /// Implementation of IBufferVk::GetVkDeviceAddress(). + virtual VkDeviceAddress DILIGENT_CALL_TYPE GetVkDeviceAddress() const override final; + bool CheckAccessFlags(VkAccessFlags AccessFlags) const { return (GetAccessFlags() & AccessFlags) == AccessFlags; diff --git a/Graphics/GraphicsEngineVulkan/include/DescriptorPoolManager.hpp b/Graphics/GraphicsEngineVulkan/include/DescriptorPoolManager.hpp index 98438da3..b60f3d89 100644 --- a/Graphics/GraphicsEngineVulkan/include/DescriptorPoolManager.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DescriptorPoolManager.hpp @@ -137,17 +137,7 @@ public: std::string PoolName, std::vector<VkDescriptorPoolSize> PoolSizes, uint32_t MaxSets, - bool AllowFreeing) noexcept: - m_DeviceVkImpl{DeviceVkImpl }, - m_PoolName {std::move(PoolName) }, - m_PoolSizes (std::move(PoolSizes)), - m_MaxSets {MaxSets }, - m_AllowFreeing{AllowFreeing } - { -#ifdef DILIGENT_DEVELOPMENT - m_AllocatedPoolCounter = 0; -#endif - } + bool AllowFreeing) noexcept; ~DescriptorPoolManager(); DescriptorPoolManager (const DescriptorPoolManager&) = delete; diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index 093ab8d9..3ff38c4f 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -50,6 +50,9 @@ #include "HashUtils.hpp" #include "ManagedVulkanObject.hpp" #include "QueryManagerVk.hpp" +#include "BottomLevelASVkImpl.hpp" +#include "TopLevelASVkImpl.hpp" +#include "ShaderBindingTableVkImpl.hpp" namespace Diligent @@ -65,6 +68,8 @@ struct DeviceContextVkImplTraits using QueryType = QueryVkImpl; using FramebufferType = FramebufferVkImpl; using RenderPassType = RenderPassVkImpl; + using BottomLevelASType = BottomLevelASVkImpl; + using TopLevelASType = TopLevelASVkImpl; }; /// Device context implementation in Vulkan backend. @@ -249,6 +254,27 @@ public: /// Implementation of IDeviceContext::Flush() in Vulkan backend. virtual void DILIGENT_CALL_TYPE Flush() override final; + /// Implementation of IDeviceContext::BuildBLAS() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE BuildBLAS(const BuildBLASAttribs& Attribs) override final; + + /// Implementation of IDeviceContext::BuildTLAS() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE BuildTLAS(const BuildTLASAttribs& Attribs) override final; + + /// Implementation of IDeviceContext::CopyBLAS() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE CopyBLAS(const CopyBLASAttribs& Attribs) override final; + + /// Implementation of IDeviceContext::CopyTLAS() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE CopyTLAS(const CopyTLASAttribs& Attribs) override final; + + /// Implementation of IDeviceContext::WriteBLASCompactedSize() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE WriteBLASCompactedSize(const WriteBLASCompactedSizeAttribs& Attribs) override final; + + /// Implementation of IDeviceContext::WriteTLASCompactedSize() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE WriteTLASCompactedSize(const WriteTLASCompactedSizeAttribs& Attribs) override final; + + /// Implementation of IDeviceContext::TraceRays() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE TraceRays(const TraceRaysAttribs& Attribs) override final; + // Transitions texture subresources from OldState to NewState, and optionally updates // internal texture state. // If OldState == RESOURCE_STATE_UNKNOWN, internal texture state is used as old state. @@ -279,6 +305,20 @@ public: virtual void DILIGENT_CALL_TYPE BufferMemoryBarrier(IBuffer* pBuffer, VkAccessFlags NewAccessFlags) override final; + // Transitions BLAS state from OldState to NewState, and optionally updates internal state. + // If OldState == RESOURCE_STATE_UNKNOWN, internal BLAS state is used as old state. + void TransitionBLASState(BottomLevelASVkImpl& BLAS, + RESOURCE_STATE OldState, + RESOURCE_STATE NewState, + bool UpdateInternalState); + + // Transitions TLAS state from OldState to NewState, and optionally updates internal state. + // If OldState == RESOURCE_STATE_UNKNOWN, internal TLAS state is used as old state. + void TransitionTLASState(TopLevelASVkImpl& TLAS, + RESOURCE_STATE OldState, + RESOURCE_STATE NewState, + bool UpdateInternalState); + void AddWaitSemaphore(ManagedSemaphore* pWaitSemaphore, VkPipelineStageFlags WaitDstStageMask) { VERIFY_EXPR(pWaitSemaphore != nullptr); @@ -369,6 +409,15 @@ private: VkImageLayout ExpectedLayout, const char* OperationName); + __forceinline void TransitionOrVerifyBLASState(BottomLevelASVkImpl& BLAS, + RESOURCE_STATE_TRANSITION_MODE TransitionMode, + RESOURCE_STATE RequiredState, + const char* OperationName); + + __forceinline void TransitionOrVerifyTLASState(TopLevelASVkImpl& TLAS, + RESOURCE_STATE_TRANSITION_MODE TransitionMode, + RESOURCE_STATE RequiredState, + const char* OperationName); __forceinline void EnsureVkCmdBuffer() { @@ -407,9 +456,12 @@ private: __forceinline void PrepareForIndexedDraw(DRAW_FLAGS Flags, VALUE_TYPE IndexType); __forceinline BufferVkImpl* PrepareIndirectDrawAttribsBuffer(IBuffer* pAttribsBuffer, RESOURCE_STATE_TRANSITION_MODE TransitonMode); __forceinline void PrepareForDispatchCompute(); + __forceinline void PrepareForRayTracing(); void DvpLogRenderPass_PSOMismatch(); + void CreateASCompactedSizeQueryPool(); + VulkanUtilities::VulkanCommandBuffer m_CommandBuffer; struct ContextState @@ -490,6 +542,8 @@ private: Int32 m_ActiveQueriesCounter = 0; std::vector<VkClearValue> m_vkClearValues; + + VulkanUtilities::QueryPoolWrapper m_ASQueryPool; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp index 73fced1f..f9f51920 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayout.hpp @@ -48,7 +48,7 @@ class ShaderResourceCacheVk; class PipelineLayout { public: - static VkDescriptorType GetVkDescriptorType(const SPIRVShaderResourceAttribs& Res); + static VkDescriptorType GetVkDescriptorType(SPIRVShaderResourceAttribs::ResourceType Type); PipelineLayout(); void Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask); @@ -69,8 +69,7 @@ public: SHADER_TYPE ShaderType, Uint32& DescriptorSet, Uint32& Binding, - Uint32& OffsetInCache, - std::vector<uint32_t>& SPIRV); + Uint32& OffsetInCache); Uint32 GetTotalDescriptors(SHADER_RESOURCE_VARIABLE_TYPE VarType) const { @@ -137,7 +136,7 @@ public: // set by the same Vulkan command. If there are no dynamic descriptors, this // function also binds descriptor sets rightaway. void PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, - bool IsCompute, + VkPipelineBindPoint BindPoint, const ShaderResourceCacheVk& ResourceCache, DescriptorSetBindInfo& BindInfo, VkDescriptorSet VkDynamicDescrSet) const; diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index d4f2fc36..6aa4e073 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -59,6 +59,7 @@ public: PipelineStateVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, const GraphicsPipelineStateCreateInfo& CreateInfo); PipelineStateVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, const ComputePipelineStateCreateInfo& CreateInfo); + PipelineStateVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, const RayTracingPipelineStateCreateInfo& CreateInfo); ~PipelineStateVkImpl(); virtual void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final; @@ -128,10 +129,11 @@ public: private: using TShaderStages = ShaderResourceLayoutVk::TShaderStages; - template <typename PSOCreateInfoType> + template <typename PSOCreateInfoType, typename InitPSODescType> void InitInternalObjects(const PSOCreateInfoType& CreateInfo, std::vector<VkPipelineShaderStageCreateInfo>& vkShaderStages, - std::vector<VulkanUtilities::ShaderModuleWrapper>& ShaderModules); + std::vector<VulkanUtilities::ShaderModuleWrapper>& ShaderModules, + InitPSODescType InitPSODesc); void InitResourceLayouts(const PipelineStateCreateInfo& CreateInfo, TShaderStages& ShaderStages); @@ -168,7 +170,8 @@ private: // Resource layout index in m_ShaderResourceLayouts array for every shader stage, // indexed by the shader type pipeline index (returned by GetShaderTypePipelineIndex) - std::array<Int8, MAX_SHADERS_IN_PIPELINE> m_ResourceLayoutIndex = {-1, -1, -1, -1, -1}; + std::array<Int8, MAX_SHADERS_IN_PIPELINE> m_ResourceLayoutIndex = {-1, -1, -1, -1, -1, -1}; + static_assert(MAX_SHADERS_IN_PIPELINE == 6, "Please update the initializer list above"); bool m_HasStaticResources = false; bool m_HasNonStaticResources = false; diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp index 469e8119..2b8408ce 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp @@ -78,6 +78,9 @@ public: /// Implementation of IRenderDevice::CreateComputePipelineState() in Vulkan backend. virtual void DILIGENT_CALL_TYPE CreateComputePipelineState(const ComputePipelineStateCreateInfo& PSOCreateInfo, IPipelineState** ppPipelineState) override final; + /// Implementation of IRenderDevice::CreateRayTracingPipelineState() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& PSOCreateInfo, IPipelineState** ppPipelineState) override final; + /// Implementation of IRenderDevice::CreateBuffer() in Vulkan backend. virtual void DILIGENT_CALL_TYPE CreateBuffer(const BufferDesc& BuffDesc, const BufferData* pBuffData, @@ -115,6 +118,18 @@ public: virtual void DILIGENT_CALL_TYPE CreateFramebuffer(const FramebufferDesc& Desc, IFramebuffer** ppFramebuffer) override final; + /// Implementation of IRenderDevice::CreateBLAS() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE CreateBLAS(const BottomLevelASDesc& Desc, + IBottomLevelAS** ppBLAS) override final; + + /// Implementation of IRenderDevice::CreateTLAS() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE CreateTLAS(const TopLevelASDesc& Desc, + ITopLevelAS** ppTLAS) override final; + + /// Implementation of IRenderDevice::CreateSBT() in Vulkan backend. + virtual void DILIGENT_CALL_TYPE CreateSBT(const ShaderBindingTableDesc& Desc, + IShaderBindingTable** ppSBT) override final; + /// Implementation of IRenderDeviceVk::GetVkDevice(). virtual VkDevice DILIGENT_CALL_TYPE GetVkDevice() override final { return m_LogicalVkDevice->GetVkDevice(); } @@ -136,6 +151,18 @@ public: RESOURCE_STATE InitialState, IBuffer** ppBuffer) override final; + /// Implementation of IRenderDeviceVk::CreateBLASFromVulkanResource(). + virtual void DILIGENT_CALL_TYPE CreateBLASFromVulkanResource(VkAccelerationStructureKHR vkBLAS, + const BottomLevelASDesc& Desc, + RESOURCE_STATE InitialState, + IBottomLevelAS** ppBLAS) override final; + + /// Implementation of IRenderDeviceVk::CreateTLASFromVulkanResource(). + virtual void DILIGENT_CALL_TYPE CreateTLASFromVulkanResource(VkAccelerationStructureKHR vkTLAS, + const TopLevelASDesc& Desc, + RESOURCE_STATE InitialState, + ITopLevelAS** ppTLAS) override final; + /// Implementation of IRenderDevice::IdleGPU() in Vulkan backend. virtual void DILIGENT_CALL_TYPE IdleGPU() override final; @@ -163,16 +190,16 @@ public: FramebufferCache& GetFramebufferCache() { return m_FramebufferCache; } RenderPassCache& GetImplicitRenderPassCache() { return m_ImplicitRenderPassCache; } - VulkanUtilities::VulkanMemoryAllocation AllocateMemory(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProperties) + VulkanUtilities::VulkanMemoryAllocation AllocateMemory(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProperties, VkMemoryAllocateFlags AllocateFlags = 0) { - return m_MemoryMgr.Allocate(MemReqs, MemoryProperties); + return m_MemoryMgr.Allocate(MemReqs, MemoryProperties, AllocateFlags); } - VulkanUtilities::VulkanMemoryAllocation AllocateMemory(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex) + VulkanUtilities::VulkanMemoryAllocation AllocateMemory(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, VkMemoryAllocateFlags AllocateFlags = 0) { const auto& MemoryProps = m_PhysicalDevice->GetMemoryProperties(); VERIFY_EXPR(MemoryTypeIndex < MemoryProps.memoryTypeCount); const auto MemoryFlags = MemoryProps.memoryTypes[MemoryTypeIndex].propertyFlags; - return m_MemoryMgr.Allocate(Size, Alignment, MemoryTypeIndex, (MemoryFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0); + return m_MemoryMgr.Allocate(Size, Alignment, MemoryTypeIndex, (MemoryFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0, AllocateFlags); } VulkanUtilities::VulkanMemoryManager& GetGlobalMemoryManager() { return m_MemoryMgr; } @@ -182,6 +209,21 @@ public: IDXCompiler* GetDxCompiler() const { return m_pDxCompiler.get(); } + struct Properties + { + const Uint32 ShaderGroupHandleSize; + const Uint32 MaxShaderRecordStride; + const Uint32 ShaderGroupBaseAlignment; + const Uint32 MaxDrawMeshTasksCount; + const Uint32 MaxRayTracingRecursionDepth; + const Uint32 MaxRayGenThreads; + }; + + const Properties& GetProperties() const + { + return m_Properties; + } + private: template <typename PSOCreateInfoType> void CreatePipelineState(const PSOCreateInfoType& PSOCreateInfo, IPipelineState** ppPipelineState); @@ -216,6 +258,8 @@ private: VulkanDynamicMemoryManager m_DynamicMemoryManager; std::unique_ptr<IDXCompiler> m_pDxCompiler; + + Properties m_Properties; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderBindingTableVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderBindingTableVkImpl.hpp new file mode 100644 index 00000000..6adc1677 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/ShaderBindingTableVkImpl.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2020 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 +/// Definition of the Diligent::ShaderBindingTableVkImpl class + +#include "RenderDeviceVk.h" +#include "RenderDeviceVkImpl.hpp" +#include "ShaderBindingTableVk.h" +#include "ShaderBindingTableBase.hpp" +#include "TopLevelASVkImpl.hpp" +#include "PipelineStateVkImpl.hpp" +#include "VulkanUtilities/VulkanObjectWrappers.hpp" + +namespace Diligent +{ + +class ShaderBindingTableVkImpl final : public ShaderBindingTableBase<IShaderBindingTableVk, PipelineStateVkImpl, TopLevelASVkImpl, RenderDeviceVkImpl> +{ +public: + using TShaderBindingTableBase = ShaderBindingTableBase<IShaderBindingTableVk, PipelineStateVkImpl, TopLevelASVkImpl, RenderDeviceVkImpl>; + + ShaderBindingTableVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const ShaderBindingTableDesc& Desc, + bool bIsDeviceInternal = false); + ~ShaderBindingTableVkImpl(); + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_ShaderBindingTableVk, TShaderBindingTableBase); +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp index 00f2c33a..0b427640 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceBindingVkImpl.hpp @@ -83,7 +83,8 @@ private: // Resource layout index in m_ShaderResourceCache array for every shader stage, // indexed by the shader type pipeline index (returned by GetShaderTypePipelineIndex) - std::array<Int8, MAX_SHADERS_IN_PIPELINE> m_ResourceLayoutIndex = {-1, -1, -1, -1, -1}; + std::array<Int8, MAX_SHADERS_IN_PIPELINE> 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 7d77ff48..0bcf79a0 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -112,12 +112,13 @@ public: /*1-7*/ // Unused /* 8 */ RefCntAutoPtr<IDeviceObject> pObject; - VkDescriptorBufferInfo GetUniformBufferDescriptorWriteInfo () const; - VkDescriptorBufferInfo GetStorageBufferDescriptorWriteInfo () const; - VkDescriptorImageInfo GetImageDescriptorWriteInfo (bool IsImmutableSampler)const; - VkBufferView GetBufferViewWriteInfo () const; - VkDescriptorImageInfo GetSamplerDescriptorWriteInfo() const; - VkDescriptorImageInfo GetInputAttachmentDescriptorWriteInfo() const; + VkDescriptorBufferInfo GetUniformBufferDescriptorWriteInfo () const; + VkDescriptorBufferInfo GetStorageBufferDescriptorWriteInfo () const; + VkDescriptorImageInfo GetImageDescriptorWriteInfo (bool IsImmutableSampler) const; + VkBufferView GetBufferViewWriteInfo () const; + VkDescriptorImageInfo GetSamplerDescriptorWriteInfo() const; + VkDescriptorImageInfo GetInputAttachmentDescriptorWriteInfo() const; + VkWriteDescriptorSetAccelerationStructureKHR GetAccelerationStructureWriteInfo() const; // clang-format on }; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp index 9452a390..88885eee 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceLayoutVk.hpp @@ -44,20 +44,9 @@ // d == m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC] // // +// Every ShaderVariableVkImpl variable managed by ShaderVariableManagerVk keeps a reference to corresponding VkResource. // -// * Every VkResource structure holds a reference to SPIRVShaderResourceAttribs structure from SPIRVShaderResources. -// * ShaderResourceLayoutVk keeps a shared pointer to SPIRVShaderResources instance. -// * Every ShaderVariableVkImpl variable managed by ShaderVariableManagerVk keeps a reference to corresponding VkResource. -// -// -// ______________________ ________________________________________________________________________ -// | | unique_ptr | | | | | | | | -// | SPIRVShaderResources |--------------->| UBs | SBs | StrgImgs | SmplImgs | ACs | SepSamplers | SepImgs | -// |______________________| |________|_________|__________|__________|_______|_____________|_________| -// A A A -// | | | -// |shared_ptr Ref Ref -// ________|__________________ ________\____________________|_____________________________________________ +// ___________________________ ___________________________________________________________________________ // | | unique_ptr | | | | | // | ShaderResourceLayoutVk |--------------->| VkResource[0] | VkResource[1] | ... | VkResource[s+m+d-1] | // |___________________________| |___________________|_________________|_______________|_____________________| @@ -99,13 +88,14 @@ #include <array> #include <memory> +#include <unordered_map> #include "PipelineState.h" #include "ShaderBase.hpp" #include "HashUtils.hpp" #include "ShaderResourceCacheVk.hpp" -#include "SPIRVShaderResources.hpp" #include "VulkanUtilities/VulkanLogicalDevice.hpp" +#include "StringPool.hpp" namespace Diligent { @@ -113,24 +103,30 @@ namespace Diligent class ShaderVkImpl; /// Diligent::ShaderResourceLayoutVk class -// sizeof(ShaderResourceLayoutVk)==56 (MS compiler, x64) +// sizeof(ShaderResourceLayoutVk)==40 (MS compiler, x64) class ShaderResourceLayoutVk { public: struct ShaderStageInfo { - ShaderStageInfo(SHADER_TYPE _Type, - const ShaderVkImpl* _pShader); + ShaderStageInfo() {} + ShaderStageInfo(SHADER_TYPE Stage, const ShaderVkImpl* pShader); - const SHADER_TYPE Type; - const ShaderVkImpl* const pShader; - std::vector<uint32_t> SPIRV; + void Append(const ShaderVkImpl* pShader); + size_t Count() const; + + SHADER_TYPE Type = SHADER_TYPE_UNKNOWN; + std::vector<const ShaderVkImpl*> Shaders; + std::vector<std::vector<uint32_t>> SPIRVs; }; using TShaderStages = std::vector<ShaderStageInfo>; 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 @@ -144,10 +140,10 @@ public: // This method is called by PipelineStateVkImpl class instance to initialize static // shader resource layout and the cache - void InitializeStaticResourceLayout(const ShaderVkImpl* pShader, - IMemoryAllocator& LayoutDataAllocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - ShaderResourceCacheVk& StaticResourceCache); + void InitializeStaticResourceLayout(const std::vector<const ShaderVkImpl*>& 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. @@ -160,7 +156,7 @@ public: bool VerifyVariables, bool VerifyImmutableSamplers); - // sizeof(VkResource) == 24 (x64) + // sizeof(VkResource) == 32 (x64) struct VkResource { // clang-format off @@ -178,40 +174,82 @@ public: static constexpr const Uint32 InvalidSamplerInd = (1 << SamplerIndBits)-1; + static constexpr const Uint32 ResourceDimBits = 7; + static constexpr const Uint32 IsMSFlagBits = 8 - ResourceDimBits; + static_assert(RESOURCE_DIM_NUM_DIMENSIONS <= (1 << ResourceDimBits), "Not enough bits to represent RESOURCE_DIMENSION"); + + using ResourceType = SPIRVShaderResourceAttribs::ResourceType; + /* 0 */ const Uint16 Binding; /* 2 */ const Uint16 DescriptorSet; + /* 4.0 */ const Uint32 CacheOffset : CacheOffsetBits; // Offset from the beginning of the cached descriptor set /* 6.5 */ const Uint32 SamplerInd : SamplerIndBits; // When using combined texture samplers, index of the separate sampler // assigned to separate image /* 7.5 */ const Uint32 VariableType : VariableTypeBits; /* 7.7 */ const Uint32 ImmutableSamplerAssigned : ImmutableSamplerFlagBits; -/* 8 */ const SPIRVShaderResourceAttribs& SpirvAttribs; -/* 16 */ const ShaderResourceLayoutVk& ParentResLayout; - - VkResource(const ShaderResourceLayoutVk& _ParentLayout, - const SPIRVShaderResourceAttribs& _SpirvAttribs, - SHADER_RESOURCE_VARIABLE_TYPE _VariableType, - uint32_t _Binding, - uint32_t _DescriptorSet, - Uint32 _CacheOffset, - Uint32 _SamplerInd, - bool _ImmutableSamplerAssigned = false)noexcept : +/* 8 */ const Uint16 ArraySize; + +/* 10 */ const ResourceType Type; +/* 11.0*/ const Uint8 ResourceDim : ResourceDimBits; +/* 11.7*/ const Uint8 IsMS : IsMSFlagBits; + +/* 16 */ const char* const Name; +/* 24 */ const ShaderResourceLayoutVk& ParentResLayout; + +#ifdef DILIGENT_DEVELOPMENT +/* 32 */ const Uint32 BufferStaticSize; +/* 36 */ const Uint32 BufferStride; +#endif + // clang-format on + + VkResource(const ShaderResourceLayoutVk& _ParentLayout, + const char* _Name, + Uint16 _ArraySize, + ResourceType _Type, + RESOURCE_DIMENSION _ResourceDim, + bool _IsMS, + SHADER_RESOURCE_VARIABLE_TYPE _VariableType, + uint32_t _Binding, + uint32_t _DescriptorSet, + Uint32 _CacheOffset, + Uint32 _SamplerInd, + bool _ImmutableSamplerAssigned, + Uint32 _BufferStaticSize, + Uint32 _BufferStride) noexcept : + // clang-format off Binding {static_cast<decltype(Binding)>(_Binding) }, DescriptorSet {static_cast<decltype(DescriptorSet)>(_DescriptorSet)}, CacheOffset {_CacheOffset }, SamplerInd {_SamplerInd }, VariableType {_VariableType }, ImmutableSamplerAssigned {_ImmutableSamplerAssigned ? 1U : 0U}, - SpirvAttribs {_SpirvAttribs }, - ParentResLayout {_ParentLayout } + ArraySize {_ArraySize }, + Type {_Type }, + ResourceDim {_ResourceDim }, + IsMS {_IsMS ? Uint8{1} : Uint8{0}}, +#ifdef DILIGENT_DEVELOPMENT + BufferStaticSize {_BufferStaticSize}, + BufferStride {_BufferStride }, +#endif + Name {_Name }, + ParentResLayout {_ParentLayout } + // clang-format on { +#if defined(_MSC_VER) && defined(_WIN64) && !defined(DILIGENT_DEBUG) + 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<decltype(Binding)>::max(), "Binding (", _Binding, ") exceeds max representable value ", std::numeric_limits<decltype(Binding)>::max() ); VERIFY(_DescriptorSet <= std::numeric_limits<decltype(DescriptorSet)>::max(), "Descriptor set (", _DescriptorSet, ") exceeds max representable value ", std::numeric_limits<decltype(DescriptorSet)>::max()); + VERIFY(_VariableType < (1 << VariableTypeBits), "Variable type (", Uint32{_VariableType}, ") exceeds max representable value ", (1 << VariableTypeBits) ); + VERIFY(_ResourceDim < (1 << ResourceDimBits), "Resource dimension (", Uint32{_ResourceDim}, ") exceeds max representable value ", (1 << ResourceDimBits) ); + // clang-format on } - // clang-format on + // Checks if a resource is bound in ResourceCache at the given ArrayIndex bool IsBound(Uint32 ArrayIndex, const ShaderResourceCacheVk& ResourceCache) const; @@ -220,17 +258,18 @@ public: 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; + 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 || - SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || - SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler, + Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || + Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler, "Immutable sampler can only be assigned to a sampled image or separate sampler"); return ImmutableSamplerAssigned != 0; } @@ -240,6 +279,44 @@ public: return static_cast<SHADER_RESOURCE_VARIABLE_TYPE>(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<RESOURCE_DIMENSION>(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, @@ -276,6 +353,11 @@ public: VkDescriptorSet vkDescrSet, Uint32 ArrayInd) const; + void CacheAccelerationStructure(IDeviceObject* pTLAS, + ShaderResourceCacheVk::Resource& DstRes, + VkDescriptorSet vkDescrSet, + Uint32 ArrayInd) const; + template <typename ObjectType, typename TPreUpdateObject> bool UpdateCachedResource(ShaderResourceCacheVk::Resource& DstRes, RefCntAutoPtr<ObjectType>&& pObject, @@ -310,15 +392,10 @@ public: const Char* GetShaderName() const { - return m_pResources->GetShaderName(); - } - - SHADER_TYPE GetShaderType() const - { - return m_pResources->GetShaderType(); + return GetStringPoolData(); } - const SPIRVShaderResources& GetResources() const { return *m_pResources; } + SHADER_TYPE GetShaderType() const { return m_ShaderType; } const VkResource& GetResource(SHADER_RESOURCE_VARIABLE_TYPE VarType, Uint32 r) const { @@ -327,7 +404,9 @@ public: return Resources[GetResourceOffset(VarType, r)]; } - bool IsUsingSeparateSamplers() const { return !m_pResources->IsUsingCombinedSamplers(); } + bool IsUsingSeparateSamplers() const { return m_IsUsingSeparateSamplers; } + + bool IsCompatibleWith(const ShaderResourceLayoutVk& ResLayout) const; private: Uint32 GetResourceOffset(SHADER_RESOURCE_VARIABLE_TYPE VarType, Uint32 r) const @@ -349,7 +428,7 @@ private: const VkResource& GetResource(Uint32 r) const { VERIFY_EXPR(r < GetTotalResourceCount()); - auto* Resources = reinterpret_cast<const VkResource*>(m_ResourceBuffer.get()); + const auto* Resources = reinterpret_cast<const VkResource*>(m_ResourceBuffer.get()); return Resources[r]; } @@ -358,12 +437,24 @@ private: return m_NumResources[SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES]; } - void AllocateMemory(const ShaderVkImpl* pShader, - IMemoryAllocator& Allocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - bool AllocateImmutableSamplers); + const char* GetStringPoolData() const + { + const auto* ResourceDataEnd = reinterpret_cast<const VkResource*>(m_ResourceBuffer.get()) + GetTotalResourceCount(); + const auto* SamplerDataEnd = reinterpret_cast<const ImmutableSamplerPtrType*>(ResourceDataEnd) + m_NumImmutableSamplers; + return reinterpret_cast<const char*>(SamplerDataEnd); + } + + static constexpr const Uint32 InvalidResourceIndex = ~0u; + + // Maps resource name to its index in m_ResourceBuffer + using ResourceNameToIndex_t = std::unordered_map<HashMapStringKey, Uint32, HashMapStringKey::Hasher>; + StringPool AllocateMemory(const std::vector<const ShaderVkImpl*>& Shaders, + IMemoryAllocator& Allocator, + const PipelineResourceLayoutDesc& ResourceLayoutDesc, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + ResourceNameToIndex_t& UniqueNames, + bool AllocateImmutableSamplers); using ImmutableSamplerPtrType = RefCntAutoPtr<ISampler>; ImmutableSamplerPtrType& GetImmutableSampler(Uint32 n) noexcept @@ -374,16 +465,16 @@ private: } // clang-format off -/* 0 */ const VulkanUtilities::VulkanLogicalDevice& m_LogicalDevice; -/* 8 */ std::unique_ptr<void, STDDeleterRawMem<void> > m_ResourceBuffer; +/* 0 */ const VulkanUtilities::VulkanLogicalDevice& m_LogicalDevice; +/* 8 */ std::unique_ptr<void, STDDeleterRawMem<void> > m_ResourceBuffer; + +/*24 */ std::array<Uint16, SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES+1> m_NumResources = {}; - // We must use shared_ptr to reference ShaderResources instance, because - // there may be multiple objects referencing the same set of resources -/*24 */ std::shared_ptr<const SPIRVShaderResources> m_pResources; +/*32 */ Uint16 m_NumImmutableSamplers = 0; +/*34 */ bool m_IsUsingSeparateSamplers = false; +/*36 */ SHADER_TYPE m_ShaderType = SHADER_TYPE_UNKNOWN; -/*40 */ std::array<Uint16, SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES+1> m_NumResources = {}; -/*48 */ Uint32 m_NumImmutableSamplers = 0; -/*56*/ // End of class +/*40 */ // End of class // clang-format on }; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp index 27689c1e..6c88bf56 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVariableVk.hpp @@ -183,14 +183,14 @@ public: Uint32 FirstElement, Uint32 NumElements) override final { - VerifyAndCorrectSetArrayArguments(m_Resource.SpirvAttribs.Name, m_Resource.SpirvAttribs.ArraySize, FirstElement, NumElements); + 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); } virtual void DILIGENT_CALL_TYPE GetResourceDesc(ShaderResourceDesc& ResourceDesc) const override final { - ResourceDesc = m_Resource.SpirvAttribs.GetResourceDesc(); + ResourceDesc = m_Resource.GetResourceDesc(); } virtual Uint32 DILIGENT_CALL_TYPE GetIndex() const override final diff --git a/Graphics/GraphicsEngineVulkan/include/TopLevelASVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/TopLevelASVkImpl.hpp new file mode 100644 index 00000000..6eda1339 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/TopLevelASVkImpl.hpp @@ -0,0 +1,83 @@ +/* + * Copyright 2019-2020 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 +/// Definition of the Diligent::TopLevelASVkImpl class + +#include "RenderDeviceVk.h" +#include "RenderDeviceVkImpl.hpp" +#include "TopLevelASVk.h" +#include "TopLevelASBase.hpp" +#include "BottomLevelASVkImpl.hpp" +#include "VulkanUtilities/VulkanObjectWrappers.hpp" + +namespace Diligent +{ + +class TopLevelASVkImpl final : public TopLevelASBase<ITopLevelASVk, BottomLevelASVkImpl, RenderDeviceVkImpl> +{ +public: + using TTopLevelASBase = TopLevelASBase<ITopLevelASVk, BottomLevelASVkImpl, RenderDeviceVkImpl>; + + TopLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const TopLevelASDesc& Desc); + TopLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const TopLevelASDesc& Desc, + RESOURCE_STATE InitialState, + VkAccelerationStructureKHR vkTLAS); + ~TopLevelASVkImpl(); + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_TopLevelASVk, TTopLevelASBase); + + /// Implementation of ITopLevelAS::GetNativeHandle() in Vulkan backend. + virtual void* DILIGENT_CALL_TYPE GetNativeHandle() override final + { + auto Handle = GetVkTLAS(); + return reinterpret_cast<void*>(Handle); + } + + /// Implementation of ITopLevelASVk::GetVkTLAS(). + virtual VkAccelerationStructureKHR DILIGENT_CALL_TYPE GetVkTLAS() const override { return m_VulkanTLAS; } + + /// Implementation of ITopLevelASVk::GetVkDeviceAddress(). + virtual VkDeviceAddress DILIGENT_CALL_TYPE GetVkDeviceAddress() const override { return m_DeviceAddress; } + + const VkAccelerationStructureKHR* GetVkTLASPtr() const { return &m_VulkanTLAS; } + +private: + VkDeviceAddress m_DeviceAddress = 0; + VulkanUtilities::AccelStructWrapper m_VulkanTLAS; + VulkanUtilities::BufferWrapper m_VulkanBuffer; + VulkanUtilities::VulkanMemoryAllocation m_MemoryAllocation; + VkDeviceSize m_MemoryAlignedOffset = 0; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp index 1e6749f1..405e9bfa 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp @@ -40,7 +40,8 @@ namespace Diligent VkFormat TexFormatToVkFormat(TEXTURE_FORMAT TexFmt); TEXTURE_FORMAT VkFormatToTexFormat(VkFormat VkFmt); -VkFormat TypeToVkFormat(VALUE_TYPE ValType, Uint32 NumComponents, Bool bIsNormalized); +VkFormat TypeToVkFormat(VALUE_TYPE ValType, Uint32 NumComponents, Bool bIsNormalized); +VkIndexType TypeToVkIndexType(VALUE_TYPE IndexType); VkPipelineRasterizationStateCreateInfo RasterizerStateDesc_To_VkRasterizationStateCI(const struct RasterizerStateDesc& RasterizerDesc); VkPipelineDepthStencilStateCreateInfo DepthStencilStateDesc_To_VkDepthStencilStateCI(const struct DepthStencilStateDesc& DepthStencilDesc); @@ -64,8 +65,10 @@ VkSamplerMipmapMode FilterTypeToVkMipmapMode(FILTER_TYPE FilterType); VkSamplerAddressMode AddressModeToVkAddressMode(TEXTURE_ADDRESS_MODE AddressMode); VkBorderColor BorderColorToVkBorderColor(const Float32 BorderColor[]); -VkAccessFlags ResourceStateFlagsToVkAccessFlags(RESOURCE_STATE StateFlags); -VkImageLayout ResourceStateToVkImageLayout(RESOURCE_STATE StateFlag, bool IsInsideRenderPass = false); +VkPipelineStageFlags ResourceStateFlagsToVkPipelineStageFlags(RESOURCE_STATE StateFlags, VkPipelineStageFlags ShaderStages); +VkAccessFlags ResourceStateFlagsToVkAccessFlags(RESOURCE_STATE StateFlags); +VkAccessFlags AccelStructStateFlagsToVkAccessFlags(RESOURCE_STATE StateFlags); +VkImageLayout ResourceStateToVkImageLayout(RESOURCE_STATE StateFlag, bool IsInsideRenderPass = false); RESOURCE_STATE VkAccessFlagsToResourceStates(VkAccessFlags AccessFlags); RESOURCE_STATE VkImageLayoutToResourceState(VkImageLayout Layout); @@ -85,4 +88,9 @@ VkAccessFlags AccessFlagsToVkAccessFlags(ACCESS_FLAGS AccessFlags); VkShaderStageFlagBits ShaderTypeToVkShaderStageFlagBit(SHADER_TYPE ShaderType); +VkBuildAccelerationStructureFlagsKHR BuildASFlagsToVkBuildAccelerationStructureFlags(RAYTRACING_BUILD_AS_FLAGS Flags); +VkGeometryFlagsKHR GeometryFlagsToVkGeometryFlags(RAYTRACING_GEOMETRY_FLAGS Flags); +VkGeometryInstanceFlagsKHR InstanceFlagsToVkGeometryInstanceFlags(RAYTRACING_INSTANCE_FLAGS Flags); +VkCopyAccelerationStructureModeKHR CopyASModeToVkCopyAccelerationStructureMode(COPY_AS_MODE Mode); + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.hpp index 7d66600d..c0d79648 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.hpp @@ -36,8 +36,8 @@ namespace VulkanUtilities class VulkanCommandBuffer { public: - VulkanCommandBuffer(VkPipelineStageFlags EnabledGraphicsShaderStages) noexcept : - m_EnabledGraphicsShaderStages{EnabledGraphicsShaderStages} + VulkanCommandBuffer(VkPipelineStageFlags EnabledShaderStages) noexcept : + m_EnabledShaderStages{EnabledShaderStages} {} // clang-format off @@ -140,27 +140,27 @@ public: __forceinline void DrawMesh(uint32_t TaskCount, uint32_t FirstTask) { -#ifdef VK_NV_mesh_shader +#if DILIGENT_USE_VOLK VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); VERIFY(m_State.RenderPass != VK_NULL_HANDLE, "vkCmdDrawMeshTasksNV() must be called inside render pass"); VERIFY(m_State.GraphicsPipeline != VK_NULL_HANDLE, "No graphics pipeline bound"); vkCmdDrawMeshTasksNV(m_VkCmdBuffer, TaskCount, FirstTask); #else - UNSUPPORTED("DrawMesh is not supported in current Vulkan headers"); + UNSUPPORTED("DrawMesh is not supported when vulkan library is linked statically"); #endif } __forceinline void DrawMeshIndirect(VkBuffer Buffer, VkDeviceSize Offset, uint32_t DrawCount, uint32_t Stride) { -#ifdef VK_NV_mesh_shader +#if DILIGENT_USE_VOLK VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); VERIFY(m_State.RenderPass != VK_NULL_HANDLE, "vkCmdDrawMeshTasksNV() must be called inside render pass"); VERIFY(m_State.GraphicsPipeline != VK_NULL_HANDLE, "No graphics pipeline bound"); vkCmdDrawMeshTasksIndirectNV(m_VkCmdBuffer, Buffer, Offset, DrawCount, Stride); #else - UNSUPPORTED("DrawMeshIndirect is not supported in current Vulkan headers"); + UNSUPPORTED("DrawMeshIndirect is not supported when vulkan library is linked statically"); #endif } @@ -280,6 +280,17 @@ public: } } + __forceinline void BindRayTracingPipeline(VkPipeline RayTracingPipeline) + { + // 9.8 + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + if (m_State.RayTracingPipeline != RayTracingPipeline) + { + vkCmdBindPipeline(m_VkCmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, RayTracingPipeline); + m_State.RayTracingPipeline = RayTracingPipeline; + } + } + __forceinline void SetViewports(uint32_t FirstViewport, uint32_t ViewportCount, const VkViewport* pViewports) { VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); @@ -331,7 +342,7 @@ public: VkImageLayout OldLayout, VkImageLayout NewLayout, const VkImageSubresourceRange& SubresRange, - VkPipelineStageFlags EnabledGraphicsShaderStages, + VkPipelineStageFlags EnabledShaderStages, VkPipelineStageFlags SrcStages = 0, VkPipelineStageFlags DestStages = 0); @@ -349,7 +360,7 @@ public: // dependencies between attachments EndRenderPass(); } - TransitionImageLayout(m_VkCmdBuffer, Image, OldLayout, NewLayout, SubresRange, m_EnabledGraphicsShaderStages, SrcStages, DestStages); + TransitionImageLayout(m_VkCmdBuffer, Image, OldLayout, NewLayout, SubresRange, m_EnabledShaderStages, SrcStages, DestStages); } @@ -357,7 +368,7 @@ public: VkBuffer Buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, - VkPipelineStageFlags EnabledGraphicsShaderStages, + VkPipelineStageFlags EnabledShaderStages, VkPipelineStageFlags SrcStages = 0, VkPipelineStageFlags DestStages = 0); @@ -374,7 +385,31 @@ public: // dependencies between attachments EndRenderPass(); } - BufferMemoryBarrier(m_VkCmdBuffer, Buffer, srcAccessMask, dstAccessMask, m_EnabledGraphicsShaderStages, SrcStages, DestStages); + BufferMemoryBarrier(m_VkCmdBuffer, Buffer, srcAccessMask, dstAccessMask, m_EnabledShaderStages, SrcStages, DestStages); + } + + + // for Acceleration structures + static void ASMemoryBarrier(VkCommandBuffer CmdBuffer, + VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, + VkPipelineStageFlags EnabledShaderStages, + VkPipelineStageFlags SrcStages = 0, + VkPipelineStageFlags DestStages = 0); + + __forceinline void ASMemoryBarrier(VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, + VkPipelineStageFlags SrcStages = 0, + VkPipelineStageFlags DestStages = 0) + { + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + if (m_State.RenderPass != VK_NULL_HANDLE) + { + // Image layout transitions within a render pass execute + // dependencies between attachments + EndRenderPass(); + } + ASMemoryBarrier(m_VkCmdBuffer, srcAccessMask, dstAccessMask, m_EnabledShaderStages, SrcStages, DestStages); } __forceinline void BindDescriptorSets(VkPipelineBindPoint pipelineBindPoint, @@ -570,6 +605,71 @@ public: dstBuffer, dstOffset, stride, flags); } + __forceinline void BuildAccelerationStructure(uint32_t infoCount, + const VkAccelerationStructureBuildGeometryInfoKHR* pInfos, + const VkAccelerationStructureBuildRangeInfoKHR* const* ppBuildRangeInfos) + { +#if DILIGENT_USE_VOLK + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + if (m_State.RenderPass != VK_NULL_HANDLE) + { + // Build AS operations must be performed outside of render pass. + EndRenderPass(); + } + vkCmdBuildAccelerationStructuresKHR(m_VkCmdBuffer, infoCount, pInfos, ppBuildRangeInfos); +#else + UNSUPPORTED("Ray tracing is not supported when vulkan library is linked statically"); +#endif + } + + __forceinline void CopyAccelerationStructure(const VkCopyAccelerationStructureInfoKHR& Info) + { +#if DILIGENT_USE_VOLK + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + if (m_State.RenderPass != VK_NULL_HANDLE) + { + // Copy AS operations must be performed outside of render pass. + EndRenderPass(); + } + vkCmdCopyAccelerationStructureKHR(m_VkCmdBuffer, &Info); +#else + UNSUPPORTED("Ray tracing is not supported when vulkan library is linked statically"); +#endif + } + + __forceinline void WriteAccelerationStructuresProperties(VkAccelerationStructureKHR accelerationStructure, VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery) + { +#if DILIGENT_USE_VOLK + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + if (m_State.RenderPass != VK_NULL_HANDLE) + { + // Write AS properties operations must be performed outside of render pass. + EndRenderPass(); + } + vkCmdWriteAccelerationStructuresPropertiesKHR(m_VkCmdBuffer, 1, &accelerationStructure, queryType, queryPool, firstQuery); +#else + UNSUPPORTED("Ray tracing is not supported when vulkan library is linked statically"); +#endif + } + + __forceinline void TraceRays(const VkStridedDeviceAddressRegionKHR& RaygenShaderBindingTable, + const VkStridedDeviceAddressRegionKHR& MissShaderBindingTable, + const VkStridedDeviceAddressRegionKHR& HitShaderBindingTable, + const VkStridedDeviceAddressRegionKHR& CallableShaderBindingTable, + uint32_t width, + uint32_t height, + uint32_t depth) + { +#if DILIGENT_USE_VOLK + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + VERIFY(m_State.RayTracingPipeline != VK_NULL_HANDLE, "No ray tracing pipeline bound"); + + vkCmdTraceRaysKHR(m_VkCmdBuffer, &RaygenShaderBindingTable, &MissShaderBindingTable, &HitShaderBindingTable, &CallableShaderBindingTable, width, height, depth); +#else + UNSUPPORTED("Ray tracing is not supported when vulkan library is linked statically"); +#endif + } + void FlushBarriers(); __forceinline void SetVkCmdBuffer(VkCommandBuffer VkCmdBuffer) @@ -578,12 +678,15 @@ public: } VkCommandBuffer GetVkCmdBuffer() const { return m_VkCmdBuffer; } + VkPipelineStageFlags GetEnabledShaderStages() const { return m_EnabledShaderStages; } + struct StateCache { VkRenderPass RenderPass = VK_NULL_HANDLE; VkFramebuffer Framebuffer = VK_NULL_HANDLE; VkPipeline GraphicsPipeline = VK_NULL_HANDLE; VkPipeline ComputePipeline = VK_NULL_HANDLE; + VkPipeline RayTracingPipeline = VK_NULL_HANDLE; VkBuffer IndexBuffer = VK_NULL_HANDLE; VkDeviceSize IndexBufferOffset = 0; VkIndexType IndexType = VK_INDEX_TYPE_MAX_ENUM; @@ -598,7 +701,7 @@ public: private: StateCache m_State; VkCommandBuffer m_VkCmdBuffer = VK_NULL_HANDLE; - const VkPipelineStageFlags m_EnabledGraphicsShaderStages; + const VkPipelineStageFlags m_EnabledShaderStages; }; } // namespace VulkanUtilities diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanHeaders.h b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanHeaders.h index ee6461fd..bb95a9e4 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanHeaders.h +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanHeaders.h @@ -30,15 +30,13 @@ #if DILIGENT_USE_VOLK # define VK_NO_PROTOTYPES #endif + #include "vulkan/vulkan.h" + #define VK_FORMAT_RANGE_SIZE (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1) #if DILIGENT_USE_VOLK # include "volk/volk.h" -#else -// Don't use extensions when statically linked with Vulkan -# undef VK_KHR_get_physical_device_properties2 -# undef VK_NV_mesh_shader #endif #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(_X11_XLIB_H_) diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanInstance.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanInstance.hpp index f5b921a6..e2bb7ad8 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanInstance.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanInstance.hpp @@ -44,7 +44,8 @@ public: VulkanInstance& operator = ( VulkanInstance&&) = delete; // clang-format on - static std::shared_ptr<VulkanInstance> Create(bool EnableValidation, + static std::shared_ptr<VulkanInstance> Create(uint32_t ApiVersion, + bool EnableValidation, uint32_t GlobalExtensionCount, const char* const* ppGlobalExtensionNames, VkAllocationCallbacks* pVkAllocator); @@ -61,7 +62,7 @@ public: } // clang-format off - bool IsLayerAvailable (const char* LayerName) const; + bool IsLayerAvailable (const char* LayerName, uint32_t& Version) const; bool IsExtensionAvailable(const char* ExtensionName)const; bool IsExtensionEnabled (const char* ExtensionName)const; @@ -69,10 +70,12 @@ public: VkAllocationCallbacks* GetVkAllocator()const{return m_pVkAllocator;} VkInstance GetVkInstance() const{return m_VkInstance; } + uint32_t GetVkVersion() const{return m_VkVersion; } // clang-format on private: - VulkanInstance(bool EnableValidation, + VulkanInstance(uint32_t ApiVersion, + bool EnableValidation, uint32_t GlobalExtensionCount, const char* const* ppGlobalExtensionNames, VkAllocationCallbacks* pVkAllocator); @@ -80,6 +83,7 @@ private: bool m_DebugUtilsEnabled = false; VkAllocationCallbacks* const m_pVkAllocator; VkInstance m_VkInstance = VK_NULL_HANDLE; + uint32_t m_VkVersion = VK_API_VERSION_1_0; std::vector<VkLayerProperties> m_Layers; std::vector<VkExtensionProperties> m_Extensions; diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.hpp index 3f06cb0e..6d2d827c 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.hpp @@ -28,7 +28,7 @@ #pragma once #include <memory> -#include "VulkanHeaders.h" +#include "VulkanPhysicalDevice.hpp" namespace VulkanUtilities { @@ -57,7 +57,8 @@ enum class VulkanHandleTypeId : uint32_t Semaphore, Queue, Event, - QueryPool + QueryPool, + AccelerationStructureKHR }; template <typename VulkanObjectType, VulkanHandleTypeId> @@ -81,13 +82,17 @@ using DescriptorPoolWrapper = DEFINE_VULKAN_OBJECT_WRAPPER(DescriptorPool); using DescriptorSetLayoutWrapper = DEFINE_VULKAN_OBJECT_WRAPPER(DescriptorSetLayout); using SemaphoreWrapper = DEFINE_VULKAN_OBJECT_WRAPPER(Semaphore); using QueryPoolWrapper = DEFINE_VULKAN_OBJECT_WRAPPER(QueryPool); +using AccelStructWrapper = DEFINE_VULKAN_OBJECT_WRAPPER(AccelerationStructureKHR); #undef DEFINE_VULKAN_OBJECT_WRAPPER class VulkanLogicalDevice : public std::enable_shared_from_this<VulkanLogicalDevice> { public: - static std::shared_ptr<VulkanLogicalDevice> Create(VkPhysicalDevice vkPhysicalDevice, + using ExtensionFeatures = VulkanPhysicalDevice::ExtensionFeatures; + + static std::shared_ptr<VulkanLogicalDevice> Create(const VulkanPhysicalDevice& PhysicalDevice, const VkDeviceCreateInfo& DeviceCI, + const ExtensionFeatures& EnabledExtFeatures, const VkAllocationCallbacks* vkAllocator); // clang-format off @@ -129,8 +134,9 @@ public: RenderPassWrapper CreateRenderPass (const VkRenderPassCreateInfo& RenderPassCI,const char* DebugName = "") const; DeviceMemoryWrapper AllocateDeviceMemory(const VkMemoryAllocateInfo & AllocInfo, const char* DebugName = "") const; - PipelineWrapper CreateComputePipeline (const VkComputePipelineCreateInfo& PipelineCI, VkPipelineCache cache, const char* DebugName = "") const; - PipelineWrapper CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& PipelineCI, VkPipelineCache cache, const char* DebugName = "") const; + PipelineWrapper CreateComputePipeline (const VkComputePipelineCreateInfo& PipelineCI, VkPipelineCache cache, const char* DebugName = "") const; + PipelineWrapper CreateGraphicsPipeline (const VkGraphicsPipelineCreateInfo& PipelineCI, VkPipelineCache cache, const char* DebugName = "") const; + PipelineWrapper CreateRayTracingPipeline(const VkRayTracingPipelineCreateInfoKHR& PipelineCI, VkPipelineCache cache, const char* DebugName = "") const; ShaderModuleWrapper CreateShaderModule (const VkShaderModuleCreateInfo& ShaderModuleCI, const char* DebugName = "") const; PipelineLayoutWrapper CreatePipelineLayout (const VkPipelineLayoutCreateInfo& LayoutCI, const char* DebugName = "") const; @@ -140,6 +146,7 @@ public: SemaphoreWrapper CreateSemaphore(const VkSemaphoreCreateInfo& SemaphoreCI, const char* DebugName = "") const; QueryPoolWrapper CreateQueryPool(const VkQueryPoolCreateInfo& QueryPoolCI, const char* DebugName = "") const; + AccelStructWrapper CreateAccelStruct(const VkAccelerationStructureCreateInfoKHR& CI, const char* DebugName = "") const; VkCommandBuffer AllocateVkCommandBuffer(const VkCommandBufferAllocateInfo& AllocInfo, const char* DebugName = "") const; VkDescriptorSet AllocateVkDescriptorSet(const VkDescriptorSetAllocateInfo& AllocInfo, const char* DebugName = "") const; @@ -161,11 +168,13 @@ public: void ReleaseVulkanObject(DescriptorSetLayoutWrapper&& DescriptorSetLayout) const; void ReleaseVulkanObject(SemaphoreWrapper&& Semaphore) const; void ReleaseVulkanObject(QueryPoolWrapper&& QueryPool) const; + void ReleaseVulkanObject(AccelStructWrapper&& AccelStruct) const; void FreeDescriptorSet(VkDescriptorPool Pool, VkDescriptorSet Set) const; VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer vkBuffer) const; - VkMemoryRequirements GetImageMemoryRequirements (VkImage vkImage ) const; + VkMemoryRequirements GetImageMemoryRequirements (VkImage vkImage ) const; + VkDeviceAddress GetAccelerationStructureDeviceAddress(VkAccelerationStructureKHR AS) const; VkResult BindBufferMemory(VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) const; VkResult BindImageMemory (VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) const; @@ -207,13 +216,19 @@ public: dataSize, pData, stride, flags); } - VkPipelineStageFlags GetEnabledGraphicsShaderStages() const { return m_EnabledGraphicsShaderStages; } + void GetAccelerationStructureBuildSizes(const VkAccelerationStructureBuildGeometryInfoKHR& BuildInfo, const uint32_t* pMaxPrimitiveCounts, VkAccelerationStructureBuildSizesInfoKHR& SizeInfo) const; + + VkResult GetRayTracingShaderGroupHandles(VkPipeline pipeline, uint32_t firstGroup, uint32_t groupCount, size_t dataSize, void* pData) const; + + VkPipelineStageFlags GetEnabledShaderStages() const { return m_EnabledShaderStages; } const VkPhysicalDeviceFeatures& GetEnabledFeatures() const { return m_EnabledFeatures; } + const ExtensionFeatures& GetEnabledExtFeatures() const { return m_EnabledExtFeatures; } private: - VulkanLogicalDevice(VkPhysicalDevice vkPhysicalDevice, + VulkanLogicalDevice(const VulkanPhysicalDevice& PhysicalDevice, const VkDeviceCreateInfo& DeviceCI, + const ExtensionFeatures& EnabledExtFeatures, const VkAllocationCallbacks* vkAllocator); template <typename VkObjectType, @@ -227,8 +242,11 @@ private: VkDevice m_VkDevice = VK_NULL_HANDLE; const VkAllocationCallbacks* const m_VkAllocator; - VkPipelineStageFlags m_EnabledGraphicsShaderStages = 0; + VkPipelineStageFlags m_EnabledShaderStages = 0; const VkPhysicalDeviceFeatures m_EnabledFeatures; + ExtensionFeatures m_EnabledExtFeatures = {}; }; +void EnableRayTracingKHRviaNV(); + } // namespace VulkanUtilities diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.hpp index 4ada51be..4d9eaa7f 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanMemoryManager.hpp @@ -95,10 +95,11 @@ struct VulkanMemoryAllocation class VulkanMemoryPage { public: - VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, - VkDeviceSize PageSize, - uint32_t MemoryTypeIndex, - bool IsHostVisible) noexcept; + VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, + VkDeviceSize PageSize, + uint32_t MemoryTypeIndex, + bool IsHostVisible, + VkMemoryAllocateFlags AllocateFlags) noexcept; ~VulkanMemoryPage(); // clang-format off @@ -198,8 +199,8 @@ public: VulkanMemoryManager& operator= (VulkanMemoryManager&&) = delete; // clang-format on - VulkanMemoryAllocation Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible); - VulkanMemoryAllocation Allocate(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProps); + VulkanMemoryAllocation Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible, VkMemoryAllocateFlags AllocateFlags); + VulkanMemoryAllocation Allocate(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProps, VkMemoryAllocateFlags AllocateFlags); void ShrinkMemory(); protected: @@ -218,19 +219,23 @@ protected: std::mutex m_PagesMtx; struct MemoryPageIndex { - const uint32_t MemoryTypeIndex; - const bool IsHostVisible; + const uint32_t MemoryTypeIndex; + const VkMemoryAllocateFlags AllocateFlags; + const bool IsHostVisible; // clang-format off - MemoryPageIndex(uint32_t _MemoryTypeIndex, - bool _IsHostVisible) : - MemoryTypeIndex(_MemoryTypeIndex), - IsHostVisible (_IsHostVisible) + MemoryPageIndex(uint32_t _MemoryTypeIndex, + bool _IsHostVisible, + VkMemoryAllocateFlags _AllocateFlags) : + MemoryTypeIndex{_MemoryTypeIndex}, + AllocateFlags {_AllocateFlags}, + IsHostVisible {_IsHostVisible} {} bool operator == (const MemoryPageIndex& rhs)const { return MemoryTypeIndex == rhs.MemoryTypeIndex && + AllocateFlags == rhs.AllocateFlags && IsHostVisible == rhs.IsHostVisible; } // clang-format on @@ -239,7 +244,7 @@ protected: { size_t operator()(const MemoryPageIndex& PageIndex) const { - return Diligent::ComputeHash(PageIndex.MemoryTypeIndex, PageIndex.IsHostVisible); + return Diligent::ComputeHash(PageIndex.MemoryTypeIndex, PageIndex.AllocateFlags, PageIndex.IsHostVisible); } }; }; diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanObjectWrappers.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanObjectWrappers.hpp index 7e2f8385..5e0390f5 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanObjectWrappers.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanObjectWrappers.hpp @@ -87,6 +87,11 @@ public: return m_VkObject; } + const VulkanObjectType* operator&() const + { + return &m_VkObject; + } + void Release() { // For externally managed objects, m_pLogicalDevice is null diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp index b4e734c7..404e3eee 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp @@ -39,10 +39,24 @@ class VulkanPhysicalDevice public: struct ExtensionFeatures { - VkPhysicalDeviceMeshShaderFeaturesNV MeshShader = {}; - VkPhysicalDevice16BitStorageFeaturesKHR Storage16Bit = {}; - VkPhysicalDevice8BitStorageFeaturesKHR Storage8Bit = {}; - VkPhysicalDeviceShaderFloat16Int8FeaturesKHR ShaderFloat16Int8 = {}; + VkPhysicalDeviceMeshShaderFeaturesNV MeshShader = {}; + VkPhysicalDevice16BitStorageFeaturesKHR Storage16Bit = {}; + VkPhysicalDevice8BitStorageFeaturesKHR Storage8Bit = {}; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR ShaderFloat16Int8 = {}; + VkPhysicalDeviceAccelerationStructureFeaturesKHR AccelStruct = {}; + VkPhysicalDeviceRayTracingPipelineFeaturesKHR RayTracingPipeline = {}; + bool Spirv14 = false; // Ray tracing requires Vulkan 1.2 or SPIRV 1.4 extension + bool Spirv15 = false; // DXC shaders with ray tracing requires Vulkan 1.2 with SPIRV 1.5 + VkPhysicalDeviceBufferDeviceAddressFeaturesKHR BufferDeviceAddress = {}; + VkPhysicalDeviceDescriptorIndexingFeaturesEXT DescriptorIndexing = {}; + }; + + struct ExtensionProperties + { + VkPhysicalDeviceMeshShaderPropertiesNV MeshShader = {}; + VkPhysicalDeviceAccelerationStructurePropertiesKHR AccelStruct = {}; + VkPhysicalDeviceRayTracingPipelinePropertiesKHR RayTracingPipeline = {}; + VkPhysicalDeviceDescriptorIndexingPropertiesEXT DescriptorIndexing = {}; }; public: @@ -70,6 +84,7 @@ public: const VkPhysicalDeviceProperties& GetProperties() const { return m_Properties; } const VkPhysicalDeviceFeatures& GetFeatures() const { return m_Features; } const ExtensionFeatures& GetExtFeatures() const { return m_ExtFeatures; } + const ExtensionProperties& GetExtProperties() const { return m_ExtProperties; } const VkPhysicalDeviceMemoryProperties& GetMemoryProperties() const { return m_MemoryProperties; } VkFormatProperties GetPhysicalDeviceFormatProperties(VkFormat imageFormat) const; @@ -82,6 +97,7 @@ private: VkPhysicalDeviceFeatures m_Features = {}; VkPhysicalDeviceMemoryProperties m_MemoryProperties = {}; ExtensionFeatures m_ExtFeatures = {}; + ExtensionProperties m_ExtProperties = {}; std::vector<VkQueueFamilyProperties> m_QueueFamilyProperties; std::vector<VkExtensionProperties> m_SupportedExtensions; }; diff --git a/Graphics/GraphicsEngineVulkan/interface/BottomLevelASVk.h b/Graphics/GraphicsEngineVulkan/interface/BottomLevelASVk.h new file mode 100644 index 00000000..ea785a51 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/interface/BottomLevelASVk.h @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2020 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 +/// Definition of the Diligent::IBottomLevelASVk interface + +#include "../../GraphicsEngine/interface/BottomLevelAS.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + + +// {7212AFC9-02E2-4D7F-81A8-1CE5353CEA2D} +static const INTERFACE_ID IID_BottomLevelASVk = + {0x7212afc9, 0x2e2, 0x4d7f, {0x81, 0xa8, 0x1c, 0xe5, 0x35, 0x3c, 0xea, 0x2d}}; + +#define DILIGENT_INTERFACE_NAME IBottomLevelASVk +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IBottomLevelASVkInclusiveMethods \ + IBottomLevelASInclusiveMethods; \ + IBottomLevelASVkMethods BottomLevelASVk + +/// Exposes Vulkan-specific functionality of a Bottom-level acceleration structure object. +DILIGENT_BEGIN_INTERFACE(IBottomLevelASVk, IBottomLevelAS) +{ + /// Returns a Vulkan BLAS object handle. + VIRTUAL VkAccelerationStructureKHR METHOD(GetVkBLAS)(THIS) CONST PURE; + + /// Returns a Vulkan BLAS device address. + VIRTUAL VkDeviceAddress METHOD(GetVkDeviceAddress)(THIS) CONST PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +# define IBottomLevelASVk_GetVkBLAS(This) CALL_IFACE_METHOD(BottomLevelASVk, GetVkBLAS, This) +# define IBottomLevelASVk_GetVkDeviceAddress(This) CALL_IFACE_METHOD(BottomLevelASVk, GetVkDeviceAddress, This) + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/interface/BufferVk.h b/Graphics/GraphicsEngineVulkan/interface/BufferVk.h index ea889795..af1f8673 100644 --- a/Graphics/GraphicsEngineVulkan/interface/BufferVk.h +++ b/Graphics/GraphicsEngineVulkan/interface/BufferVk.h @@ -62,6 +62,9 @@ DILIGENT_BEGIN_INTERFACE(IBufferVk, IBuffer) /// If the buffer state is known to the engine (i.e. not Diligent::RESOURCE_STATE_UNKNOWN), /// returns Vulkan access flags corresponding to the state. If the state is unknown, returns 0. VIRTUAL VkAccessFlags METHOD(GetAccessFlags)(THIS) CONST PURE; + + /// Returns a Vulkan device address. + VIRTUAL VkDeviceAddress METHOD(GetVkDeviceAddress)(THIS) CONST PURE; }; DILIGENT_END_INTERFACE @@ -72,6 +75,7 @@ DILIGENT_END_INTERFACE # define IBufferVk_GetVkBuffer(This) CALL_IFACE_METHOD(BufferVk, GetVkBuffer, This) # define IBufferVk_SetAccessFlags(This, ...) CALL_IFACE_METHOD(BufferVk, SetAccessFlags, This, __VA_ARGS__) # define IBufferVk_GetAccessFlags(This) CALL_IFACE_METHOD(BufferVk, GetAccessFlags, This) +# define IBufferVk_GetVkDeviceAddress(This) CALL_IFACE_METHOD(BufferVk, GetVkDeviceAddress, This) #endif diff --git a/Graphics/GraphicsEngineVulkan/interface/RenderDeviceVk.h b/Graphics/GraphicsEngineVulkan/interface/RenderDeviceVk.h index dfded94f..cba7f360 100644 --- a/Graphics/GraphicsEngineVulkan/interface/RenderDeviceVk.h +++ b/Graphics/GraphicsEngineVulkan/interface/RenderDeviceVk.h @@ -112,6 +112,44 @@ DILIGENT_BEGIN_INTERFACE(IRenderDeviceVk, IRenderDevice) const BufferDesc REF BuffDesc, RESOURCE_STATE InitialState, IBuffer** ppBuffer) PURE; + + /// Creates a bottom-level AS object from native Vulkan resource + + /// \param [in] vkBLAS - Vulkan acceleration structure handle. + /// \param [in] Desc - Bottom-level AS description. + /// \param [in] InitialState - Initial BLAS state. Can be RESOURCE_STATE_UNKNOWN, RESOURCE_STATE_BUILD_AS_READ, RESOURCE_STATE_BUILD_AS_WRITE. + /// See Diligent::RESOURCE_STATE. + /// \param [out] ppBLAS - Address of the memory location where the pointer to the + /// bottom-level AS interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + /// \note Created bottom-level AS object does not take ownership of the Vulkan acceleration structure and will not + /// destroy it once released. The application must not destroy Vulkan acceleration structure while it is + /// in use by the engine. + VIRTUAL void METHOD(CreateBLASFromVulkanResource)(THIS_ + VkAccelerationStructureKHR vkBLAS, + const BottomLevelASDesc REF Desc, + RESOURCE_STATE InitialState, + IBottomLevelAS** ppBLAS) PURE; + + /// Creates a top-level AS object from native Vulkan resource + + /// \param [in] vkTLAS - Vulkan acceleration structure handle. + /// \param [in] Desc - Bottom-level AS description. + /// \param [in] InitialState - Initial TLAS state. Can be RESOURCE_STATE_UNKNOWN, RESOURCE_STATE_BUILD_AS_READ, RESOURCE_STATE_BUILD_AS_WRITE, RESOURCE_STATE_RAY_TRACING. + /// See Diligent::RESOURCE_STATE. + /// \param [out] ppTLAS - Address of the memory location where the pointer to the + /// top-level AS interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + /// \note Created top-level AS object does not take ownership of the Vulkan acceleration structure and will not + /// destroy it once released. The application must not destroy Vulkan acceleration structure while it is + /// in use by the engine. + VIRTUAL void METHOD(CreateTLASFromVulkanResource)(THIS_ + VkAccelerationStructureKHR vkTLAS, + const TopLevelASDesc REF Desc, + RESOURCE_STATE InitialState, + ITopLevelAS** ppTLAS) PURE; }; DILIGENT_END_INTERFACE @@ -129,6 +167,8 @@ DILIGENT_END_INTERFACE # define IRenderDeviceVk_IsFenceSignaled(This, ...) CALL_IFACE_METHOD(RenderDeviceVk, IsFenceSignaled, This, __VA_ARGS__) # define IRenderDeviceVk_CreateTextureFromVulkanImage(This, ...) CALL_IFACE_METHOD(RenderDeviceVk, CreateTextureFromVulkanImage, This, __VA_ARGS__) # define IRenderDeviceVk_CreateBufferFromVulkanResource(This, ...) CALL_IFACE_METHOD(RenderDeviceVk, CreateBufferFromVulkanResource, This, __VA_ARGS__) +# define IRenderDeviceVk_CreateBLASFromVulkanResource(This, ...) CALL_IFACE_METHOD(RenderDeviceVk, CreateBLASFromVulkanResource, This, __VA_ARGS__) +# define IRenderDeviceVk_CreateTLASFromVulkanResource(This, ...) CALL_IFACE_METHOD(RenderDeviceVk, CreateTLASFromVulkanResource, This, __VA_ARGS__) // clang-format on diff --git a/Graphics/GraphicsEngineVulkan/interface/ShaderBindingTableVk.h b/Graphics/GraphicsEngineVulkan/interface/ShaderBindingTableVk.h new file mode 100644 index 00000000..10970156 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/interface/ShaderBindingTableVk.h @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2020 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 +/// Definition of the Diligent::IShaderBindingTableVk interface + +#include "../../GraphicsEngine/interface/ShaderBindingTable.h" +#include "DeviceContextVk.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {31ED9B4B-4FF4-44D8-AE71-12B5D8AF7F93} +static const INTERFACE_ID IID_ShaderBindingTableVk = + {0x31ed9b4b, 0x4ff4, 0x44d8, {0xae, 0x71, 0x12, 0xb5, 0xd8, 0xaf, 0x7f, 0x93}}; + +#define DILIGENT_INTERFACE_NAME IShaderBindingTableVk +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IShaderBindingTableVkInclusiveMethods \ + IShaderBindingTableInclusiveMethods; \ + IShaderBindingTableVkMethods ShaderBindingTableVk + +#if DILIGENT_CPP_INTERFACE // Empty structs are not allwed in C + +// clang-format off +/// Exposes Vulkan-specific functionality of a Shader binding table object. +DILIGENT_BEGIN_INTERFACE(IShaderBindingTableVk, IShaderBindingTable) +{ +}; +DILIGENT_END_INTERFACE +// clang-format on + +#endif + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/interface/TopLevelASVk.h b/Graphics/GraphicsEngineVulkan/interface/TopLevelASVk.h new file mode 100644 index 00000000..c09f10c0 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/interface/TopLevelASVk.h @@ -0,0 +1,68 @@ +/* + * Copyright 2019-2020 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 +/// Definition of the Diligent::ITopLevelASVk interface + +#include "../../GraphicsEngine/interface/TopLevelAS.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {356FFFFA-9E57-49F7-8FF4-7017B61BE6A8} +static const INTERFACE_ID IID_TopLevelASVk = + {0x356ffffa, 0x9e57, 0x49f7, {0x8f, 0xf4, 0x70, 0x17, 0xb6, 0x1b, 0xe6, 0xa8}}; + +#define DILIGENT_INTERFACE_NAME ITopLevelASVk +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define ITopLevelASVkInclusiveMethods \ + ITopLevelASInclusiveMethods; \ + ITopLevelASVkMethods TopLevelASVk + +/// Exposes Vulkan-specific functionality of a Top-level acceleration structure object. +DILIGENT_BEGIN_INTERFACE(ITopLevelASVk, ITopLevelAS) +{ + /// Returns a Vulkan TLAS object handle. + VIRTUAL VkAccelerationStructureKHR METHOD(GetVkTLAS)(THIS) CONST PURE; + + /// Returns a Vulkan TLAS device address. + VIRTUAL VkDeviceAddress METHOD(GetVkDeviceAddress)(THIS) CONST PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +# define ITopLevelASVk_GetVkTLAS(This) CALL_IFACE_METHOD(TopLevelASVk, GetVkTLAS, This) +# define ITopLevelASVk_GetVkDeviceAddress(This) CALL_IFACE_METHOD(TopLevelASVk, GetVkDeviceAddress, This) + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/BottomLevelASVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BottomLevelASVkImpl.cpp new file mode 100644 index 00000000..19253b1f --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/BottomLevelASVkImpl.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2019-2020 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 "BottomLevelASVkImpl.hpp" +#include "VulkanTypeConversions.hpp" + +namespace Diligent +{ + +BottomLevelASVkImpl::BottomLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const BottomLevelASDesc& Desc) : + TBottomLevelASBase{pRefCounters, pRenderDeviceVk, Desc} +{ + const auto& LogicalDevice = pRenderDeviceVk->GetLogicalDevice(); + const auto& PhysicalDevice = pRenderDeviceVk->GetPhysicalDevice(); + const auto& Limits = PhysicalDevice.GetExtProperties().AccelStruct; + Uint32 AccelStructSize = m_Desc.CompactedSize; + + if (AccelStructSize == 0) + { + VkAccelerationStructureBuildGeometryInfoKHR vkBuildInfo = {}; + std::vector<VkAccelerationStructureGeometryKHR> vkGeometries; + std::vector<uint32_t> MaxPrimitiveCounts; + VkAccelerationStructureBuildSizesInfoKHR vkSizeInfo = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR}; + + vkGeometries.resize(Desc.TriangleCount + Desc.BoxCount); + MaxPrimitiveCounts.resize(vkGeometries.size()); + + if (m_Desc.pTriangles != nullptr) + { + Uint32 MaxPrimitiveCount = 0; + for (uint32_t i = 0; i < m_Desc.TriangleCount; ++i) + { + auto& src = m_Desc.pTriangles[i]; + auto& dst = vkGeometries[i]; + auto& tri = dst.geometry.triangles; + + dst.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + dst.pNext = nullptr; + dst.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; + dst.flags = VkGeometryFlagsKHR(0); + + tri.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; + tri.pNext = nullptr; + tri.vertexFormat = TypeToVkFormat(src.VertexValueType, src.VertexComponentCount, src.VertexValueType < VT_FLOAT16); + tri.maxVertex = src.MaxVertexCount; + tri.indexType = TypeToVkIndexType(src.IndexType); + tri.transformData.deviceAddress = src.AllowsTransforms ? 1 : 0; + MaxPrimitiveCounts[i] = src.MaxPrimitiveCount; + + MaxPrimitiveCount += src.MaxPrimitiveCount; + } + VERIFY_EXPR(MaxPrimitiveCount <= Limits.maxPrimitiveCount); + } + else if (m_Desc.pBoxes != nullptr) + { + Uint32 MaxBoxCount = 0; + for (uint32_t i = 0; i < m_Desc.BoxCount; ++i) + { + auto& src = m_Desc.pBoxes[i]; + auto& dst = vkGeometries[i]; + auto& box = dst.geometry.aabbs; + + dst.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + dst.pNext = nullptr; + dst.geometryType = VK_GEOMETRY_TYPE_AABBS_KHR; + + box.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR; + box.pNext = nullptr; + MaxPrimitiveCounts[i] = src.MaxBoxCount; + + MaxBoxCount += src.MaxBoxCount; + } + VERIFY_EXPR(MaxBoxCount <= Limits.maxPrimitiveCount); + } + else + { + UNEXPECTED("Either pTriangles or pBoxes must not be null"); + } + + vkBuildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + vkBuildInfo.flags = BuildASFlagsToVkBuildAccelerationStructureFlags(m_Desc.Flags); + vkBuildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + vkBuildInfo.pGeometries = vkGeometries.data(); + vkBuildInfo.geometryCount = static_cast<uint32_t>(vkGeometries.size()); + + VERIFY_EXPR(vkBuildInfo.geometryCount <= Limits.maxGeometryCount); + + LogicalDevice.GetAccelerationStructureBuildSizes(vkBuildInfo, MaxPrimitiveCounts.data(), vkSizeInfo); + + AccelStructSize = static_cast<Uint32>(vkSizeInfo.accelerationStructureSize); + m_ScratchSize.Build = static_cast<Uint32>(vkSizeInfo.buildScratchSize); + m_ScratchSize.Update = static_cast<Uint32>(vkSizeInfo.updateScratchSize); + } + + VkBufferCreateInfo vkBuffCI = {}; + + vkBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + vkBuffCI.flags = 0; + vkBuffCI.size = AccelStructSize; + vkBuffCI.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR; + vkBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vkBuffCI.queueFamilyIndexCount = 0; + vkBuffCI.pQueueFamilyIndices = nullptr; + + m_VulkanBuffer = LogicalDevice.CreateBuffer(vkBuffCI, m_Desc.Name); + + VkMemoryRequirements MemReqs = LogicalDevice.GetBufferMemoryRequirements(m_VulkanBuffer); + uint32_t MemoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VERIFY(IsPowerOfTwo(MemReqs.alignment), "Alignment is not power of 2!"); + m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT); + + m_MemoryAlignedOffset = Align(VkDeviceSize{m_MemoryAllocation.UnalignedOffset}, MemReqs.alignment); + VERIFY(m_MemoryAllocation.Size >= MemReqs.size + (m_MemoryAlignedOffset - m_MemoryAllocation.UnalignedOffset), "Size of memory allocation is too small"); + auto Memory = m_MemoryAllocation.Page->GetVkMemory(); + auto err = LogicalDevice.BindBufferMemory(m_VulkanBuffer, Memory, m_MemoryAlignedOffset); + CHECK_VK_ERROR_AND_THROW(err, "Failed to bind buffer memory"); + + VkAccelerationStructureCreateInfoKHR vkAccelStrCI = {}; + + vkAccelStrCI.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + vkAccelStrCI.createFlags = 0; + vkAccelStrCI.buffer = m_VulkanBuffer; + vkAccelStrCI.offset = 0; + vkAccelStrCI.size = AccelStructSize; + vkAccelStrCI.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + + m_VulkanBLAS = LogicalDevice.CreateAccelStruct(vkAccelStrCI, m_Desc.Name); + + m_DeviceAddress = LogicalDevice.GetAccelerationStructureDeviceAddress(m_VulkanBLAS); + + SetState(RESOURCE_STATE_BUILD_AS_READ); +} + +BottomLevelASVkImpl::BottomLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const BottomLevelASDesc& Desc, + RESOURCE_STATE InitialState, + VkAccelerationStructureKHR vkBLAS) : + TBottomLevelASBase{pRefCounters, pRenderDeviceVk, Desc}, + m_VulkanBLAS{vkBLAS} +{ + SetState(InitialState); + m_DeviceAddress = pRenderDeviceVk->GetLogicalDevice().GetAccelerationStructureDeviceAddress(m_VulkanBLAS); +} + +BottomLevelASVkImpl::~BottomLevelASVkImpl() +{ + // Vk object can only be destroyed when it is no longer used by the GPU + if (m_VulkanBLAS != VK_NULL_HANDLE) + m_pDevice->SafeReleaseDeviceObject(std::move(m_VulkanBLAS), m_Desc.CommandQueueMask); + if (m_VulkanBuffer != VK_NULL_HANDLE) + m_pDevice->SafeReleaseDeviceObject(std::move(m_VulkanBuffer), m_Desc.CommandQueueMask); + if (m_MemoryAllocation.Page != nullptr) + m_pDevice->SafeReleaseDeviceObject(std::move(m_MemoryAllocation), m_Desc.CommandQueueMask); +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp index 0d41ec33..06bb887a 100644 --- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp @@ -86,54 +86,89 @@ BufferVkImpl::BufferVkImpl(IReferenceCounters* pRefCounters, VkBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | // The buffer can be used as the source of a transfer command VK_BUFFER_USAGE_TRANSFER_DST_BIT; // The buffer can be used as the destination of a transfer command - if (m_Desc.BindFlags & BIND_UNORDERED_ACCESS) - { - // VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - // HLSL formatted buffers are mapped to GLSL storage buffers: - // - // RWBuffer<uint4> RWBuff - // - // | - // V - // - // layout(std140, binding = 3) buffer RWBuff - // { - // uvec4 data[]; - // }g_RWBuff; - // - // So we have to set both VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT and VK_BUFFER_USAGE_STORAGE_BUFFER_BIT bits - VkBuffCI.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - - // Each element of pDynamicOffsets of vkCmdBindDescriptorSets function which corresponds to a descriptor - // binding with type VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC must be a multiple of - // VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment (13.2.5) - m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minTexelBufferOffsetAlignment)); - m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minStorageBufferOffsetAlignment)); - } - if (m_Desc.BindFlags & BIND_SHADER_RESOURCE) - { - // VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - // HLSL buffer SRVs are mapped to storge buffers in GLSL, so we need to set both - // VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT and VK_BUFFER_USAGE_STORAGE_BUFFER_BIT flags - VkBuffCI.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minTexelBufferOffsetAlignment)); - m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minStorageBufferOffsetAlignment)); - } - if (m_Desc.BindFlags & BIND_VERTEX_BUFFER) - VkBuffCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_INDEX_BUFFER) - VkBuffCI.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_INDIRECT_DRAW_ARGS) - VkBuffCI.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_UNIFORM_BUFFER) + static_assert(BIND_FLAGS_LAST == 0x400, "Please update this function to handle the new bind flags"); + + for (Uint32 BindFlag = 1; BindFlag <= m_Desc.BindFlags; BindFlag <<= 1) { - VkBuffCI.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + if ((m_Desc.BindFlags & BindFlag) != BindFlag) + continue; - // Each element of pDynamicOffsets parameter of vkCmdBindDescriptorSets function which corresponds to a descriptor - // binding with type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC must be a multiple of - // VkPhysicalDeviceLimits::minUniformBufferOffsetAlignment (13.2.5) - m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minUniformBufferOffsetAlignment)); + switch (BindFlag) + { + case BIND_UNORDERED_ACCESS: + { + // VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + // HLSL formatted buffers are mapped to GLSL storage buffers: + // + // RWBuffer<uint4> RWBuff + // + // | + // V + // + // layout(std140, binding = 3) buffer RWBuff + // { + // uvec4 data[]; + // }g_RWBuff; + // + // So we have to set both VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT and VK_BUFFER_USAGE_STORAGE_BUFFER_BIT bits + VkBuffCI.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + + // Each element of pDynamicOffsets of vkCmdBindDescriptorSets function which corresponds to a descriptor + // binding with type VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC must be a multiple of + // VkPhysicalDeviceLimits::minStorageBufferOffsetAlignment (13.2.5) + m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minTexelBufferOffsetAlignment)); + m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minStorageBufferOffsetAlignment)); + break; + } + case BIND_SHADER_RESOURCE: + { + // VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + // HLSL buffer SRVs are mapped to storge buffers in GLSL, so we need to set both + // VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT and VK_BUFFER_USAGE_STORAGE_BUFFER_BIT flags + VkBuffCI.usage |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + + m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minTexelBufferOffsetAlignment)); + m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minStorageBufferOffsetAlignment)); + break; + } + case BIND_VERTEX_BUFFER: + { + VkBuffCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + break; + } + case BIND_INDEX_BUFFER: + { + VkBuffCI.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + break; + } + case BIND_INDIRECT_DRAW_ARGS: + { + VkBuffCI.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; + break; + } + case BIND_UNIFORM_BUFFER: + { + VkBuffCI.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + + // Each element of pDynamicOffsets parameter of vkCmdBindDescriptorSets function which corresponds to a descriptor + // binding with type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC must be a multiple of + // VkPhysicalDeviceLimits::minUniformBufferOffsetAlignment (13.2.5) + m_DynamicOffsetAlignment = std::max(m_DynamicOffsetAlignment, static_cast<Uint32>(DeviceLimits.minUniformBufferOffsetAlignment)); + break; + } + case BIND_RAY_TRACING: + { + VkBuffCI.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; // for scratch buffer + VkBuffCI.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VkBuffCI.usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; // acceleration structure build inputs such as vertex, index, transform, aabb, and instance data + VkBuffCI.usage |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR; + break; + } + default: + UNEXPECTED("unsupported buffer binding type"); + break; + } } if (m_Desc.Usage == USAGE_DYNAMIC) @@ -213,11 +248,15 @@ BufferVkImpl::BufferVkImpl(IReferenceCounters* pRefCounters, MemoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, vkMemoryFlags); } + VkMemoryAllocateFlags AllocateFlags = 0; + if (VkBuffCI.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) + AllocateFlags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + if (MemoryTypeIndex == VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex) LOG_ERROR_AND_THROW("Failed to find suitable memory type for buffer '", m_Desc.Name, '\''); VERIFY(IsPowerOfTwo(MemReqs.alignment), "Alignment is not power of 2!"); - m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs.size, MemReqs.alignment, MemoryTypeIndex); + m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, AllocateFlags); m_BufferMemoryAlignedOffset = Align(VkDeviceSize{m_MemoryAllocation.UnalignedOffset}, MemReqs.alignment); VERIFY(m_MemoryAllocation.Size >= MemReqs.size + (m_BufferMemoryAlignedOffset - m_MemoryAllocation.UnalignedOffset), "Size of memory allocation is too small"); @@ -285,12 +324,12 @@ BufferVkImpl::BufferVkImpl(IReferenceCounters* pRefCounters, VkCommandBuffer vkCmdBuff; pRenderDeviceVk->AllocateTransientCmdPool(CmdPool, vkCmdBuff, "Transient command pool to copy staging data to a device buffer"); - auto EnabledGraphicsShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages(); - VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledGraphicsShaderStages); + auto EnabledShaderStages = LogicalDevice.GetEnabledShaderStages(); + VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledShaderStages); InitialState = RESOURCE_STATE_COPY_DEST; VkAccessFlags AccessFlags = ResourceStateFlagsToVkAccessFlags(InitialState); VERIFY_EXPR(AccessFlags == VK_ACCESS_TRANSFER_WRITE_BIT); - VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, m_VulkanBuffer, 0, AccessFlags, EnabledGraphicsShaderStages); + VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, m_VulkanBuffer, 0, AccessFlags, EnabledShaderStages); // Copy commands MUST be recorded outside of a render pass instance. This is OK here // as copy will be the only command in the cmd buffer @@ -408,7 +447,7 @@ void BufferVkImpl::CreateViewInternal(const BufferViewDesc& OrigViewDesc, IBuffe VulkanUtilities::BufferViewWrapper BufferVkImpl::CreateView(struct BufferViewDesc& ViewDesc) { VulkanUtilities::BufferViewWrapper BuffView; - CorrectBufferViewDesc(ViewDesc); + ValidateAndCorrectBufferViewDesc(m_Desc, ViewDesc); if ((ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE || ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) && (m_Desc.Mode == BUFFER_MODE_FORMATTED || m_Desc.Mode == BUFFER_MODE_RAW)) { @@ -457,6 +496,32 @@ VkAccessFlags BufferVkImpl::GetAccessFlags() const return ResourceStateFlagsToVkAccessFlags(GetState()); } +VkDeviceAddress BufferVkImpl::GetVkDeviceAddress() const +{ + constexpr auto DeviceAddressFlags = BIND_RAY_TRACING; + + if (m_VulkanBuffer != VK_NULL_HANDLE && (m_Desc.BindFlags & DeviceAddressFlags) != 0) + { +#if DILIGENT_USE_VOLK + VkBufferDeviceAddressInfoKHR BufferInfo = {}; + + BufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR; + BufferInfo.buffer = m_VulkanBuffer; + VkDeviceAddress Result = vkGetBufferDeviceAddressKHR(m_pDevice->GetLogicalDevice().GetVkDevice(), &BufferInfo); + VERIFY_EXPR(Result > 0); + return Result; +#else + UNSUPPORTED("vkGetBufferDeviceAddressKHR is only available through Volk"); + return VkDeviceAddress{}; +#endif + } + else + { + UNEXPECTED("Can't get device address for buffer"); + return 0; + } +} + #ifdef DILIGENT_DEVELOPMENT void BufferVkImpl::DvpVerifyDynamicAllocation(DeviceContextVkImpl* pCtx) const { diff --git a/Graphics/GraphicsEngineVulkan/src/CommandPoolManager.cpp b/Graphics/GraphicsEngineVulkan/src/CommandPoolManager.cpp index e7fbc9bd..cf24d9ea 100644 --- a/Graphics/GraphicsEngineVulkan/src/CommandPoolManager.cpp +++ b/Graphics/GraphicsEngineVulkan/src/CommandPoolManager.cpp @@ -53,14 +53,17 @@ VulkanUtilities::CommandPoolWrapper CommandPoolManager::AllocateCommandPool(cons { std::lock_guard<std::mutex> LockGuard{m_Mutex}; + const auto& LogicalDevice = m_DeviceVkImpl.GetLogicalDevice(); + VulkanUtilities::CommandPoolWrapper CmdPool; if (!m_CmdPools.empty()) { CmdPool = std::move(m_CmdPools.front()); m_CmdPools.pop_front(); + + LogicalDevice.ResetCommandPool(CmdPool); } - auto& LogicalDevice = m_DeviceVkImpl.GetLogicalDevice(); if (CmdPool == VK_NULL_HANDLE) { VkCommandPoolCreateInfo CmdPoolCI = {}; @@ -74,8 +77,6 @@ VulkanUtilities::CommandPoolWrapper CommandPoolManager::AllocateCommandPool(cons DEV_CHECK_ERR(CmdPool != VK_NULL_HANDLE, "Failed to create Vulkan command pool"); } - LogicalDevice.ResetCommandPool(CmdPool); - #ifdef DILIGENT_DEVELOPMENT ++m_AllocatedPoolCounter; #endif diff --git a/Graphics/GraphicsEngineVulkan/src/DescriptorPoolManager.cpp b/Graphics/GraphicsEngineVulkan/src/DescriptorPoolManager.cpp index 412ddd7b..56a83b85 100644 --- a/Graphics/GraphicsEngineVulkan/src/DescriptorPoolManager.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DescriptorPoolManager.cpp @@ -59,6 +59,44 @@ VulkanUtilities::DescriptorPoolWrapper DescriptorPoolManager::CreateDescriptorPo return m_DeviceVkImpl.GetLogicalDevice().CreateDescriptorPool(PoolCI, DebugName); } +static std::vector<VkDescriptorPoolSize> PrunePoolSizes(RenderDeviceVkImpl& DeviceVkImpl, std::vector<VkDescriptorPoolSize>&& PoolSizes) +{ + const auto& Feats = DeviceVkImpl.GetLogicalDevice().GetEnabledExtFeatures(); + for (auto iter = PoolSizes.begin(); iter != PoolSizes.end();) + { + switch (iter->type) + { + case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: + if (Feats.RayTracingPipeline.rayTracingPipeline == VK_FALSE) + iter = PoolSizes.erase(iter); + else + ++iter; + break; + default: + ++iter; + } + } + return PoolSizes; +} + +DescriptorPoolManager::DescriptorPoolManager(RenderDeviceVkImpl& DeviceVkImpl, + std::string PoolName, + std::vector<VkDescriptorPoolSize> PoolSizes, + uint32_t MaxSets, + bool AllowFreeing) noexcept : + // clang-format off + m_DeviceVkImpl{DeviceVkImpl }, + m_PoolName {std::move(PoolName) }, + m_PoolSizes (PrunePoolSizes(DeviceVkImpl, std::move(PoolSizes))), + m_MaxSets {MaxSets }, + m_AllowFreeing{AllowFreeing } +// clang-format on +{ +#ifdef DILIGENT_DEVELOPMENT + m_AllocatedPoolCounter = 0; +#endif +} + DescriptorPoolManager::~DescriptorPoolManager() { DEV_CHECK_ERR(m_AllocatedPoolCounter == 0, "Not all allocated descriptor pools are returned to the pool manager"); diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 3520fa40..e0d6ee2b 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -69,7 +69,7 @@ DeviceContextVkImpl::DeviceContextVkImpl(IReferenceCounters* p bIsDeferred ? std::numeric_limits<decltype(m_NumCommandsToFlush)>::max() : EngineCI.NumCommandsToFlushCmdBuffer, bIsDeferred }, - m_CommandBuffer { pDeviceVkImpl->GetLogicalDevice().GetEnabledGraphicsShaderStages() }, + m_CommandBuffer { pDeviceVkImpl->GetLogicalDevice().GetEnabledShaderStages() }, m_CmdListAllocator { GetRawAllocator(), sizeof(CommandListVkImpl), 64 }, // Command pools must be thread safe because command buffers are returned into pools by release queues // potentially running in another thread @@ -117,6 +117,8 @@ DeviceContextVkImpl::DeviceContextVkImpl(IReferenceCounters* p m_DummyVB = pDummyVB.RawPtr<BufferVkImpl>(); m_vkClearValues.reserve(16); + + CreateASCompactedSizeQueryPool(); } DeviceContextVkImpl::~DeviceContextVkImpl() @@ -298,6 +300,11 @@ void DeviceContextVkImpl::SetPipelineState(IPipelineState* pPipelineState) m_CommandBuffer.BindComputePipeline(vkPipeline); break; } + case PIPELINE_TYPE_RAY_TRACING: + { + m_CommandBuffer.BindRayTracingPipeline(vkPipeline); + break; + } default: UNEXPECTED("unknown pipeline type"); } @@ -526,7 +533,7 @@ void DeviceContextVkImpl::PrepareForIndexedDraw(DRAW_FLAGS Flags, VALUE_TYPE Ind } #endif DEV_CHECK_ERR(IndexType == VT_UINT16 || IndexType == VT_UINT32, "Unsupported index format. Only R16_UINT and R32_UINT are allowed."); - VkIndexType vkIndexType = IndexType == VT_UINT16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; + VkIndexType vkIndexType = TypeToVkIndexType(IndexType); m_CommandBuffer.BindIndexBuffer(m_pIndexBuffer->GetVkBuffer(), m_IndexDataStartOffset + m_pIndexBuffer->GetDynamicOffset(m_ContextId, this), vkIndexType); } @@ -634,6 +641,19 @@ void DeviceContextVkImpl::PrepareForDispatchCompute() #endif } +void DeviceContextVkImpl::PrepareForRayTracing() +{ + EnsureVkCmdBuffer(); + + if (m_DescrSetBindInfo.DynamicOffsetCount != 0) + { + if (!m_DescrSetBindInfo.DynamicDescriptorsBound || m_DescrSetBindInfo.DynamicBuffersPresent) + { + m_pPipelineState->BindDescriptorSetsWithDynamicOffsets(GetCommandBuffer(), m_ContextId, this, m_DescrSetBindInfo); + } + } +} + void DeviceContextVkImpl::DispatchCompute(const DispatchComputeAttribs& Attribs) { if (!DvpVerifyDispatchArguments(Attribs)) @@ -2242,6 +2262,22 @@ void DeviceContextVkImpl::TransitionImageLayout(ITexture* pTexture, VkImageLayou } } +namespace +{ +NODISCARD inline bool ResourceStateHasWriteAccess(RESOURCE_STATE State) +{ + static_assert(RESOURCE_STATE_MAX_BIT == RESOURCE_STATE_RAY_TRACING, "This function must be updated to handle new resource state flag"); + constexpr RESOURCE_STATE WriteAccessStates = + RESOURCE_STATE_RENDER_TARGET | + RESOURCE_STATE_UNORDERED_ACCESS | + RESOURCE_STATE_COPY_DEST | + RESOURCE_STATE_RESOLVE_DEST | + RESOURCE_STATE_BUILD_AS_WRITE; + + return State & WriteAccessStates; +} +} // namespace + void DeviceContextVkImpl::TransitionTextureState(TextureVkImpl& TextureVk, RESOURCE_STATE OldState, RESOURCE_STATE NewState, @@ -2304,15 +2340,22 @@ void DeviceContextVkImpl::TransitionTextureState(TextureVkImpl& Textur pSubresRange->aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; } - // Note that when both old and new states are RESOURCE_STATE_UNORDERED_ACCESS, we need to execute UAV barrier - // to make sure that all UAV writes are complete and visible. + // Always add barrier after writes. + const bool AfterWrite = ResourceStateHasWriteAccess(OldState); + auto OldLayout = ResourceStateToVkImageLayout(OldState); auto NewLayout = ResourceStateToVkImageLayout(NewState); - m_CommandBuffer.TransitionImageLayout(vkImg, OldLayout, NewLayout, *pSubresRange); - if (UpdateTextureState) + auto OldStages = ResourceStateFlagsToVkPipelineStageFlags(OldState, m_CommandBuffer.GetEnabledShaderStages()); + auto NewStages = ResourceStateFlagsToVkPipelineStageFlags(NewState, m_CommandBuffer.GetEnabledShaderStages()); + + if (((OldState & NewState) != NewState) || OldLayout != NewLayout || AfterWrite) { - TextureVk.SetState(NewState); - VERIFY_EXPR(TextureVk.GetLayout() == NewLayout); + m_CommandBuffer.TransitionImageLayout(vkImg, OldLayout, NewLayout, *pSubresRange, OldStages, NewStages); + if (UpdateTextureState) + { + TextureVk.SetState(NewState); + VERIFY_EXPR(TextureVk.GetLayout() == NewLayout); + } } } @@ -2327,10 +2370,7 @@ void DeviceContextVkImpl::TransitionOrVerifyTextureState(TextureVkImpl& VERIFY(m_pActiveRenderPass == nullptr, "State transitions are not allowed inside a render pass"); if (Texture.IsInKnownState()) { - if (!Texture.CheckState(RequiredState)) - { - TransitionTextureState(Texture, RESOURCE_STATE_UNKNOWN, RequiredState, true); - } + TransitionTextureState(Texture, RESOURCE_STATE_UNKNOWN, RequiredState, true); VERIFY_EXPR(Texture.GetLayout() == ExpectedLayout); } } @@ -2395,9 +2435,10 @@ void DeviceContextVkImpl::TransitionBufferState(BufferVkImpl& BufferVk, RESOURCE } } - // When both old and new states are RESOURCE_STATE_UNORDERED_ACCESS, we need to execute UAV barrier - // to make sure that all UAV writes are complete and visible. - if (((OldState & NewState) != NewState) || NewState == RESOURCE_STATE_UNORDERED_ACCESS) + // Always add barrier after writes. + const bool AfterWrite = ResourceStateHasWriteAccess(OldState); + + if (((OldState & NewState) != NewState) || AfterWrite) { DEV_CHECK_ERR(BufferVk.m_VulkanBuffer != VK_NULL_HANDLE, "Cannot transition suballocated buffer"); VERIFY_EXPR(BufferVk.GetDynamicOffset(m_ContextId, this) == 0); @@ -2406,7 +2447,9 @@ void DeviceContextVkImpl::TransitionBufferState(BufferVkImpl& BufferVk, RESOURCE auto vkBuff = BufferVk.GetVkBuffer(); auto OldAccessFlags = ResourceStateFlagsToVkAccessFlags(OldState); auto NewAccessFlags = ResourceStateFlagsToVkAccessFlags(NewState); - m_CommandBuffer.BufferMemoryBarrier(vkBuff, OldAccessFlags, NewAccessFlags); + auto OldStages = ResourceStateFlagsToVkPipelineStageFlags(OldState, m_CommandBuffer.GetEnabledShaderStages()); + auto NewStages = ResourceStateFlagsToVkPipelineStageFlags(NewState, m_CommandBuffer.GetEnabledShaderStages()); + m_CommandBuffer.BufferMemoryBarrier(vkBuff, OldAccessFlags, NewAccessFlags, OldStages, NewStages); if (UpdateBufferState) { BufferVk.SetState(NewState); @@ -2425,10 +2468,7 @@ void DeviceContextVkImpl::TransitionOrVerifyBufferState(BufferVkImpl& VERIFY(m_pActiveRenderPass == nullptr, "State transitions are not allowed inside a render pass"); if (Buffer.IsInKnownState()) { - if (!Buffer.CheckState(RequiredState)) - { - TransitionBufferState(Buffer, RESOURCE_STATE_UNKNOWN, RequiredState, true); - } + TransitionBufferState(Buffer, RESOURCE_STATE_UNKNOWN, RequiredState, true); VERIFY_EXPR(Buffer.CheckAccessFlags(ExpectedAccessFlags)); } } @@ -2440,6 +2480,145 @@ void DeviceContextVkImpl::TransitionOrVerifyBufferState(BufferVkImpl& #endif } +void DeviceContextVkImpl::TransitionBLASState(BottomLevelASVkImpl& BLAS, + RESOURCE_STATE OldState, + RESOURCE_STATE NewState, + bool UpdateInternalState) +{ + VERIFY(m_pActiveRenderPass == nullptr, "State transitions are not allowed inside a render pass"); + if (OldState == RESOURCE_STATE_UNKNOWN) + { + if (BLAS.IsInKnownState()) + { + OldState = BLAS.GetState(); + } + else + { + LOG_ERROR_MESSAGE("Failed to transition the state of BLAS '", BLAS.GetDesc().Name, "' because the BLAS state is unknown and is not explicitly specified"); + return; + } + } + else + { + if (BLAS.IsInKnownState() && BLAS.GetState() != OldState) + { + LOG_ERROR_MESSAGE("The state ", GetResourceStateString(BLAS.GetState()), " of BLAS '", + BLAS.GetDesc().Name, "' does not match the old state ", GetResourceStateString(OldState), + " specified by the barrier"); + } + } + + // Always add barrier after writes. + const bool AfterWrite = ResourceStateHasWriteAccess(OldState); + + if ((OldState & NewState) != NewState || AfterWrite) + { + EnsureVkCmdBuffer(); + auto OldAccessFlags = AccelStructStateFlagsToVkAccessFlags(OldState); + auto NewAccessFlags = AccelStructStateFlagsToVkAccessFlags(NewState); + auto OldStages = ResourceStateFlagsToVkPipelineStageFlags(OldState, m_CommandBuffer.GetEnabledShaderStages()); + auto NewStages = ResourceStateFlagsToVkPipelineStageFlags(NewState, m_CommandBuffer.GetEnabledShaderStages()); + m_CommandBuffer.ASMemoryBarrier(OldAccessFlags, NewAccessFlags, OldStages, NewStages); + if (UpdateInternalState) + { + BLAS.SetState(NewState); + } + } +} + +void DeviceContextVkImpl::TransitionTLASState(TopLevelASVkImpl& TLAS, + RESOURCE_STATE OldState, + RESOURCE_STATE NewState, + bool UpdateInternalState) +{ + VERIFY(m_pActiveRenderPass == nullptr, "State transitions are not allowed inside a render pass"); + if (OldState == RESOURCE_STATE_UNKNOWN) + { + if (TLAS.IsInKnownState()) + { + OldState = TLAS.GetState(); + } + else + { + LOG_ERROR_MESSAGE("Failed to transition the state of TLAS '", TLAS.GetDesc().Name, "' because the TLAS state is unknown and is not explicitly specified"); + return; + } + } + else + { + if (TLAS.IsInKnownState() && TLAS.GetState() != OldState) + { + LOG_ERROR_MESSAGE("The state ", GetResourceStateString(TLAS.GetState()), " of TLAS '", + TLAS.GetDesc().Name, "' does not match the old state ", GetResourceStateString(OldState), + " specified by the barrier"); + } + } + + // Always add barrier after writes. + const bool AfterWrite = ResourceStateHasWriteAccess(OldState); + + if ((OldState & NewState) != NewState || AfterWrite) + { + EnsureVkCmdBuffer(); + auto OldAccessFlags = AccelStructStateFlagsToVkAccessFlags(OldState); + auto NewAccessFlags = AccelStructStateFlagsToVkAccessFlags(NewState); + auto OldStages = ResourceStateFlagsToVkPipelineStageFlags(OldState, m_CommandBuffer.GetEnabledShaderStages()); + auto NewStages = ResourceStateFlagsToVkPipelineStageFlags(NewState, m_CommandBuffer.GetEnabledShaderStages()); + m_CommandBuffer.ASMemoryBarrier(OldAccessFlags, NewAccessFlags, OldStages, NewStages); + if (UpdateInternalState) + { + TLAS.SetState(NewState); + } + } +} + +void DeviceContextVkImpl::TransitionOrVerifyBLASState(BottomLevelASVkImpl& BLAS, + RESOURCE_STATE_TRANSITION_MODE TransitionMode, + RESOURCE_STATE RequiredState, + const char* OperationName) +{ + if (TransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION) + { + VERIFY(m_pActiveRenderPass == nullptr, "State transitions are not allowed inside a render pass"); + if (BLAS.IsInKnownState()) + { + TransitionBLASState(BLAS, RESOURCE_STATE_UNKNOWN, RequiredState, true); + } + } +#ifdef DILIGENT_DEVELOPMENT + else if (TransitionMode == RESOURCE_STATE_TRANSITION_MODE_VERIFY) + { + DvpVerifyBLASState(BLAS, RequiredState, OperationName); + } +#endif +} + +void DeviceContextVkImpl::TransitionOrVerifyTLASState(TopLevelASVkImpl& TLAS, + RESOURCE_STATE_TRANSITION_MODE TransitionMode, + RESOURCE_STATE RequiredState, + const char* OperationName) +{ + if (TransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION) + { + VERIFY(m_pActiveRenderPass == nullptr, "State transitions are not allowed inside a render pass"); + if (TLAS.IsInKnownState()) + { + TransitionTLASState(TLAS, RESOURCE_STATE_UNKNOWN, RequiredState, true); + } + } +#ifdef DILIGENT_DEVELOPMENT + else if (TransitionMode == RESOURCE_STATE_TRANSITION_MODE_VERIFY) + { + DvpVerifyTLASState(TLAS, RequiredState, OperationName); + } + + if (RequiredState & (RESOURCE_STATE_RAY_TRACING | RESOURCE_STATE_BUILD_AS_READ)) + { + TLAS.ValidateContent(); + } +#endif +} + VulkanDynamicAllocation DeviceContextVkImpl::AllocateDynamicSpace(Uint32 SizeInBytes, Uint32 Alignment) { auto DynAlloc = m_DynamicHeap.Allocate(SizeInBytes, Alignment); @@ -2472,23 +2651,31 @@ void DeviceContextVkImpl::TransitionResourceStates(Uint32 BarrierCount, StateTra } VERIFY(Barrier.TransitionType == STATE_TRANSITION_TYPE_IMMEDIATE || Barrier.TransitionType == STATE_TRANSITION_TYPE_END, "Unexpected barrier type"); - if (Barrier.pTexture) + if (RefCntAutoPtr<TextureVkImpl> pTexture{Barrier.pResource, IID_TextureVk}) { - auto* pTextureVkImpl = ValidatedCast<TextureVkImpl>(Barrier.pTexture); - VkImageSubresourceRange SubResRange; SubResRange.aspectMask = 0; SubResRange.baseMipLevel = Barrier.FirstMipLevel; SubResRange.levelCount = (Barrier.MipLevelsCount == REMAINING_MIP_LEVELS) ? VK_REMAINING_MIP_LEVELS : Barrier.MipLevelsCount; SubResRange.baseArrayLayer = Barrier.FirstArraySlice; SubResRange.layerCount = (Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES) ? VK_REMAINING_ARRAY_LAYERS : Barrier.ArraySliceCount; - TransitionTextureState(*pTextureVkImpl, Barrier.OldState, Barrier.NewState, Barrier.UpdateResourceState, &SubResRange); + TransitionTextureState(*pTexture, Barrier.OldState, Barrier.NewState, Barrier.UpdateResourceState, &SubResRange); + } + else if (RefCntAutoPtr<BufferVkImpl> pBuffer{Barrier.pResource, IID_BufferVk}) + { + TransitionBufferState(*pBuffer, Barrier.OldState, Barrier.NewState, Barrier.UpdateResourceState); + } + else if (RefCntAutoPtr<BottomLevelASVkImpl> pBottomLevelAS{Barrier.pResource, IID_BottomLevelAS}) + { + TransitionBLASState(*pBottomLevelAS, Barrier.OldState, Barrier.NewState, Barrier.UpdateResourceState); + } + else if (RefCntAutoPtr<TopLevelASVkImpl> pTopLevelAS{Barrier.pResource, IID_TopLevelAS}) + { + TransitionTLASState(*pTopLevelAS, Barrier.OldState, Barrier.NewState, Barrier.UpdateResourceState); } else { - VERIFY_EXPR(Barrier.pBuffer != nullptr); - auto* pBufferVkImpl = ValidatedCast<BufferVkImpl>(Barrier.pBuffer); - TransitionBufferState(*pBufferVkImpl, Barrier.OldState, Barrier.NewState, Barrier.UpdateResourceState); + UNEXPECTED("unsupported resource type"); } } } @@ -2548,4 +2735,430 @@ void DeviceContextVkImpl::ResolveTextureSubresource(ITexture* 1, &ResolveRegion); } +void DeviceContextVkImpl::BuildBLAS(const BuildBLASAttribs& Attribs) +{ + if (!TDeviceContextBase::BuildBLAS(Attribs, 0)) + return; + + auto* pBLASVk = ValidatedCast<BottomLevelASVkImpl>(Attribs.pBLAS); + auto* pScratchVk = ValidatedCast<BufferVkImpl>(Attribs.pScratchBuffer); + auto& BLASDesc = pBLASVk->GetDesc(); + + EnsureVkCmdBuffer(); + + const char* OpName = "Build BottomLevelAS (DeviceContextVkImpl::BuildBLAS)"; + TransitionOrVerifyBLASState(*pBLASVk, Attribs.BLASTransitionMode, RESOURCE_STATE_BUILD_AS_WRITE, OpName); + TransitionOrVerifyBufferState(*pScratchVk, Attribs.ScratchBufferTransitionMode, RESOURCE_STATE_BUILD_AS_WRITE, VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, OpName); + + VkAccelerationStructureBuildGeometryInfoKHR vkASBuildInfo = {}; + std::vector<VkAccelerationStructureBuildRangeInfoKHR> vkRanges; + std::vector<VkAccelerationStructureGeometryKHR> vkGeometries; + + if (Attribs.pTriangleData != nullptr) + { + vkGeometries.resize(Attribs.TriangleDataCount); + vkRanges.resize(Attribs.TriangleDataCount); + pBLASVk->SetActualGeometryCount(Attribs.TriangleDataCount); + + for (Uint32 i = 0; i < Attribs.TriangleDataCount; ++i) + { + const auto& SrcTris = Attribs.pTriangleData[i]; + Uint32 Idx = i; + Uint32 GeoIdx = pBLASVk->UpdateGeometryIndex(SrcTris.GeometryName, Idx, Attribs.Update); + + if (GeoIdx == INVALID_INDEX || Idx == INVALID_INDEX) + { + UNEXPECTED("Failed to find geometry by name"); + continue; + } + + auto& vkGeo = vkGeometries[Idx]; + auto& vkTris = vkGeo.geometry.triangles; + auto& off = vkRanges[Idx]; + const auto& TriDesc = BLASDesc.pTriangles[GeoIdx]; + + vkGeo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + vkGeo.pNext = nullptr; + vkGeo.flags = GeometryFlagsToVkGeometryFlags(SrcTris.Flags); + vkGeo.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; + vkTris.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; + vkTris.pNext = nullptr; + + auto* const pVB = ValidatedCast<BufferVkImpl>(SrcTris.pVertexBuffer); + + // vertex format in SrcTris may be undefined, so use vertex format from description + vkTris.vertexFormat = TypeToVkFormat(TriDesc.VertexValueType, TriDesc.VertexComponentCount, TriDesc.VertexValueType < VT_FLOAT16); + vkTris.vertexStride = SrcTris.VertexStride; + vkTris.maxVertex = SrcTris.VertexCount; + vkTris.vertexData.deviceAddress = pVB->GetVkDeviceAddress() + SrcTris.VertexOffset; + + TransitionOrVerifyBufferState(*pVB, Attribs.GeometryTransitionMode, RESOURCE_STATE_BUILD_AS_READ, VK_ACCESS_SHADER_READ_BIT, OpName); + + if (SrcTris.pIndexBuffer) + { + auto* const pIB = ValidatedCast<BufferVkImpl>(SrcTris.pIndexBuffer); + + // index type in SrcTris may be undefined, so use index type from description + vkTris.indexType = TypeToVkIndexType(TriDesc.IndexType); + vkTris.indexData.deviceAddress = pIB->GetVkDeviceAddress() + SrcTris.IndexOffset; + + TransitionOrVerifyBufferState(*pIB, Attribs.GeometryTransitionMode, RESOURCE_STATE_BUILD_AS_READ, VK_ACCESS_SHADER_READ_BIT, OpName); + } + else + { + vkTris.indexType = VK_INDEX_TYPE_NONE_KHR; + vkTris.indexData.deviceAddress = 0; + } + + if (SrcTris.pTransformBuffer) + { + auto* const pTB = ValidatedCast<BufferVkImpl>(SrcTris.pTransformBuffer); + vkTris.transformData.deviceAddress = pTB->GetVkDeviceAddress() + SrcTris.TransformBufferOffset; + + TransitionOrVerifyBufferState(*pTB, Attribs.GeometryTransitionMode, RESOURCE_STATE_BUILD_AS_READ, VK_ACCESS_SHADER_READ_BIT, OpName); + } + else + { + vkTris.transformData.deviceAddress = 0; + } + + off.primitiveCount = SrcTris.PrimitiveCount; + off.firstVertex = 0; + off.primitiveOffset = 0; + off.transformOffset = 0; + } + } + else if (Attribs.pBoxData != nullptr) + { + vkGeometries.resize(Attribs.BoxDataCount); + vkRanges.resize(Attribs.BoxDataCount); + pBLASVk->SetActualGeometryCount(Attribs.BoxDataCount); + + for (Uint32 i = 0; i < Attribs.BoxDataCount; ++i) + { + const auto& SrcBoxes = Attribs.pBoxData[i]; + Uint32 Idx = i; + Uint32 GeoIdx = pBLASVk->UpdateGeometryIndex(SrcBoxes.GeometryName, Idx, Attribs.Update); + + if (GeoIdx == INVALID_INDEX || Idx == INVALID_INDEX) + { + UNEXPECTED("Failed to find geometry by name"); + continue; + } + + auto& vkGeo = vkGeometries[Idx]; + auto& vkAABBs = vkGeo.geometry.aabbs; + auto& off = vkRanges[Idx]; + + vkGeo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + vkGeo.pNext = nullptr; + vkGeo.flags = GeometryFlagsToVkGeometryFlags(SrcBoxes.Flags); + vkGeo.geometryType = VK_GEOMETRY_TYPE_AABBS_KHR; + + auto* const pBB = ValidatedCast<BufferVkImpl>(SrcBoxes.pBoxBuffer); + vkAABBs.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR; + vkAABBs.pNext = nullptr; + vkAABBs.stride = SrcBoxes.BoxStride; + vkAABBs.data.deviceAddress = pBB->GetVkDeviceAddress() + SrcBoxes.BoxOffset; + + VERIFY(vkAABBs.data.deviceAddress % 8 == 0, "AABB start address is not properly aligned"); + + TransitionOrVerifyBufferState(*pBB, Attribs.GeometryTransitionMode, RESOURCE_STATE_BUILD_AS_READ, VK_ACCESS_SHADER_READ_BIT, OpName); + + off.firstVertex = 0; + off.transformOffset = 0; + off.primitiveOffset = 0; + off.primitiveCount = SrcBoxes.BoxCount; + } + } + + VkAccelerationStructureBuildRangeInfoKHR const* VkRangePtr = vkRanges.data(); + + vkASBuildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + vkASBuildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; // type must be compatible with create info + vkASBuildInfo.flags = BuildASFlagsToVkBuildAccelerationStructureFlags(BLASDesc.Flags); // flags must be compatible with create info + vkASBuildInfo.mode = Attribs.Update ? VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR : VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + vkASBuildInfo.srcAccelerationStructure = Attribs.Update ? pBLASVk->GetVkBLAS() : VK_NULL_HANDLE; + vkASBuildInfo.dstAccelerationStructure = pBLASVk->GetVkBLAS(); + vkASBuildInfo.geometryCount = static_cast<uint32_t>(vkGeometries.size()); + vkASBuildInfo.pGeometries = vkGeometries.data(); + vkASBuildInfo.ppGeometries = nullptr; + vkASBuildInfo.scratchData.deviceAddress = pScratchVk->GetVkDeviceAddress() + Attribs.ScratchBufferOffset; + + EnsureVkCmdBuffer(); + m_CommandBuffer.BuildAccelerationStructure(1, &vkASBuildInfo, &VkRangePtr); + ++m_State.NumCommands; + +#ifdef DILIGENT_DEVELOPMENT + pBLASVk->UpdateVersion(); +#endif +} + +void DeviceContextVkImpl::BuildTLAS(const BuildTLASAttribs& Attribs) +{ + if (!TDeviceContextBase::BuildTLAS(Attribs, 0)) + return; + + static_assert(TLAS_INSTANCE_DATA_SIZE == sizeof(VkAccelerationStructureInstanceKHR), "Value in TLAS_INSTANCE_DATA_SIZE doesn't match the actual instance description size"); + + auto* pTLASVk = ValidatedCast<TopLevelASVkImpl>(Attribs.pTLAS); + auto* pScratchVk = ValidatedCast<BufferVkImpl>(Attribs.pScratchBuffer); + auto* pInstancesVk = ValidatedCast<BufferVkImpl>(Attribs.pInstanceBuffer); + auto& TLASDesc = pTLASVk->GetDesc(); + + EnsureVkCmdBuffer(); + + const char* OpName = "Build TopLevelAS (DeviceContextVkImpl::BuildTLAS)"; + TransitionOrVerifyTLASState(*pTLASVk, Attribs.TLASTransitionMode, RESOURCE_STATE_BUILD_AS_WRITE, OpName); + TransitionOrVerifyBufferState(*pScratchVk, Attribs.ScratchBufferTransitionMode, RESOURCE_STATE_BUILD_AS_WRITE, VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, OpName); + + if (Attribs.Update) + { + if (!pTLASVk->UpdateInstances(Attribs.pInstances, Attribs.InstanceCount, Attribs.BaseContributionToHitGroupIndex, Attribs.HitGroupStride, Attribs.BindingMode)) + return; + } + else + { + if (!pTLASVk->SetInstanceData(Attribs.pInstances, Attribs.InstanceCount, Attribs.BaseContributionToHitGroupIndex, Attribs.HitGroupStride, Attribs.BindingMode)) + return; + } + + // copy instance data into instance buffer + { + size_t Size = Attribs.InstanceCount * sizeof(VkAccelerationStructureInstanceKHR); + auto TmpSpace = m_UploadHeap.Allocate(Size, 16); + + for (Uint32 i = 0; i < Attribs.InstanceCount; ++i) + { + const auto& Inst = Attribs.pInstances[i]; + const auto InstDesc = pTLASVk->GetInstanceDesc(Inst.InstanceName); + + if (InstDesc.InstanceIndex >= Attribs.InstanceCount) + { + UNEXPECTED("Failed to find instance by name"); + return; + } + + auto& vkASInst = static_cast<VkAccelerationStructureInstanceKHR*>(TmpSpace.CPUAddress)[InstDesc.InstanceIndex]; + auto* pBLASVk = ValidatedCast<BottomLevelASVkImpl>(Inst.pBLAS); + + static_assert(sizeof(vkASInst.transform) == sizeof(Inst.Transform), "size mismatch"); + std::memcpy(&vkASInst.transform, Inst.Transform.data, sizeof(vkASInst.transform)); + + vkASInst.instanceCustomIndex = Inst.CustomId; + vkASInst.instanceShaderBindingTableRecordOffset = InstDesc.ContributionToHitGroupIndex; + vkASInst.mask = Inst.Mask; + vkASInst.flags = InstanceFlagsToVkGeometryInstanceFlags(Inst.Flags); + vkASInst.accelerationStructureReference = pBLASVk->GetVkDeviceAddress(); + + TransitionOrVerifyBLASState(*pBLASVk, Attribs.BLASTransitionMode, RESOURCE_STATE_BUILD_AS_READ, OpName); + } + + UpdateBufferRegion(pInstancesVk, Attribs.InstanceBufferOffset, Size, TmpSpace.vkBuffer, TmpSpace.AlignedOffset, Attribs.InstanceBufferTransitionMode); + } + TransitionOrVerifyBufferState(*pInstancesVk, Attribs.InstanceBufferTransitionMode, RESOURCE_STATE_BUILD_AS_READ, VK_ACCESS_SHADER_READ_BIT, OpName); + + VkAccelerationStructureBuildGeometryInfoKHR vkASBuildInfo = {}; + VkAccelerationStructureBuildRangeInfoKHR vkRange = {}; + VkAccelerationStructureBuildRangeInfoKHR const* vkRangePtr = &vkRange; + VkAccelerationStructureGeometryKHR vkASGeometry = {}; + + vkRange.primitiveCount = Attribs.InstanceCount; + + vkASGeometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + vkASGeometry.pNext = nullptr; + vkASGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; + vkASGeometry.flags = 0; + + auto& vkASInst = vkASGeometry.geometry.instances; + vkASInst.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR; + vkASInst.pNext = nullptr; + vkASInst.arrayOfPointers = VK_FALSE; + vkASInst.data.deviceAddress = pInstancesVk->GetVkDeviceAddress() + Attribs.InstanceBufferOffset; + + VERIFY(vkASInst.data.deviceAddress % 16 == 0, "Instance data address is not properly aligned"); + + vkASBuildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + vkASBuildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; // type must be compatible with create info + vkASBuildInfo.flags = BuildASFlagsToVkBuildAccelerationStructureFlags(TLASDesc.Flags); // flags must be compatible with create info + vkASBuildInfo.mode = Attribs.Update ? VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR : VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + vkASBuildInfo.srcAccelerationStructure = Attribs.Update ? pTLASVk->GetVkTLAS() : VK_NULL_HANDLE; + vkASBuildInfo.dstAccelerationStructure = pTLASVk->GetVkTLAS(); + vkASBuildInfo.geometryCount = 1; + vkASBuildInfo.pGeometries = &vkASGeometry; + vkASBuildInfo.ppGeometries = nullptr; + vkASBuildInfo.scratchData.deviceAddress = pScratchVk->GetVkDeviceAddress() + Attribs.ScratchBufferOffset; + + m_CommandBuffer.BuildAccelerationStructure(1, &vkASBuildInfo, &vkRangePtr); + ++m_State.NumCommands; +} + +void DeviceContextVkImpl::CopyBLAS(const CopyBLASAttribs& Attribs) +{ + if (!TDeviceContextBase::CopyBLAS(Attribs, 0)) + return; + + auto* pSrcVk = ValidatedCast<BottomLevelASVkImpl>(Attribs.pSrc); + auto* pDstVk = ValidatedCast<BottomLevelASVkImpl>(Attribs.pDst); + + // Dst BLAS description has specified CompactedSize, but doesn't have specified pTriangles and pBoxes. + // We should copy geometries because it required for SBT to map geometry name to hit group. + pDstVk->CopyGeometryDescription(*pSrcVk); + pDstVk->SetActualGeometryCount(pSrcVk->GetActualGeometryCount()); + + VkCopyAccelerationStructureInfoKHR Info = {}; + + Info.sType = VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR; + Info.src = pSrcVk->GetVkBLAS(); + Info.dst = pDstVk->GetVkBLAS(); + Info.mode = CopyASModeToVkCopyAccelerationStructureMode(Attribs.Mode); + + EnsureVkCmdBuffer(); + + const char* OpName = "Copy BottomLevelAS (DeviceContextVkImpl::CopyBLAS)"; + TransitionOrVerifyBLASState(*pSrcVk, Attribs.SrcTransitionMode, RESOURCE_STATE_BUILD_AS_READ, OpName); + TransitionOrVerifyBLASState(*pDstVk, Attribs.DstTransitionMode, RESOURCE_STATE_BUILD_AS_WRITE, OpName); + + m_CommandBuffer.CopyAccelerationStructure(Info); + ++m_State.NumCommands; + +#ifdef DILIGENT_DEVELOPMENT + pDstVk->UpdateVersion(); +#endif +} + +void DeviceContextVkImpl::CopyTLAS(const CopyTLASAttribs& Attribs) +{ + if (!TDeviceContextBase::CopyTLAS(Attribs, 0)) + return; + + auto* pSrcVk = ValidatedCast<TopLevelASVkImpl>(Attribs.pSrc); + auto* pDstVk = ValidatedCast<TopLevelASVkImpl>(Attribs.pDst); + + // Instances specified in BuildTLAS command. + // We should copy instances because it required for SBT to map instance name to hit group. + pDstVk->CopyInstancceData(*pSrcVk); + + VkCopyAccelerationStructureInfoKHR Info = {}; + + Info.sType = VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR; + Info.src = pSrcVk->GetVkTLAS(); + Info.dst = pDstVk->GetVkTLAS(); + Info.mode = CopyASModeToVkCopyAccelerationStructureMode(Attribs.Mode); + + EnsureVkCmdBuffer(); + + const char* OpName = "Copy TopLevelAS (DeviceContextVkImpl::CopyTLAS)"; + TransitionOrVerifyTLASState(*pSrcVk, Attribs.SrcTransitionMode, RESOURCE_STATE_BUILD_AS_READ, OpName); + TransitionOrVerifyTLASState(*pDstVk, Attribs.DstTransitionMode, RESOURCE_STATE_BUILD_AS_WRITE, OpName); + + m_CommandBuffer.CopyAccelerationStructure(Info); + ++m_State.NumCommands; +} + +void DeviceContextVkImpl::WriteBLASCompactedSize(const WriteBLASCompactedSizeAttribs& Attribs) +{ + if (!TDeviceContextBase::WriteBLASCompactedSize(Attribs, 0)) + return; + + const Uint32 QueryIndex = 0; + auto* pBLASVk = ValidatedCast<BottomLevelASVkImpl>(Attribs.pBLAS); + auto* pDestBuffVk = ValidatedCast<BufferVkImpl>(Attribs.pDestBuffer); + + EnsureVkCmdBuffer(); + + const char* OpName = "Write AS compacted size (DeviceContextVkImpl::WriteBLASCompactedSize)"; + TransitionOrVerifyBLASState(*pBLASVk, Attribs.BLASTransitionMode, RESOURCE_STATE_BUILD_AS_READ, OpName); + TransitionOrVerifyBufferState(*pDestBuffVk, Attribs.BufferTransitionMode, RESOURCE_STATE_COPY_DEST, VK_ACCESS_TRANSFER_WRITE_BIT, OpName); + + m_CommandBuffer.WriteAccelerationStructuresProperties(pBLASVk->GetVkBLAS(), VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR, m_ASQueryPool, QueryIndex); + m_CommandBuffer.CopyQueryPoolResults(m_ASQueryPool, QueryIndex, 1, pDestBuffVk->GetVkBuffer(), Attribs.DestBufferOffset, sizeof(Uint64), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + m_CommandBuffer.ResetQueryPool(m_ASQueryPool, QueryIndex, 1); + ++m_State.NumCommands; +} + +void DeviceContextVkImpl::WriteTLASCompactedSize(const WriteTLASCompactedSizeAttribs& Attribs) +{ + if (!TDeviceContextBase::WriteTLASCompactedSize(Attribs, 0)) + return; + + const Uint32 QueryIndex = 0; + auto* pTLASVk = ValidatedCast<TopLevelASVkImpl>(Attribs.pTLAS); + auto* pDestBuffVk = ValidatedCast<BufferVkImpl>(Attribs.pDestBuffer); + + EnsureVkCmdBuffer(); + + const char* OpName = "Write AS compacted size (DeviceContextVkImpl::WriteTLASCompactedSize)"; + TransitionOrVerifyTLASState(*pTLASVk, Attribs.TLASTransitionMode, RESOURCE_STATE_BUILD_AS_READ, OpName); + TransitionOrVerifyBufferState(*pDestBuffVk, Attribs.BufferTransitionMode, RESOURCE_STATE_COPY_DEST, VK_ACCESS_TRANSFER_WRITE_BIT, OpName); + + m_CommandBuffer.WriteAccelerationStructuresProperties(pTLASVk->GetVkTLAS(), VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR, m_ASQueryPool, QueryIndex); + m_CommandBuffer.CopyQueryPoolResults(m_ASQueryPool, QueryIndex, 1, pDestBuffVk->GetVkBuffer(), Attribs.DestBufferOffset, sizeof(Uint64), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + m_CommandBuffer.ResetQueryPool(m_ASQueryPool, QueryIndex, 1); + ++m_State.NumCommands; +} + +void DeviceContextVkImpl::CreateASCompactedSizeQueryPool() +{ + if (m_pDevice->GetDeviceCaps().Features.RayTracing == DEVICE_FEATURE_STATE_ENABLED) + { + const auto& LogicalDevice = m_pDevice->GetLogicalDevice(); + VkQueryPoolCreateInfo Info = {}; + + Info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + Info.queryCount = 1; + Info.queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR; + + m_ASQueryPool = LogicalDevice.CreateQueryPool(Info); + } +} + +void DeviceContextVkImpl::TraceRays(const TraceRaysAttribs& Attribs) +{ + if (!TDeviceContextBase::TraceRays(Attribs, 0)) + return; + + auto* pSBTVk = ValidatedCast<ShaderBindingTableVkImpl>(Attribs.pSBT); + IBuffer* pBuffer = nullptr; + + ShaderBindingTableVkImpl::BindingTable RayGenShaderRecord = {}; + ShaderBindingTableVkImpl::BindingTable MissShaderTable = {}; + ShaderBindingTableVkImpl::BindingTable HitGroupTable = {}; + ShaderBindingTableVkImpl::BindingTable CallableShaderTable = {}; + + pSBTVk->GetData(pBuffer, RayGenShaderRecord, MissShaderTable, HitGroupTable, CallableShaderTable); + + auto* pSBTBufferVk = ValidatedCast<BufferVkImpl>(pBuffer); + + const char* OpName = "Trace rays (DeviceContextVkImpl::TraceRays)"; + TransitionOrVerifyBufferState(*pSBTBufferVk, Attribs.SBTTransitionMode, RESOURCE_STATE_COPY_DEST, VK_ACCESS_TRANSFER_WRITE_BIT, OpName); + + // buffer ranges are not intersected, so we don't need to add barriers between them + if (RayGenShaderRecord.pData) + UpdateBuffer(pBuffer, RayGenShaderRecord.Offset, RayGenShaderRecord.Size, RayGenShaderRecord.pData, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + + if (MissShaderTable.pData) + UpdateBuffer(pBuffer, MissShaderTable.Offset, MissShaderTable.Size, MissShaderTable.pData, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + + if (HitGroupTable.pData) + UpdateBuffer(pBuffer, HitGroupTable.Offset, HitGroupTable.Size, HitGroupTable.pData, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + + if (CallableShaderTable.pData) + UpdateBuffer(pBuffer, CallableShaderTable.Offset, CallableShaderTable.Size, CallableShaderTable.pData, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + + TransitionOrVerifyBufferState(*pSBTBufferVk, Attribs.SBTTransitionMode, RESOURCE_STATE_RAY_TRACING, VK_ACCESS_SHADER_READ_BIT, OpName); + + // clang-format off + VkStridedDeviceAddressRegionKHR RaygenShaderBindingTable = {pSBTBufferVk->GetVkDeviceAddress() + RayGenShaderRecord.Offset, RayGenShaderRecord.Stride, RayGenShaderRecord.Size }; + VkStridedDeviceAddressRegionKHR MissShaderBindingTable = {pSBTBufferVk->GetVkDeviceAddress() + MissShaderTable.Offset, MissShaderTable.Stride, MissShaderTable.Size }; + VkStridedDeviceAddressRegionKHR HitShaderBindingTable = {pSBTBufferVk->GetVkDeviceAddress() + HitGroupTable.Offset, HitGroupTable.Stride, HitGroupTable.Size }; + VkStridedDeviceAddressRegionKHR CallableShaderBindingTable = {pSBTBufferVk->GetVkDeviceAddress() + CallableShaderTable.Offset, CallableShaderTable.Stride, CallableShaderTable.Size}; + // clang-format on + + PrepareForRayTracing(); + m_CommandBuffer.TraceRays(RaygenShaderBindingTable, MissShaderBindingTable, HitShaderBindingTable, CallableShaderBindingTable, + Attribs.DimensionX, Attribs.DimensionY, Attribs.DimensionZ); + ++m_State.NumCommands; +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/EngineFactoryVk.cpp b/Graphics/GraphicsEngineVulkan/src/EngineFactoryVk.cpp index fbbd9492..ed537333 100644 --- a/Graphics/GraphicsEngineVulkan/src/EngineFactoryVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/EngineFactoryVk.cpp @@ -137,7 +137,12 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E try { + Uint32 Version = VK_API_VERSION_1_0; + if (EngineCI.Features.RayTracing != DEVICE_FEATURE_STATE_DISABLED) + Version = VK_API_VERSION_1_2; + auto Instance = VulkanUtilities::VulkanInstance::Create( + Version, EngineCI.EnableValidation, EngineCI.GlobalExtensionCount, EngineCI.ppGlobalExtensionNames, @@ -238,7 +243,11 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E VK_KHR_MAINTENANCE1_EXTENSION_NAME // To allow negative viewport height }; - const auto& DeviceExtFeatures = PhysicalDevice->GetExtFeatures(); + const VulkanUtilities::VulkanPhysicalDevice::ExtensionFeatures& DeviceExtFeatures = PhysicalDevice->GetExtFeatures(); + VulkanUtilities::VulkanPhysicalDevice::ExtensionFeatures EnabledExtFeats = {}; + + // SPIRV 1.5 is in Vulkan 1.2 core + EnabledExtFeats.Spirv15 = DeviceExtFeatures.Spirv15; #define ENABLE_FEATURE(IsFeatureSupported, Feature, FeatureName) \ do \ @@ -247,64 +256,71 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E GetFeatureState(EngineCI.Features.Feature, IsFeatureSupported, FeatureName); \ } while (false) - - auto MeshShaderFeats = DeviceExtFeatures.MeshShader; + const auto& MeshShaderFeats = DeviceExtFeatures.MeshShader; ENABLE_FEATURE(MeshShaderFeats.taskShader != VK_FALSE && MeshShaderFeats.meshShader != VK_FALSE, MeshShaders, "Mesh shaders are"); - auto ShaderFloat16Int8 = DeviceExtFeatures.ShaderFloat16Int8; + const auto& ShaderFloat16Int8Feats = DeviceExtFeatures.ShaderFloat16Int8; // clang-format off - ENABLE_FEATURE(ShaderFloat16Int8.shaderFloat16 != VK_FALSE, ShaderFloat16, "16-bit float shader operations are"); - ENABLE_FEATURE(ShaderFloat16Int8.shaderInt8 != VK_FALSE, ShaderInt8, "8-bit int shader operations are"); + ENABLE_FEATURE(ShaderFloat16Int8Feats.shaderFloat16 != VK_FALSE, ShaderFloat16, "16-bit float shader operations are"); + ENABLE_FEATURE(ShaderFloat16Int8Feats.shaderInt8 != VK_FALSE, ShaderInt8, "8-bit int shader operations are"); // clang-format on - auto Storage16BitFeats = DeviceExtFeatures.Storage16Bit; + const auto& Storage16BitFeats = DeviceExtFeatures.Storage16Bit; // clang-format off ENABLE_FEATURE(Storage16BitFeats.storageBuffer16BitAccess != VK_FALSE, ResourceBuffer16BitAccess, "16-bit resoure buffer access is"); ENABLE_FEATURE(Storage16BitFeats.uniformAndStorageBuffer16BitAccess != VK_FALSE, UniformBuffer16BitAccess, "16-bit uniform buffer access is"); ENABLE_FEATURE(Storage16BitFeats.storageInputOutput16 != VK_FALSE, ShaderInputOutput16, "16-bit shader inputs/outputs are"); // clang-format on - auto Storage8BitFeats = DeviceExtFeatures.Storage8Bit; + const auto& Storage8BitFeats = DeviceExtFeatures.Storage8Bit; // clang-format off ENABLE_FEATURE(Storage8BitFeats.storageBuffer8BitAccess != VK_FALSE, ResourceBuffer8BitAccess, "8-bit resoure buffer access is"); ENABLE_FEATURE(Storage8BitFeats.uniformAndStorageBuffer8BitAccess != VK_FALSE, UniformBuffer8BitAccess, "8-bit uniform buffer access is"); // clang-format on + + ENABLE_FEATURE(DeviceExtFeatures.AccelStruct.accelerationStructure != VK_FALSE && DeviceExtFeatures.RayTracingPipeline.rayTracingPipeline != VK_FALSE, RayTracing, "Ray tracing is"); #undef FeatureSupport // To enable some device extensions you must enable instance extension VK_KHR_get_physical_device_properties2 // and add feature description to DeviceCreateInfo.pNext. - const auto SupportsFeatures2 = Instance->IsExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + bool SupportsFeatures2 = Instance->IsExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + + // Enable extensions if (SupportsFeatures2) { void** NextExt = const_cast<void**>(&DeviceCreateInfo.pNext); + + // Mesh shader if (EngineCI.Features.MeshShaders != DEVICE_FEATURE_STATE_DISABLED) { - VERIFY_EXPR(MeshShaderFeats.taskShader != VK_FALSE && MeshShaderFeats.meshShader != VK_FALSE); + EnabledExtFeats.MeshShader = MeshShaderFeats; + VERIFY_EXPR(EnabledExtFeats.MeshShader.taskShader != VK_FALSE && EnabledExtFeats.MeshShader.meshShader != VK_FALSE); VERIFY(PhysicalDevice->IsExtensionSupported(VK_NV_MESH_SHADER_EXTENSION_NAME), "VK_NV_mesh_shader extension must be supported as it has already been checked by VulkanPhysicalDevice and " "both taskShader and meshShader features are TRUE"); DeviceExtensions.push_back(VK_NV_MESH_SHADER_EXTENSION_NAME); - *NextExt = &MeshShaderFeats; - NextExt = &MeshShaderFeats.pNext; + *NextExt = &EnabledExtFeats.MeshShader; + NextExt = &EnabledExtFeats.MeshShader.pNext; } if (EngineCI.Features.ShaderFloat16 != DEVICE_FEATURE_STATE_DISABLED || EngineCI.Features.ShaderInt8 != DEVICE_FEATURE_STATE_DISABLED) { - VERIFY_EXPR(ShaderFloat16Int8.shaderFloat16 != VK_FALSE || ShaderFloat16Int8.shaderInt8 != VK_FALSE); + EnabledExtFeats.ShaderFloat16Int8 = ShaderFloat16Int8Feats; + VERIFY_EXPR(EnabledExtFeats.ShaderFloat16Int8.shaderFloat16 != VK_FALSE || EnabledExtFeats.ShaderFloat16Int8.shaderInt8 != VK_FALSE); VERIFY(PhysicalDevice->IsExtensionSupported(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME), "VK_KHR_shader_float16_int8 extension must be supported as it has already been checked by VulkanPhysicalDevice " "and at least one of shaderFloat16 or shaderInt8 features is TRUE"); DeviceExtensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); if (EngineCI.Features.ShaderFloat16 == DEVICE_FEATURE_STATE_DISABLED) - ShaderFloat16Int8.shaderFloat16 = VK_FALSE; + EnabledExtFeats.ShaderFloat16Int8.shaderFloat16 = VK_FALSE; if (EngineCI.Features.ShaderInt8 == DEVICE_FEATURE_STATE_DISABLED) - ShaderFloat16Int8.shaderInt8 = VK_FALSE; + EnabledExtFeats.ShaderFloat16Int8.shaderInt8 = VK_FALSE; - *NextExt = &ShaderFloat16Int8; - NextExt = &ShaderFloat16Int8.pNext; + *NextExt = &EnabledExtFeats.ShaderFloat16Int8; + NextExt = &EnabledExtFeats.ShaderFloat16Int8.pNext; } bool StorageBufferStorageClassExtensionRequired = false; @@ -316,9 +332,10 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E // clang-format on { // clang-format off - VERIFY_EXPR(EngineCI.Features.ResourceBuffer16BitAccess == DEVICE_FEATURE_STATE_DISABLED || Storage16BitFeats.storageBuffer16BitAccess != VK_FALSE); - VERIFY_EXPR(EngineCI.Features.UniformBuffer16BitAccess == DEVICE_FEATURE_STATE_DISABLED || Storage16BitFeats.uniformAndStorageBuffer16BitAccess != VK_FALSE); - VERIFY_EXPR(EngineCI.Features.ShaderInputOutput16 == DEVICE_FEATURE_STATE_DISABLED || Storage16BitFeats.storageInputOutput16 != VK_FALSE); + EnabledExtFeats.Storage16Bit = Storage16BitFeats; + VERIFY_EXPR(EngineCI.Features.ResourceBuffer16BitAccess == DEVICE_FEATURE_STATE_DISABLED || EnabledExtFeats.Storage16Bit.storageBuffer16BitAccess != VK_FALSE); + VERIFY_EXPR(EngineCI.Features.UniformBuffer16BitAccess == DEVICE_FEATURE_STATE_DISABLED || EnabledExtFeats.Storage16Bit.uniformAndStorageBuffer16BitAccess != VK_FALSE); + VERIFY_EXPR(EngineCI.Features.ShaderInputOutput16 == DEVICE_FEATURE_STATE_DISABLED || EnabledExtFeats.Storage16Bit.storageInputOutput16 != VK_FALSE); // clang-format on VERIFY(PhysicalDevice->IsExtensionSupported(VK_KHR_16BIT_STORAGE_EXTENSION_NAME), @@ -335,14 +352,14 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E StorageBufferStorageClassExtensionRequired = true; if (EngineCI.Features.ResourceBuffer16BitAccess == DEVICE_FEATURE_STATE_DISABLED) - Storage16BitFeats.storageBuffer16BitAccess = VK_FALSE; + EnabledExtFeats.Storage16Bit.storageBuffer16BitAccess = VK_FALSE; if (EngineCI.Features.UniformBuffer16BitAccess == DEVICE_FEATURE_STATE_DISABLED) - Storage16BitFeats.uniformAndStorageBuffer16BitAccess = VK_FALSE; + EnabledExtFeats.Storage16Bit.uniformAndStorageBuffer16BitAccess = VK_FALSE; if (EngineCI.Features.ShaderInputOutput16 == DEVICE_FEATURE_STATE_DISABLED) - Storage16BitFeats.storageInputOutput16 = VK_FALSE; + EnabledExtFeats.Storage16Bit.storageInputOutput16 = VK_FALSE; - *NextExt = &Storage16BitFeats; - NextExt = &Storage16BitFeats.pNext; + *NextExt = &EnabledExtFeats.Storage16Bit; + NextExt = &EnabledExtFeats.Storage16Bit.pNext; } // clang-format off @@ -351,8 +368,9 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E // clang-format on { // clang-format off - VERIFY_EXPR(EngineCI.Features.ResourceBuffer8BitAccess == DEVICE_FEATURE_STATE_DISABLED || Storage8BitFeats.storageBuffer8BitAccess != VK_FALSE); - VERIFY_EXPR(EngineCI.Features.UniformBuffer8BitAccess == DEVICE_FEATURE_STATE_DISABLED || Storage8BitFeats.uniformAndStorageBuffer8BitAccess != VK_FALSE); + EnabledExtFeats.Storage8Bit = Storage8BitFeats; + VERIFY_EXPR(EngineCI.Features.ResourceBuffer8BitAccess == DEVICE_FEATURE_STATE_DISABLED || EnabledExtFeats.Storage8Bit.storageBuffer8BitAccess != VK_FALSE); + VERIFY_EXPR(EngineCI.Features.UniformBuffer8BitAccess == DEVICE_FEATURE_STATE_DISABLED || EnabledExtFeats.Storage8Bit.uniformAndStorageBuffer8BitAccess != VK_FALSE); // clang-format on VERIFY(PhysicalDevice->IsExtensionSupported(VK_KHR_8BIT_STORAGE_EXTENSION_NAME), @@ -369,12 +387,12 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E StorageBufferStorageClassExtensionRequired = true; if (EngineCI.Features.ResourceBuffer8BitAccess == DEVICE_FEATURE_STATE_DISABLED) - Storage8BitFeats.storageBuffer8BitAccess = VK_FALSE; + EnabledExtFeats.Storage8Bit.storageBuffer8BitAccess = VK_FALSE; if (EngineCI.Features.UniformBuffer8BitAccess == DEVICE_FEATURE_STATE_DISABLED) - Storage8BitFeats.uniformAndStorageBuffer8BitAccess = VK_FALSE; + EnabledExtFeats.Storage8Bit.uniformAndStorageBuffer8BitAccess = VK_FALSE; - *NextExt = &Storage8BitFeats; - NextExt = &Storage8BitFeats.pNext; + *NextExt = &EnabledExtFeats.Storage8Bit; + NextExt = &EnabledExtFeats.Storage8Bit.pNext; } if (StorageBufferStorageClassExtensionRequired) @@ -383,21 +401,65 @@ void EngineFactoryVkImpl::CreateDeviceAndContextsVk(const EngineVkCreateInfo& _E DeviceExtensions.push_back(VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME); } - *NextExt = nullptr; - } + // Ray tracing + if (EngineCI.Features.RayTracing != DEVICE_FEATURE_STATE_DISABLED) + { + // this extensions added to Vulkan 1.2 core + if (!DeviceExtFeatures.Spirv15) + { + DeviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); // required for VK_KHR_spirv_1_4 + DeviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); // required for VK_KHR_ray_tracing_pipeline + EnabledExtFeats.Spirv14 = DeviceExtFeatures.Spirv14; + VERIFY_EXPR(DeviceExtFeatures.Spirv14); + } + DeviceExtensions.push_back(VK_KHR_MAINTENANCE3_EXTENSION_NAME); // required for VK_EXT_descriptor_indexing + DeviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); // required for VK_KHR_acceleration_structure + DeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); // required for VK_KHR_acceleration_structure + DeviceExtensions.push_back(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); // required for VK_KHR_acceleration_structure + DeviceExtensions.push_back(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME); // required for ray tracing + DeviceExtensions.push_back(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME); // required for ray tracing + + EnabledExtFeats.AccelStruct = DeviceExtFeatures.AccelStruct; + EnabledExtFeats.RayTracingPipeline = DeviceExtFeatures.RayTracingPipeline; + EnabledExtFeats.BufferDeviceAddress = DeviceExtFeatures.BufferDeviceAddress; + EnabledExtFeats.DescriptorIndexing = DeviceExtFeatures.DescriptorIndexing; + + // disable unused features + EnabledExtFeats.AccelStruct.accelerationStructureCaptureReplay = false; + EnabledExtFeats.AccelStruct.accelerationStructureIndirectBuild = false; + EnabledExtFeats.AccelStruct.accelerationStructureHostCommands = false; + EnabledExtFeats.AccelStruct.descriptorBindingAccelerationStructureUpdateAfterBind = false; + + EnabledExtFeats.RayTracingPipeline.rayTracingPipelineShaderGroupHandleCaptureReplay = false; + EnabledExtFeats.RayTracingPipeline.rayTracingPipelineShaderGroupHandleCaptureReplayMixed = false; + EnabledExtFeats.RayTracingPipeline.rayTracingPipelineTraceRaysIndirect = false; + EnabledExtFeats.RayTracingPipeline.rayTraversalPrimitiveCulling = false; // for GLSL_EXT_ray_flags_primitive_culling + + *NextExt = &EnabledExtFeats.AccelStruct; + NextExt = &EnabledExtFeats.AccelStruct.pNext; + *NextExt = &EnabledExtFeats.RayTracingPipeline; + NextExt = &EnabledExtFeats.RayTracingPipeline.pNext; + *NextExt = &EnabledExtFeats.DescriptorIndexing; + NextExt = &EnabledExtFeats.DescriptorIndexing.pNext; + *NextExt = &EnabledExtFeats.BufferDeviceAddress; + NextExt = &EnabledExtFeats.BufferDeviceAddress.pNext; + } + + // make sure that last pNext is null + *NextExt = nullptr; + } #if defined(_MSC_VER) && defined(_WIN64) - static_assert(sizeof(DeviceFeatures) == 31, "Did you add a new feature to DeviceFeatures? Please handle its satus here."); + static_assert(sizeof(DeviceFeatures) == 32, "Did you add a new feature to DeviceFeatures? Please handle its satus here."); #endif DeviceCreateInfo.ppEnabledExtensionNames = DeviceExtensions.empty() ? nullptr : DeviceExtensions.data(); DeviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(DeviceExtensions.size()); - auto vkAllocator = Instance->GetVkAllocator(); - auto vkPhysicalDevice = PhysicalDevice->GetVkDeviceHandle(); - auto LogicalDevice = VulkanUtilities::VulkanLogicalDevice::Create(vkPhysicalDevice, DeviceCreateInfo, vkAllocator); + auto vkAllocator = Instance->GetVkAllocator(); + auto LogicalDevice = VulkanUtilities::VulkanLogicalDevice::Create(*PhysicalDevice, DeviceCreateInfo, EnabledExtFeats, vkAllocator); auto& RawMemAllocator = GetRawAllocator(); diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp index b221659a..3840b739 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp @@ -45,18 +45,19 @@ class ResourceTypeToVkDescriptorType public: ResourceTypeToVkDescriptorType() { - static_assert(SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes == 11, "Please add the corresponding decriptor type"); - m_Map[SPIRVShaderResourceAttribs::ResourceType::UniformBuffer] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - m_Map[SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; - m_Map[SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer] = 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; - m_Map[SPIRVShaderResourceAttribs::ResourceType::SampledImage] = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - m_Map[SPIRVShaderResourceAttribs::ResourceType::AtomicCounter] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - m_Map[SPIRVShaderResourceAttribs::ResourceType::SeparateImage] = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - m_Map[SPIRVShaderResourceAttribs::ResourceType::SeparateSampler] = VK_DESCRIPTOR_TYPE_SAMPLER; - m_Map[SPIRVShaderResourceAttribs::ResourceType::InputAttachment] = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + 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 @@ -65,13 +66,13 @@ public: } private: - std::array<VkDescriptorType, SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes> m_Map = {}; + std::array<VkDescriptorType, Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes}> m_Map = {}; }; -VkDescriptorType PipelineLayout::GetVkDescriptorType(const SPIRVShaderResourceAttribs& Res) +VkDescriptorType PipelineLayout::GetVkDescriptorType(SPIRVShaderResourceAttribs::ResourceType Type) { static const ResourceTypeToVkDescriptorType ResTypeToVkDescrType; - return ResTypeToVkDescrType[Res.Type]; + return ResTypeToVkDescrType[Type]; } PipelineLayout::DescriptorSetLayoutManager::DescriptorSetLayoutManager(IMemoryAllocator& MemAllocator) : @@ -331,7 +332,7 @@ void PipelineLayout::DescriptorSetLayoutManager::AllocateResourceSlot(const SPIR Binding = DescrSet.NumLayoutBindings; VkBinding.binding = Binding; - VkBinding.descriptorType = GetVkDescriptorType(ResAttribs); + 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); @@ -369,16 +370,13 @@ void PipelineLayout::AllocateResourceSlot(const SPIRVShaderResourceAttribs& ResA SHADER_TYPE ShaderType, Uint32& DescriptorSet, // Output parameter Uint32& Binding, // Output parameter - Uint32& OffsetInCache, - std::vector<uint32_t>& SPIRV) + 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); - SPIRV[ResAttribs.BindingDecorationOffset] = Binding; - SPIRV[ResAttribs.DescriptorSetDecorationOffset] = DescriptorSet; } void PipelineLayout::Finalize(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice) @@ -435,7 +433,7 @@ void PipelineLayout::InitResourceCache(RenderDeviceVkImpl* pDeviceVkImpl, } void PipelineLayout::PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkImpl, - bool IsCompute, + VkPipelineBindPoint BindPoint, const ShaderResourceCacheVk& ResourceCache, DescriptorSetBindInfo& BindInfo, VkDescriptorSet VkDynamicDescrSet) const @@ -481,7 +479,7 @@ void PipelineLayout::PrepareDescriptorSets(DeviceContextVkImpl* pCtxVkIm BindInfo.DynamicOffsetCount = TotalDynamicDescriptors; if (TotalDynamicDescriptors > BindInfo.DynamicOffsets.size()) BindInfo.DynamicOffsets.resize(TotalDynamicDescriptors); - BindInfo.BindPoint = IsCompute ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS; + BindInfo.BindPoint = BindPoint; BindInfo.pResourceCache = &ResourceCache; #ifdef DILIGENT_DEBUG BindInfo.pDbgPipelineLayout = this; diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 2325f9cb..7d846b90 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -44,98 +44,24 @@ namespace Diligent { - -RenderPassDesc PipelineStateVkImpl::GetImplicitRenderPassDesc( - Uint32 NumRenderTargets, - const TEXTURE_FORMAT RTVFormats[], - TEXTURE_FORMAT DSVFormat, - Uint8 SampleCount, - std::array<RenderPassAttachmentDesc, MAX_RENDER_TARGETS + 1>& Attachments, - std::array<AttachmentReference, MAX_RENDER_TARGETS + 1>& AttachmentReferences, - SubpassDesc& SubpassDesc) +namespace { - VERIFY_EXPR(NumRenderTargets <= MAX_RENDER_TARGETS); - - RenderPassDesc RPDesc; - - RPDesc.AttachmentCount = (DSVFormat != TEX_FORMAT_UNKNOWN ? 1 : 0) + NumRenderTargets; - - uint32_t AttachmentInd = 0; - AttachmentReference* pDepthAttachmentReference = nullptr; - if (DSVFormat != TEX_FORMAT_UNKNOWN) - { - auto& DepthAttachment = Attachments[AttachmentInd]; - - DepthAttachment.Format = DSVFormat; - DepthAttachment.SampleCount = SampleCount; - DepthAttachment.LoadOp = ATTACHMENT_LOAD_OP_LOAD; // previous contents of the image within the render area - // will be preserved. For attachments with a depth/stencil format, - // this uses the access type VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT. - DepthAttachment.StoreOp = ATTACHMENT_STORE_OP_STORE; // the contents generated during the render pass and within the render - // area are written to memory. For attachments with a depth/stencil format, - // this uses the access type VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT. - DepthAttachment.StencilLoadOp = ATTACHMENT_LOAD_OP_LOAD; - DepthAttachment.StencilStoreOp = ATTACHMENT_STORE_OP_STORE; - DepthAttachment.InitialState = RESOURCE_STATE_DEPTH_WRITE; - DepthAttachment.FinalState = RESOURCE_STATE_DEPTH_WRITE; - - pDepthAttachmentReference = &AttachmentReferences[AttachmentInd]; - pDepthAttachmentReference->AttachmentIndex = AttachmentInd; - pDepthAttachmentReference->State = RESOURCE_STATE_DEPTH_WRITE; - - ++AttachmentInd; - } - - AttachmentReference* pColorAttachmentsReference = NumRenderTargets > 0 ? &AttachmentReferences[AttachmentInd] : nullptr; - for (Uint32 rt = 0; rt < NumRenderTargets; ++rt, ++AttachmentInd) - { - auto& ColorAttachment = Attachments[AttachmentInd]; - - ColorAttachment.Format = RTVFormats[rt]; - ColorAttachment.SampleCount = SampleCount; - ColorAttachment.LoadOp = ATTACHMENT_LOAD_OP_LOAD; // previous contents of the image within the render area - // will be preserved. For attachments with a depth/stencil format, - // this uses the access type VK_ACCESS_COLOR_ATTACHMENT_READ_BIT. - ColorAttachment.StoreOp = ATTACHMENT_STORE_OP_STORE; // the contents generated during the render pass and within the render - // area are written to memory. For attachments with a color format, - // this uses the access type VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT. - ColorAttachment.StencilLoadOp = ATTACHMENT_LOAD_OP_DISCARD; - ColorAttachment.StencilStoreOp = ATTACHMENT_STORE_OP_DISCARD; - ColorAttachment.InitialState = RESOURCE_STATE_RENDER_TARGET; - ColorAttachment.FinalState = RESOURCE_STATE_RENDER_TARGET; - - auto& ColorAttachmentRef = AttachmentReferences[AttachmentInd]; - ColorAttachmentRef.AttachmentIndex = AttachmentInd; - ColorAttachmentRef.State = RESOURCE_STATE_RENDER_TARGET; - } - - RPDesc.pAttachments = Attachments.data(); - RPDesc.SubpassCount = 1; - RPDesc.pSubpasses = &SubpassDesc; - RPDesc.DependencyCount = 0; // the number of dependencies between pairs of subpasses, or zero indicating no dependencies. - RPDesc.pDependencies = nullptr; // an array of dependencyCount number of VkSubpassDependency structures describing - // dependencies between pairs of subpasses, or NULL if dependencyCount is zero. - - - SubpassDesc.InputAttachmentCount = 0; - SubpassDesc.pInputAttachments = nullptr; - SubpassDesc.RenderTargetAttachmentCount = NumRenderTargets; - SubpassDesc.pRenderTargetAttachments = pColorAttachmentsReference; - SubpassDesc.pResolveAttachments = nullptr; - SubpassDesc.pDepthStencilAttachment = pDepthAttachmentReference; - SubpassDesc.PreserveAttachmentCount = 0; - SubpassDesc.pPreserveAttachments = nullptr; - - return RPDesc; -} -static bool StripReflection(std::vector<uint32_t>& SPIRV) +bool StripReflection(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice, std::vector<uint32_t>& SPIRV) { #if DILIGENT_NO_HLSL - return false; + return true; #else std::vector<uint32_t> StrippedSPIRV; - spvtools::Optimizer SpirvOptimizer(SPV_ENV_VULKAN_1_0); + spv_target_env Target = SPV_ENV_VULKAN_1_0; + const auto& ExtFeats = LogicalDevice.GetEnabledExtFeatures(); + + if (ExtFeats.Spirv15) + Target = SPV_ENV_VULKAN_1_2; + else if (ExtFeats.Spirv14) + Target = SPV_ENV_VULKAN_1_1_SPIRV_1_4; + + spvtools::Optimizer SpirvOptimizer(Target); // Decorations defined in SPV_GOOGLE_hlsl_functionality1 are the only instructions // removed by strip-reflect-info pass. SPIRV offsets become INVALID after this operation. SpirvOptimizer.RegisterPass(spvtools::CreateStripReflectInfoPass()); @@ -149,21 +75,25 @@ static bool StripReflection(std::vector<uint32_t>& SPIRV) #endif } -static void InitPipelineShaderStages(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice, - ShaderResourceLayoutVk::TShaderStages& ShaderStages, - std::vector<VulkanUtilities::ShaderModuleWrapper>& vkShaderModules, - std::vector<VkPipelineShaderStageCreateInfo>& vkPipelineShaderStages) +void InitPipelineShaderStages(const VulkanUtilities::VulkanLogicalDevice& LogicalDevice, + ShaderResourceLayoutVk::TShaderStages& ShaderStages, + std::vector<VulkanUtilities::ShaderModuleWrapper>& ShaderModules, + std::vector<VkPipelineShaderStageCreateInfo>& Stages) { for (size_t s = 0; s < ShaderStages.size(); ++s) { - auto& StageInfo = ShaderStages[s]; + const auto& Shaders = ShaderStages[s].Shaders; + auto& SPIRVs = ShaderStages[s].SPIRVs; + const auto ShaderType = ShaderStages[s].Type; + + VERIFY_EXPR(Shaders.size() == SPIRVs.size()); VkPipelineShaderStageCreateInfo StageCI = {}; StageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; StageCI.pNext = nullptr; StageCI.flags = 0; // reserved for future use - StageCI.stage = ShaderTypeToVkShaderStageFlagBit(StageInfo.Type); + StageCI.stage = ShaderTypeToVkShaderStageFlagBit(ShaderType); VkShaderModuleCreateInfo ShaderModuleCI = {}; @@ -171,25 +101,31 @@ static void InitPipelineShaderStages(const VulkanUtilities::VulkanLogicalDevice& ShaderModuleCI.pNext = nullptr; ShaderModuleCI.flags = 0; - // We have to strip reflection instructions to fix the follownig validation error: - // SPIR-V module not valid: DecorateStringGOOGLE requires one of the following extensions: SPV_GOOGLE_decorate_string - // Optimizer also performs validation and may catch problems with the byte code. - if (!StripReflection(StageInfo.SPIRV)) - LOG_ERROR("Failed to strip reflection information from shader '", StageInfo.pShader->GetDesc().Name, "'. This may indicate a problem with the byte code."); + for (size_t i = 0; i < Shaders.size(); ++i) + { + auto* pShader = Shaders[i]; + auto& SPIRV = SPIRVs[i]; + + // We have to strip reflection instructions to fix the follownig validation error: + // SPIR-V module not valid: DecorateStringGOOGLE requires one of the following extensions: SPV_GOOGLE_decorate_string + // Optimizer also performs validation and may catch problems with the byte code. + if (!StripReflection(LogicalDevice, SPIRV)) + LOG_ERROR("Failed to strip reflection information from shader '", pShader->GetDesc().Name, "'. This may indicate a problem with the byte code."); - ShaderModuleCI.codeSize = StageInfo.SPIRV.size() * sizeof(uint32_t); - ShaderModuleCI.pCode = StageInfo.SPIRV.data(); + ShaderModuleCI.codeSize = SPIRV.size() * sizeof(uint32_t); + ShaderModuleCI.pCode = SPIRV.data(); - vkShaderModules.push_back(LogicalDevice.CreateShaderModule(ShaderModuleCI, StageInfo.pShader->GetDesc().Name)); + ShaderModules.push_back(LogicalDevice.CreateShaderModule(ShaderModuleCI, pShader->GetDesc().Name)); - StageCI.module = vkShaderModules.back(); - StageCI.pName = StageInfo.pShader->GetEntryPoint(); - StageCI.pSpecializationInfo = nullptr; + StageCI.module = ShaderModules.back(); + StageCI.pName = pShader->GetEntryPoint(); + StageCI.pSpecializationInfo = nullptr; - vkPipelineShaderStages.push_back(StageCI); + Stages.push_back(StageCI); + } } - VERIFY_EXPR(vkShaderModules.size() == vkPipelineShaderStages.size()); + VERIFY_EXPR(ShaderModules.size() == Stages.size()); } @@ -401,6 +337,253 @@ static void CreateGraphicsPipeline(RenderDeviceVkImpl* Pipeline = LogicalDevice.CreateGraphicsPipeline(PipelineCI, VK_NULL_HANDLE, PSODesc.Name); } + +static void CreateRayTracingPipeline(RenderDeviceVkImpl* pDeviceVk, + std::vector<VkPipelineShaderStageCreateInfo>& Stages, + const std::vector<VkRayTracingShaderGroupCreateInfoKHR>& ShaderGroups, + const PipelineLayout& Layout, + const PipelineStateDesc& PSODesc, + const RayTracingPipelineDesc& RayTracingPipeline, + VulkanUtilities::PipelineWrapper& Pipeline) +{ + const auto& LogicalDevice = pDeviceVk->GetLogicalDevice(); + + VkRayTracingPipelineCreateInfoKHR PipelineCI = {}; + + PipelineCI.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR; + PipelineCI.pNext = nullptr; +#ifdef DILIGENT_DEBUG + PipelineCI.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT; +#endif + + PipelineCI.stageCount = static_cast<Uint32>(Stages.size()); + PipelineCI.pStages = Stages.data(); + PipelineCI.groupCount = static_cast<Uint32>(ShaderGroups.size()); + PipelineCI.pGroups = ShaderGroups.data(); + PipelineCI.maxPipelineRayRecursionDepth = std::max(1u, Uint32{RayTracingPipeline.MaxRecursionDepth}) - 1; // for compatibility with D3D12, zero means only one ray tracing depth. + PipelineCI.pLibraryInfo = nullptr; + PipelineCI.pLibraryInterface = nullptr; + PipelineCI.pDynamicState = nullptr; + PipelineCI.layout = Layout.GetVkPipelineLayout(); + PipelineCI.basePipelineHandle = VK_NULL_HANDLE; // a pipeline to derive from + PipelineCI.basePipelineIndex = -1; // an index into the pCreateInfos parameter to use as a pipeline to derive from + + Pipeline = LogicalDevice.CreateRayTracingPipeline(PipelineCI, VK_NULL_HANDLE, PSODesc.Name); +} + + +template <typename TNameToGroupIndexMap> +void BuildRTPipelineDescription(const RayTracingPipelineStateCreateInfo& CreateInfo, + const TNameToGroupIndexMap& NameToGroupIndex, + std::vector<VkRayTracingShaderGroupCreateInfoKHR>& ShaderGroups, + const ShaderResourceLayoutVk::TShaderStages& ShaderStages) +{ +#define LOG_PSO_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of ray tracing PSO '", CreateInfo.PSODesc.Name, "' is invalid: ", ##__VA_ARGS__) + ShaderGroups.reserve(CreateInfo.GeneralShaderCount + CreateInfo.TriangleHitShaderCount + CreateInfo.ProceduralHitShaderCount); + + std::array<Uint32, MAX_SHADERS_IN_PIPELINE> ShaderIndices = {}; + std::unordered_map<const IShader*, Uint32> UniqueShaders; + + const auto ShaderToIndex = [&ShaderIndices, &UniqueShaders](const IShader* pShader) -> Uint32 { + if (pShader != nullptr) + { + Uint32& Index = ShaderIndices[GetShaderTypePipelineIndex(pShader->GetDesc().ShaderType, PIPELINE_TYPE_RAY_TRACING)]; + auto Result = UniqueShaders.emplace(pShader, Index); + if (Result.second) + { + ++Index; + } + return Result.first->second; + } + return VK_SHADER_UNUSED_KHR; + }; + + Uint32 ShaderCount = 0; + for (auto& Stage : ShaderStages) + { + ShaderIndices[GetShaderTypePipelineIndex(Stage.Type, PIPELINE_TYPE_RAY_TRACING)] = ShaderCount; + ShaderCount += static_cast<Uint32>(Stage.Count()); + } + + for (Uint32 i = 0; i < CreateInfo.GeneralShaderCount; ++i) + { + const auto& GeneralShader = CreateInfo.pGeneralShaders[i]; + + VkRayTracingShaderGroupCreateInfoKHR Group = {}; + + Group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + Group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; + Group.generalShader = ShaderToIndex(GeneralShader.pShader); + Group.closestHitShader = VK_SHADER_UNUSED_KHR; + Group.anyHitShader = VK_SHADER_UNUSED_KHR; + Group.intersectionShader = VK_SHADER_UNUSED_KHR; + +#ifdef DILIGENT_DEVELOPMENT + auto Iter = NameToGroupIndex.find(GeneralShader.Name); + if (Iter == NameToGroupIndex.end()) + LOG_PSO_ERROR_AND_THROW("Can't find general shader '", GeneralShader.Name, "'"); + if (Iter->second != ShaderGroups.size()) + LOG_PSO_ERROR_AND_THROW("General shader group '", GeneralShader.Name, "' index mismatch: (", Iter->second, ") != (", ShaderGroups.size(), ")"); +#endif + + ShaderGroups.push_back(Group); + } + + for (Uint32 i = 0; i < CreateInfo.TriangleHitShaderCount; ++i) + { + const auto& TriHitShader = CreateInfo.pTriangleHitShaders[i]; + + VkRayTracingShaderGroupCreateInfoKHR Group = {}; + + Group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + Group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR; + Group.generalShader = VK_SHADER_UNUSED_KHR; + Group.closestHitShader = ShaderToIndex(TriHitShader.pClosestHitShader); + Group.anyHitShader = ShaderToIndex(TriHitShader.pAnyHitShader); + Group.intersectionShader = VK_SHADER_UNUSED_KHR; + +#ifdef DILIGENT_DEVELOPMENT + auto Iter = NameToGroupIndex.find(TriHitShader.Name); + if (Iter == NameToGroupIndex.end()) + LOG_PSO_ERROR_AND_THROW("Can't find triangle hit group '", TriHitShader.Name, "'"); + if (Iter->second != ShaderGroups.size()) + LOG_PSO_ERROR_AND_THROW("Triangle hit group '", TriHitShader.Name, "' index mismatch: (", Iter->second, ") != (", ShaderGroups.size(), ")"); +#endif + + ShaderGroups.push_back(Group); + } + + for (Uint32 i = 0; i < CreateInfo.ProceduralHitShaderCount; ++i) + { + const auto& ProcHitShader = CreateInfo.pProceduralHitShaders[i]; + + VkRayTracingShaderGroupCreateInfoKHR Group = {}; + + Group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + Group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR; + Group.generalShader = VK_SHADER_UNUSED_KHR; + Group.intersectionShader = ShaderToIndex(ProcHitShader.pIntersectionShader); + Group.closestHitShader = ShaderToIndex(ProcHitShader.pClosestHitShader); + Group.anyHitShader = ShaderToIndex(ProcHitShader.pAnyHitShader); + +#ifdef DILIGENT_DEVELOPMENT + auto Iter = NameToGroupIndex.find(ProcHitShader.Name); + if (Iter == NameToGroupIndex.end()) + LOG_PSO_ERROR_AND_THROW("Can't find procedural hit group '", ProcHitShader.Name, "'"); + if (Iter->second != ShaderGroups.size()) + LOG_PSO_ERROR_AND_THROW("Procedural hit group '", ProcHitShader.Name, "' index mismatch: (", Iter->second, ") != (", ShaderGroups.size(), ")"); +#endif + + ShaderGroups.push_back(Group); + } + +#ifdef DILIGENT_DEVELOPMENT + Uint32 ShaderIndex2 = 0; + for (auto& Stage : ShaderStages) + { + for (auto* pShader : Stage.Shaders) + { + auto iter = UniqueShaders.find(static_cast<const IShader*>(pShader)); + if (iter != UniqueShaders.end()) + VERIFY_EXPR(iter->second == ShaderIndex2); + else + UNEXPECTED("Shader '", pShader->GetDesc().Name, "' is not used in ray tracing shader groups"); + + ++ShaderIndex2; + } + } + VERIFY_EXPR(UniqueShaders.size() == ShaderIndex2); +#endif +#undef LOG_PSO_ERROR_AND_THROW +} + +} // namespace + + +RenderPassDesc PipelineStateVkImpl::GetImplicitRenderPassDesc( + Uint32 NumRenderTargets, + const TEXTURE_FORMAT RTVFormats[], + TEXTURE_FORMAT DSVFormat, + Uint8 SampleCount, + std::array<RenderPassAttachmentDesc, MAX_RENDER_TARGETS + 1>& Attachments, + std::array<AttachmentReference, MAX_RENDER_TARGETS + 1>& AttachmentReferences, + SubpassDesc& SubpassDesc) +{ + VERIFY_EXPR(NumRenderTargets <= MAX_RENDER_TARGETS); + + RenderPassDesc RPDesc; + + RPDesc.AttachmentCount = (DSVFormat != TEX_FORMAT_UNKNOWN ? 1 : 0) + NumRenderTargets; + + uint32_t AttachmentInd = 0; + AttachmentReference* pDepthAttachmentReference = nullptr; + if (DSVFormat != TEX_FORMAT_UNKNOWN) + { + auto& DepthAttachment = Attachments[AttachmentInd]; + + DepthAttachment.Format = DSVFormat; + DepthAttachment.SampleCount = SampleCount; + DepthAttachment.LoadOp = ATTACHMENT_LOAD_OP_LOAD; // previous contents of the image within the render area + // will be preserved. For attachments with a depth/stencil format, + // this uses the access type VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT. + DepthAttachment.StoreOp = ATTACHMENT_STORE_OP_STORE; // the contents generated during the render pass and within the render + // area are written to memory. For attachments with a depth/stencil format, + // this uses the access type VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT. + DepthAttachment.StencilLoadOp = ATTACHMENT_LOAD_OP_LOAD; + DepthAttachment.StencilStoreOp = ATTACHMENT_STORE_OP_STORE; + DepthAttachment.InitialState = RESOURCE_STATE_DEPTH_WRITE; + DepthAttachment.FinalState = RESOURCE_STATE_DEPTH_WRITE; + + pDepthAttachmentReference = &AttachmentReferences[AttachmentInd]; + pDepthAttachmentReference->AttachmentIndex = AttachmentInd; + pDepthAttachmentReference->State = RESOURCE_STATE_DEPTH_WRITE; + + ++AttachmentInd; + } + + AttachmentReference* pColorAttachmentsReference = NumRenderTargets > 0 ? &AttachmentReferences[AttachmentInd] : nullptr; + for (Uint32 rt = 0; rt < NumRenderTargets; ++rt, ++AttachmentInd) + { + auto& ColorAttachment = Attachments[AttachmentInd]; + + ColorAttachment.Format = RTVFormats[rt]; + ColorAttachment.SampleCount = SampleCount; + ColorAttachment.LoadOp = ATTACHMENT_LOAD_OP_LOAD; // previous contents of the image within the render area + // will be preserved. For attachments with a depth/stencil format, + // this uses the access type VK_ACCESS_COLOR_ATTACHMENT_READ_BIT. + ColorAttachment.StoreOp = ATTACHMENT_STORE_OP_STORE; // the contents generated during the render pass and within the render + // area are written to memory. For attachments with a color format, + // this uses the access type VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT. + ColorAttachment.StencilLoadOp = ATTACHMENT_LOAD_OP_DISCARD; + ColorAttachment.StencilStoreOp = ATTACHMENT_STORE_OP_DISCARD; + ColorAttachment.InitialState = RESOURCE_STATE_RENDER_TARGET; + ColorAttachment.FinalState = RESOURCE_STATE_RENDER_TARGET; + + auto& ColorAttachmentRef = AttachmentReferences[AttachmentInd]; + ColorAttachmentRef.AttachmentIndex = AttachmentInd; + ColorAttachmentRef.State = RESOURCE_STATE_RENDER_TARGET; + } + + RPDesc.pAttachments = Attachments.data(); + RPDesc.SubpassCount = 1; + RPDesc.pSubpasses = &SubpassDesc; + RPDesc.DependencyCount = 0; // the number of dependencies between pairs of subpasses, or zero indicating no dependencies. + RPDesc.pDependencies = nullptr; // an array of dependencyCount number of VkSubpassDependency structures describing + // dependencies between pairs of subpasses, or NULL if dependencyCount is zero. + + + SubpassDesc.InputAttachmentCount = 0; + SubpassDesc.pInputAttachments = nullptr; + SubpassDesc.RenderTargetAttachmentCount = NumRenderTargets; + SubpassDesc.pRenderTargetAttachments = pColorAttachmentsReference; + SubpassDesc.pResolveAttachments = nullptr; + SubpassDesc.pDepthStencilAttachment = pDepthAttachmentReference; + SubpassDesc.PreserveAttachmentCount = 0; + SubpassDesc.pPreserveAttachments = nullptr; + + return RPDesc; +} + void PipelineStateVkImpl::InitResourceLayouts(const PipelineStateCreateInfo& CreateInfo, TShaderStages& ShaderStages) { @@ -416,7 +599,7 @@ void PipelineStateVkImpl::InitResourceLayouts(const PipelineStateCreateInfo& Cre m_ResourceLayoutIndex[ShaderTypeInd] = static_cast<Int8>(s); auto& StaticResLayout = m_ShaderResourceLayouts[GetNumShaderStages() + s]; - StaticResLayout.InitializeStaticResourceLayout(StageInfo.pShader, GetRawAllocator(), m_Desc.ResourceLayout, m_StaticResCaches[s]); + StaticResLayout.InitializeStaticResourceLayout(StageInfo.Shaders, GetRawAllocator(), m_Desc.ResourceLayout, m_StaticResCaches[s]); m_StaticVarsMgrs[s].Initialize(StaticResLayout, GetRawAllocator(), nullptr, 0); } @@ -460,17 +643,18 @@ void PipelineStateVkImpl::InitResourceLayouts(const PipelineStateCreateInfo& Cre m_ShaderResourceLayoutHash = m_PipelineLayout.GetHash(); } -template <typename PSOCreateInfoType> +template <typename PSOCreateInfoType, typename InitPSODescType> void PipelineStateVkImpl::InitInternalObjects(const PSOCreateInfoType& CreateInfo, std::vector<VkPipelineShaderStageCreateInfo>& vkShaderStages, - std::vector<VulkanUtilities::ShaderModuleWrapper>& ShaderModules) + std::vector<VulkanUtilities::ShaderModuleWrapper>& ShaderModules, + InitPSODescType InitPSODesc) { m_ResourceLayoutIndex.fill(-1); TShaderStages ShaderStages; ExtractShaders<ShaderVkImpl>(CreateInfo, ShaderStages); - LinearAllocator MemPool{GetRawAllocator()}; + FixedLinearAllocator MemPool{GetRawAllocator()}; const auto NumShaderStages = GetNumShaderStages(); VERIFY_EXPR(NumShaderStages > 0 && NumShaderStages == ShaderStages.size()); @@ -498,7 +682,7 @@ void PipelineStateVkImpl::InitInternalObjects(const PSOCreateInfoType& for (Uint32 s = 0; s < NumShaderStages; ++s) new (m_StaticVarsMgrs + s) ShaderVariableManagerVk{*this, m_StaticResCaches[s]}; - InitializePipelineDesc(CreateInfo, MemPool); + InitPSODesc(CreateInfo, MemPool, ShaderStages); // It is important to construct all objects before initializing them because if an exception is thrown, // destructors will be called for all objects @@ -509,6 +693,7 @@ void PipelineStateVkImpl::InitInternalObjects(const PSOCreateInfoType& InitPipelineShaderStages(GetDevice()->GetLogicalDevice(), ShaderStages, ShaderModules, vkShaderStages); } + PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pDeviceVk, const GraphicsPipelineStateCreateInfo& CreateInfo) : @@ -520,7 +705,12 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* std::vector<VkPipelineShaderStageCreateInfo> vkShaderStages; std::vector<VulkanUtilities::ShaderModuleWrapper> ShaderModules; - InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules); + InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules, + [this](const GraphicsPipelineStateCreateInfo& CreateInfo, FixedLinearAllocator& MemPool, TShaderStages /*ShaderStages*/) // + { + InitializePipelineDesc(CreateInfo, MemPool); + } // + ); CreateGraphicsPipeline(pDeviceVk, vkShaderStages, m_PipelineLayout, m_Desc, GetGraphicsPipelineDesc(), m_Pipeline, m_pRenderPass); } @@ -543,7 +733,12 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* p std::vector<VkPipelineShaderStageCreateInfo> vkShaderStages; std::vector<VulkanUtilities::ShaderModuleWrapper> ShaderModules; - InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules); + InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules, + [this](const ComputePipelineStateCreateInfo& CreateInfo, FixedLinearAllocator& MemPool, TShaderStages /*ShaderStages*/) // + { + InitializePipelineDesc(CreateInfo, MemPool); + } // + ); CreateComputePipeline(pDeviceVk, vkShaderStages, m_PipelineLayout, m_Desc, m_Pipeline); } @@ -554,6 +749,41 @@ PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* p } } +PipelineStateVkImpl::PipelineStateVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pDeviceVk, + const RayTracingPipelineStateCreateInfo& CreateInfo) : + TPipelineStateBase{pRefCounters, pDeviceVk, CreateInfo}, + m_SRBMemAllocator{GetRawAllocator()} +{ + try + { + const auto& LogicalDevice = pDeviceVk->GetLogicalDevice(); + const auto ShaderGroupHandleSize = pDeviceVk->GetProperties().ShaderGroupHandleSize; + + std::vector<VkPipelineShaderStageCreateInfo> vkShaderStages; + std::vector<VulkanUtilities::ShaderModuleWrapper> ShaderModules; + std::vector<VkRayTracingShaderGroupCreateInfoKHR> ShaderGroups; + + InitInternalObjects(CreateInfo, vkShaderStages, ShaderModules, + [&](const RayTracingPipelineStateCreateInfo& CreateInfo, FixedLinearAllocator& MemPool, TShaderStages& ShaderStages) // + { + InitializePipelineDesc(CreateInfo, MemPool); + BuildRTPipelineDescription(CreateInfo, m_pRayTracingPipelineData->NameToGroupIndex, ShaderGroups, ShaderStages); + } // + ); + + CreateRayTracingPipeline(pDeviceVk, vkShaderStages, ShaderGroups, m_PipelineLayout, m_Desc, GetRayTracingPipelineDesc(), m_Pipeline); + + auto err = LogicalDevice.GetRayTracingShaderGroupHandles(m_Pipeline, 0, static_cast<uint32_t>(ShaderGroups.size()), ShaderGroupHandleSize, &m_pRayTracingPipelineData->Shaders[0]); + VERIFY(err == VK_SUCCESS, "Failed to get shader group handles"); + (void)err; + } + catch (...) + { + Destruct(); + throw; + } +} PipelineStateVkImpl::~PipelineStateVkImpl() { @@ -562,6 +792,8 @@ PipelineStateVkImpl::~PipelineStateVkImpl() void PipelineStateVkImpl::Destruct() { + TPipelineStateBase::Destruct(); + m_pDevice->SafeReleaseDeviceObject(std::move(m_Pipeline), m_Desc.CommandQueueMask); m_PipelineLayout.Release(m_pDevice, m_Desc.CommandQueueMask); @@ -617,7 +849,6 @@ bool PipelineStateVkImpl::IsCompatibleWith(const IPipelineState* pPSO) const return false; auto IsSamePipelineLayout = m_PipelineLayout.IsSameAs(pPSOVk->m_PipelineLayout); - #ifdef DILIGENT_DEBUG { bool IsCompatibleShaders = true; @@ -634,8 +865,8 @@ bool PipelineStateVkImpl::IsCompatibleWith(const IPipelineState* pPSO) const break; } - const auto& Res0 = GetShaderResLayout(s).GetResources(); - const auto& Res1 = pPSOVk->GetShaderResLayout(s).GetResources(); + const auto& Res0 = GetShaderResLayout(s); + const auto& Res1 = pPSOVk->GetShaderResLayout(s); if (!Res0.IsCompatibleWith(Res1)) { IsCompatibleShaders = false; @@ -736,9 +967,23 @@ void PipelineStateVkImpl::CommitAndTransitionShaderResources(IShaderResourceBind Layout.CommitDynamicResources(ResourceCache, DynamicDescrSet); } } - // Prepare descriptor sets, and also bind them if there are no dynamic descriptors + + + 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); - m_PipelineLayout.PrepareDescriptorSets(pCtxVkImpl, m_Desc.IsComputePipeline(), ResourceCache, *pDescrSetBindInfo, DynamicDescrSet); + // 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(). } diff --git a/Graphics/GraphicsEngineVulkan/src/QueryManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/QueryManagerVk.cpp index fb09687b..ec94814e 100644 --- a/Graphics/GraphicsEngineVulkan/src/QueryManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/QueryManagerVk.cpp @@ -97,7 +97,7 @@ QueryManagerVk::QueryManagerVk(RenderDeviceVkImpl* pRenderDeviceVk, VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT | VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT; - const auto EnabledShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages(); + const auto EnabledShaderStages = LogicalDevice.GetEnabledShaderStages(); if (EnabledShaderStages & VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT) { QueryPoolCI.pipelineStatistics |= diff --git a/Graphics/GraphicsEngineVulkan/src/QueryVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/QueryVkImpl.cpp index 381ee631..a0801cb7 100644 --- a/Graphics/GraphicsEngineVulkan/src/QueryVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/QueryVkImpl.cpp @@ -225,7 +225,7 @@ bool QueryVkImpl::GetData(void* pData, Uint32 DataSize, bool AutoInvalidate) { auto& QueryData = *reinterpret_cast<QueryDataPipelineStatistics*>(pData); - const auto EnabledShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages(); + const auto EnabledShaderStages = LogicalDevice.GetEnabledShaderStages(); auto Idx = 0; diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp index ccfb74ac..44bc0eb8 100644 --- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp @@ -39,6 +39,9 @@ #include "QueryVkImpl.hpp" #include "RenderPassVkImpl.hpp" #include "FramebufferVkImpl.hpp" +#include "BottomLevelASVkImpl.hpp" +#include "TopLevelASVkImpl.hpp" +#include "ShaderBindingTableVkImpl.hpp" #include "EngineMemory.h" namespace Diligent @@ -75,7 +78,10 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* sizeof(FenceVkImpl), sizeof(QueryVkImpl), sizeof(RenderPassVkImpl), - sizeof(FramebufferVkImpl) + sizeof(FramebufferVkImpl), + sizeof(BottomLevelASVkImpl), + sizeof(TopLevelASVkImpl), + sizeof(ShaderBindingTableVkImpl), } }, m_VulkanInstance {Instance }, @@ -90,17 +96,18 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* "Main descriptor pool", std::vector<VkDescriptorPoolSize> { - {VK_DESCRIPTOR_TYPE_SAMPLER, EngineCI.MainDescriptorPoolSize.NumSeparateSamplerDescriptors}, - {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, EngineCI.MainDescriptorPoolSize.NumCombinedSamplerDescriptors}, - {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, EngineCI.MainDescriptorPoolSize.NumSampledImageDescriptors}, - {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, EngineCI.MainDescriptorPoolSize.NumStorageImageDescriptors}, - {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, EngineCI.MainDescriptorPoolSize.NumUniformTexelBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, EngineCI.MainDescriptorPoolSize.NumStorageTexelBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.MainDescriptorPoolSize.NumUniformBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.MainDescriptorPoolSize.NumStorageBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, EngineCI.MainDescriptorPoolSize.NumUniformBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, EngineCI.MainDescriptorPoolSize.NumStorageBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, EngineCI.MainDescriptorPoolSize.NumInputAttachmentDescriptors}, + {VK_DESCRIPTOR_TYPE_SAMPLER, EngineCI.MainDescriptorPoolSize.NumSeparateSamplerDescriptors}, + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, EngineCI.MainDescriptorPoolSize.NumCombinedSamplerDescriptors}, + {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, EngineCI.MainDescriptorPoolSize.NumSampledImageDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, EngineCI.MainDescriptorPoolSize.NumStorageImageDescriptors}, + {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, EngineCI.MainDescriptorPoolSize.NumUniformTexelBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, EngineCI.MainDescriptorPoolSize.NumStorageTexelBufferDescriptors}, + //{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.MainDescriptorPoolSize.NumUniformBufferDescriptors}, + //{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.MainDescriptorPoolSize.NumStorageBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, EngineCI.MainDescriptorPoolSize.NumUniformBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, EngineCI.MainDescriptorPoolSize.NumStorageBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, EngineCI.MainDescriptorPoolSize.NumInputAttachmentDescriptors}, + {VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, EngineCI.MainDescriptorPoolSize.NumAccelStructDescriptors} }, EngineCI.MainDescriptorPoolSize.MaxDescriptorSets, true @@ -111,17 +118,18 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* "Dynamic descriptor pool", std::vector<VkDescriptorPoolSize> { - {VK_DESCRIPTOR_TYPE_SAMPLER, EngineCI.DynamicDescriptorPoolSize.NumSeparateSamplerDescriptors}, - {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, EngineCI.DynamicDescriptorPoolSize.NumCombinedSamplerDescriptors}, - {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, EngineCI.DynamicDescriptorPoolSize.NumSampledImageDescriptors}, - {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, EngineCI.DynamicDescriptorPoolSize.NumStorageImageDescriptors}, - {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumUniformTexelBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumStorageTexelBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumUniformBufferDescriptors}, - //{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumStorageBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, EngineCI.DynamicDescriptorPoolSize.NumUniformBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, EngineCI.DynamicDescriptorPoolSize.NumStorageBufferDescriptors}, - {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, EngineCI.MainDescriptorPoolSize.NumInputAttachmentDescriptors}, + {VK_DESCRIPTOR_TYPE_SAMPLER, EngineCI.DynamicDescriptorPoolSize.NumSeparateSamplerDescriptors}, + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, EngineCI.DynamicDescriptorPoolSize.NumCombinedSamplerDescriptors}, + {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, EngineCI.DynamicDescriptorPoolSize.NumSampledImageDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, EngineCI.DynamicDescriptorPoolSize.NumStorageImageDescriptors}, + {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumUniformTexelBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumStorageTexelBufferDescriptors}, + //{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumUniformBufferDescriptors}, + //{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, EngineCI.DynamicDescriptorPoolSize.NumStorageBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, EngineCI.DynamicDescriptorPoolSize.NumUniformBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, EngineCI.DynamicDescriptorPoolSize.NumStorageBufferDescriptors}, + {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, EngineCI.MainDescriptorPoolSize.NumInputAttachmentDescriptors}, + {VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, EngineCI.MainDescriptorPoolSize.NumAccelStructDescriptors} }, EngineCI.DynamicDescriptorPoolSize.MaxDescriptorSets, false // Pools can only be reset @@ -151,9 +159,27 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* EngineCI.DynamicHeapSize, ~Uint64{0} }, - m_pDxCompiler{CreateDXCompiler(DXCompilerTarget::Vulkan, EngineCI.pDxCompilerPath)} + m_pDxCompiler{CreateDXCompiler(DXCompilerTarget::Vulkan, EngineCI.pDxCompilerPath)}, + m_Properties + { + m_PhysicalDevice->GetExtProperties().RayTracingPipeline.shaderGroupHandleSize, + m_PhysicalDevice->GetExtProperties().RayTracingPipeline.maxShaderGroupStride, + m_PhysicalDevice->GetExtProperties().RayTracingPipeline.shaderGroupBaseAlignment, + m_PhysicalDevice->GetExtProperties().MeshShader.maxDrawMeshTasksCount, + m_PhysicalDevice->GetExtProperties().RayTracingPipeline.maxRayRecursionDepth + 1, // for compatibility with D3D12 + m_PhysicalDevice->GetExtProperties().RayTracingPipeline.maxRayDispatchInvocationCount + } // 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"); + + // set device properties + { + static_assert(sizeof(DeviceProperties) == sizeof(Uint32) * 1, "Please set new properties below"); + m_DeviceProperties.MaxRayTracingRecursionDepth = m_Properties.MaxRayTracingRecursionDepth; + } + m_DeviceCaps.DevType = RENDER_DEVICE_TYPE_VULKAN; m_DeviceCaps.MajorVersion = 1; m_DeviceCaps.MinorVersion = 0; @@ -223,7 +249,7 @@ RenderDeviceVkImpl::RenderDeviceVkImpl(IReferenceCounters* Features.DurationQueries = DEVICE_FEATURE_STATE_ENABLED; #if defined(_MSC_VER) && defined(_WIN64) - static_assert(sizeof(DeviceFeatures) == 31, "Did you add a new feature to DeviceFeatures? Please handle its satus here (if necessary)."); + static_assert(sizeof(DeviceFeatures) == 32, "Did you add a new feature to DeviceFeatures? Please handle its satus here (if necessary)."); #endif const auto& vkDeviceLimits = m_PhysicalDevice->GetProperties().limits; @@ -567,6 +593,10 @@ void RenderDeviceVkImpl::CreateComputePipelineState(const ComputePipelineStateCr CreatePipelineState(PSOCreateInfo, ppPipelineState); } +void RenderDeviceVkImpl::CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& PSOCreateInfo, IPipelineState** ppPipelineState) +{ + CreatePipelineState(PSOCreateInfo, ppPipelineState); +} void RenderDeviceVkImpl::CreateBufferFromVulkanResource(VkBuffer vkBuffer, const BufferDesc& BuffDesc, RESOURCE_STATE InitialState, IBuffer** ppBuffer) { @@ -732,4 +762,72 @@ void RenderDeviceVkImpl::CreateFramebuffer(const FramebufferDesc& Desc, IFramebu }); } +void RenderDeviceVkImpl::CreateBLASFromVulkanResource(VkAccelerationStructureKHR vkBLAS, + const BottomLevelASDesc& Desc, + RESOURCE_STATE InitialState, + IBottomLevelAS** ppBLAS) +{ + CreateDeviceObject( + "BottomLevelAS", Desc, ppBLAS, + [&]() // + { + BottomLevelASVkImpl* pBottomLevelASVk(NEW_RC_OBJ(m_BLASAllocator, "BottomLevelASVkImpl instance", BottomLevelASVkImpl)(this, Desc, InitialState, vkBLAS)); + pBottomLevelASVk->QueryInterface(IID_BottomLevelAS, reinterpret_cast<IObject**>(ppBLAS)); + OnCreateDeviceObject(pBottomLevelASVk); + } // + ); +} + +void RenderDeviceVkImpl::CreateBLAS(const BottomLevelASDesc& Desc, + IBottomLevelAS** ppBLAS) +{ + CreateDeviceObject("BottomLevelAS", Desc, ppBLAS, + [&]() // + { + BottomLevelASVkImpl* pBottomLevelASVk(NEW_RC_OBJ(m_BLASAllocator, "BottomLevelASVkImpl instance", BottomLevelASVkImpl)(this, Desc)); + pBottomLevelASVk->QueryInterface(IID_BottomLevelAS, reinterpret_cast<IObject**>(ppBLAS)); + OnCreateDeviceObject(pBottomLevelASVk); + }); +} + +void RenderDeviceVkImpl::CreateTLASFromVulkanResource(VkAccelerationStructureKHR vkTLAS, + const TopLevelASDesc& Desc, + RESOURCE_STATE InitialState, + ITopLevelAS** ppTLAS) +{ + CreateDeviceObject( + "TopLevelAS", Desc, ppTLAS, + [&]() // + { + TopLevelASVkImpl* pTopLevelASVk(NEW_RC_OBJ(m_BLASAllocator, "TopLevelASVkImpl instance", TopLevelASVkImpl)(this, Desc, InitialState, vkTLAS)); + pTopLevelASVk->QueryInterface(IID_TopLevelAS, reinterpret_cast<IObject**>(ppTLAS)); + OnCreateDeviceObject(pTopLevelASVk); + } // + ); +} + +void RenderDeviceVkImpl::CreateTLAS(const TopLevelASDesc& Desc, + ITopLevelAS** ppTLAS) +{ + CreateDeviceObject("TopLevelAS", Desc, ppTLAS, + [&]() // + { + TopLevelASVkImpl* pTopLevelASVk(NEW_RC_OBJ(m_TLASAllocator, "TopLevelASVkImpl instance", TopLevelASVkImpl)(this, Desc)); + pTopLevelASVk->QueryInterface(IID_TopLevelAS, reinterpret_cast<IObject**>(ppTLAS)); + OnCreateDeviceObject(pTopLevelASVk); + }); +} + +void RenderDeviceVkImpl::CreateSBT(const ShaderBindingTableDesc& Desc, + IShaderBindingTable** ppSBT) +{ + CreateDeviceObject("ShaderBindingTable", Desc, ppSBT, + [&]() // + { + ShaderBindingTableVkImpl* pSBTVk(NEW_RC_OBJ(m_SBTAllocator, "ShaderBindingTableVkImpl instance", ShaderBindingTableVkImpl)(this, Desc)); + pSBTVk->QueryInterface(IID_ShaderBindingTable, reinterpret_cast<IObject**>(ppSBT)); + OnCreateDeviceObject(pSBTVk); + }); +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderBindingTableVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderBindingTableVkImpl.cpp new file mode 100644 index 00000000..194d15f4 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/ShaderBindingTableVkImpl.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2019-2020 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 "ShaderBindingTableVkImpl.hpp" +#include "BufferVkImpl.hpp" +#include "VulkanTypeConversions.hpp" + +namespace Diligent +{ + +ShaderBindingTableVkImpl::ShaderBindingTableVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const ShaderBindingTableDesc& Desc, + bool bIsDeviceInternal) : + TShaderBindingTableBase{pRefCounters, pRenderDeviceVk, Desc, bIsDeviceInternal} +{ +} + +ShaderBindingTableVkImpl::~ShaderBindingTableVkImpl() +{ +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp index 3e4a65cf..d81fb675 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceBindingVkImpl.cpp @@ -30,7 +30,7 @@ #include "PipelineStateVkImpl.hpp" #include "ShaderVkImpl.hpp" #include "RenderDeviceVkImpl.hpp" -#include "LinearAllocator.hpp" +#include "FixedLinearAllocator.hpp" namespace Diligent { @@ -54,7 +54,7 @@ ShaderResourceBindingVkImpl::ShaderResourceBindingVkImpl(IReferenceCounters* pR m_NumShaders = static_cast<decltype(m_NumShaders)>(pPSO->GetNumShaderStages()); - LinearAllocator MemPool{GetRawAllocator()}; + FixedLinearAllocator MemPool{GetRawAllocator()}; MemPool.AddSpace<ShaderVariableManagerVk>(m_NumShaders); MemPool.Reserve(); m_pShaderVarMgrs = MemPool.ConstructArray<ShaderVariableManagerVk>(m_NumShaders, std::ref(*this), std::ref(m_ShaderResourceCache)); diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index caf8ffd2..26516695 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -33,6 +33,7 @@ #include "TextureViewVkImpl.hpp" #include "TextureVkImpl.hpp" #include "SamplerVkImpl.hpp" +#include "TopLevelASVkImpl.hpp" #include "VulkanTypeConversions.hpp" namespace Diligent @@ -155,7 +156,7 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) for (Uint32 res = 0; res < m_TotalResources; ++res) { auto& Res = pResources[res]; - static_assert(SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes == 11, "Please handle the new resource type below"); + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type below"); switch (Res.Type) { case SPIRVShaderResourceAttribs::ResourceType::UniformBuffer: @@ -321,6 +322,40 @@ void ShaderResourceCacheVk::TransitionResources(DeviceContextVkImpl* pCtxVkImpl) } break; + case SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure: + { + auto* pTLASVk = Res.pObject.RawPtr<TopLevelASVkImpl>(); + if (pTLASVk != nullptr && pTLASVk->IsInKnownState()) + { + constexpr RESOURCE_STATE RequiredState = RESOURCE_STATE_RAY_TRACING; + const bool IsInRequiredState = pTLASVk->CheckState(RequiredState); + if (VerifyOnly) + { + if (!IsInRequiredState) + { + LOG_ERROR_MESSAGE("State of TLAS '", pTLASVk->GetDesc().Name, "' is incorrect. Required state: ", + GetResourceStateString(RequiredState), ". Actual state: ", + GetResourceStateString(pTLASVk->GetState()), + ". Call IDeviceContext::TransitionShaderResources(), use RESOURCE_STATE_TRANSITION_MODE_TRANSITION " + "when calling IDeviceContext::CommitShaderResources() or explicitly transition the TLAS state " + "with IDeviceContext::TransitionResourceStates()."); + } + } + else + { + if (!IsInRequiredState) + { + pCtxVkImpl->TransitionTLASState(*pTLASVk, RESOURCE_STATE_UNKNOWN, RequiredState, true); + } + } + +#ifdef DILIGENT_DEVELOPMENT + pTLASVk->ValidateContent(); +#endif + } + } + break; + default: UNEXPECTED("Unexpected resource type"); } } @@ -497,4 +532,20 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetInputAttachmentDescrip return DescrImgInfo; } +VkWriteDescriptorSetAccelerationStructureKHR ShaderResourceCacheVk::Resource::GetAccelerationStructureWriteInfo() const +{ + VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure, "Acceleration structure resource is expected"); + DEV_CHECK_ERR(pObject != nullptr, "Unable to get acceleration structure write info: cached object is null"); + + auto* pTLASVk = pObject.RawPtr<const TopLevelASVkImpl>(); + + VkWriteDescriptorSetAccelerationStructureKHR DescrAS; + DescrAS.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR; + DescrAS.pNext = nullptr; + DescrAS.accelerationStructureCount = 1; + DescrAS.pAccelerationStructures = pTLASVk->GetVkTLASPtr(); + + return DescrAS; +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp index e73ee9aa..e2ea6cb5 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceLayoutVk.cpp @@ -39,6 +39,7 @@ #include "ShaderResourceVariableBase.hpp" #include "StringTools.hpp" #include "PipelineStateVkImpl.hpp" +#include "TopLevelASVkImpl.hpp" namespace Diligent { @@ -93,12 +94,24 @@ static SHADER_RESOURCE_VARIABLE_TYPE FindShaderVariableType(SHADER_TYPE } } -ShaderResourceLayoutVk::ShaderStageInfo::ShaderStageInfo(SHADER_TYPE _Type, - const ShaderVkImpl* _pShader) : - Type{_Type}, - pShader{_pShader}, - SPIRV{pShader->GetSPIRV()} + +ShaderResourceLayoutVk::ShaderStageInfo::ShaderStageInfo(SHADER_TYPE Stage, const ShaderVkImpl* pShader) : + Type{Stage}, + Shaders{pShader}, + SPIRVs{pShader->GetSPIRV()} +{ +} + +void ShaderResourceLayoutVk::ShaderStageInfo::Append(const ShaderVkImpl* pShader) +{ + Shaders.push_back(pShader); + SPIRVs.push_back(pShader->GetSPIRV()); +} + +size_t ShaderResourceLayoutVk::ShaderStageInfo::Count() const { + VERIFY_EXPR(Shaders.size() == SPIRVs.size()); + return Shaders.size(); } @@ -111,33 +124,51 @@ ShaderResourceLayoutVk::~ShaderResourceLayoutVk() GetImmutableSampler(s).~ImmutableSamplerPtrType(); } -void ShaderResourceLayoutVk::AllocateMemory(const ShaderVkImpl* pShader, - IMemoryAllocator& Allocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - bool AllocateImmutableSamplers) +StringPool ShaderResourceLayoutVk::AllocateMemory(const std::vector<const ShaderVkImpl*>& 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(!m_pResources); - m_pResources = pShader->GetShaderResources(); + 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 = ShaderName.length() + 1; - const auto ShaderType = pShader->GetDesc().ShaderType; - VERIFY_EXPR(m_pResources->GetShaderType() == ShaderType); // Count the number of resources to allocate all needed memory + for (size_t s = 0; s < Shaders.size(); ++s) { - const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); - const auto* CombinedSamplerSuffix = m_pResources->GetCombinedSamplerSuffix(); - m_pResources->ProcessResources( + 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(ShaderType, ResAttribs, ResourceLayoutDesc, CombinedSamplerSuffix); + auto VarType = FindShaderVariableType(m_ShaderType, ResAttribs, ResourceLayoutDesc, CombinedSamplerSuffix); if (IsAllowedType(VarType, AllowedTypeBits)) { - // For immutable separate samplers we still allocate VkResource instances, but they are never exposed to the app + bool IsUniqueName = UniqueNames.emplace(HashMapStringKey{ResAttribs.Name}, Uint32{InvalidResourceIndex}).second; + if (IsUniqueName) + { + StringPoolSize += strlen(ResAttribs.Name) + 1; - VERIFY(Uint32{m_NumResources[VarType]} + 1 <= Uint32{std::numeric_limits<Uint16>::max()}, "Number of resources exceeds Uint16 maximum representable value"); - ++m_NumResources[VarType]; + // 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<Uint16>::max()}, "Number of resources exceeds Uint16 maximum representable value"); + ++m_NumResources[VarType]; + } } } // ); @@ -157,24 +188,33 @@ void ShaderResourceLayoutVk::AllocateMemory(const ShaderVkImpl* for (Uint32 s = 0; s < ResourceLayoutDesc.NumImmutableSamplers; ++s) { const auto& ImtblSamDesc = ResourceLayoutDesc.ImmutableSamplers[s]; - if ((ImtblSamDesc.ShaderStages & ShaderType) != 0) + if ((ImtblSamDesc.ShaderStages & m_ShaderType) != 0) ++m_NumImmutableSamplers; } } - size_t MemSize = TotalResources * sizeof(VkResource) + m_NumImmutableSamplers * sizeof(ImmutableSamplerPtrType); - static_assert((sizeof(VkResource) % sizeof(void*)) == 0, "sizeof(VkResource) must be multiple of sizeof(void*)"); - if (MemSize == 0) - return; + FixedLinearAllocator MemPool{Allocator}; - auto* pRawMem = ALLOCATE_RAW(Allocator, "Raw memory buffer for shader resource layout resources", MemSize); - m_ResourceBuffer = std::unique_ptr<void, STDDeleterRawMem<void>>(pRawMem, Allocator); - for (Uint32 s = 0; s < m_NumImmutableSamplers; ++s) - { - // We need to initialize immutable samplers - auto& UninitializedImmutableSampler = GetImmutableSampler(s); - new (std::addressof(UninitializedImmutableSampler)) ImmutableSamplerPtrType; - } + MemPool.AddSpace<VkResource>(TotalResources); + MemPool.AddSpace<ImmutableSamplerPtrType>(m_NumImmutableSamplers); + MemPool.AddSpace<char>(StringPoolSize); + + MemPool.Reserve(); + + auto* pResources = MemPool.Allocate<VkResource>(TotalResources); + auto* pImtblSamplers = MemPool.ConstructArray<ImmutableSamplerPtrType>(m_NumImmutableSamplers); + auto* pStringData = MemPool.ConstructArray<char>(StringPoolSize); + + m_ResourceBuffer = std::unique_ptr<void, STDDeleterRawMem<void>>(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; } @@ -194,8 +234,8 @@ static Uint32 FindAssignedSampler(const ShaderResourceLayoutVk& Layout, for (SamplerInd = 0; SamplerInd < CurrResourceCount; ++SamplerInd) { const auto& Res = Layout.GetResource(ImgVarType, SamplerInd); - if (Res.SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && - strcmp(Res.SpirvAttribs.Name, SepSampler.Name) == 0) + 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, @@ -217,62 +257,103 @@ static Uint32 FindAssignedSampler(const ShaderResourceLayoutVk& Layout, } -void ShaderResourceLayoutVk::InitializeStaticResourceLayout(const ShaderVkImpl* pShader, - IMemoryAllocator& LayoutDataAllocator, - const PipelineResourceLayoutDesc& ResourceLayoutDesc, - ShaderResourceCacheVk& StaticResourceCache) +void ShaderResourceLayoutVk::InitializeStaticResourceLayout(const std::vector<const ShaderVkImpl*>& Shaders, + IMemoryAllocator& LayoutDataAllocator, + const PipelineResourceLayoutDesc& ResourceLayoutDesc, + ShaderResourceCacheVk& StaticResourceCache) { const auto AllowedVarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; // 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; - AllocateMemory(pShader, LayoutDataAllocator, ResourceLayoutDesc, &AllowedVarType, 1, AllocateImmutableSamplers); + ResourceNameToIndex_t ResourceNameToIndex; + constexpr bool AllocateImmutableSamplers = false; + + auto stringPool = AllocateMemory(Shaders, LayoutDataAllocator, ResourceLayoutDesc, &AllowedVarType, 1, ResourceNameToIndex, AllocateImmutableSamplers); std::array<Uint32, SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES> CurrResInd = {}; - Uint32 StaticResCacheSize = 0; + Uint32 StaticResCacheSize = 0; + const Uint32 AllowedTypeBits = GetAllowedTypeBits(&AllowedVarType, 1); - const Uint32 AllowedTypeBits = GetAllowedTypeBits(&AllowedVarType, 1); - const auto* CombinedSamplerSuffix = m_pResources->GetCombinedSamplerSuffix(); - const auto ShaderType = pShader->GetDesc().ShaderType; + 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; - m_pResources->ProcessResources( - [&](const SPIRVShaderResourceAttribs& Attribs, Uint32) // - { - auto VarType = FindShaderVariableType(ShaderType, Attribs, ResourceLayoutDesc, CombinedSamplerSuffix); - if (!IsAllowedType(VarType, AllowedTypeBits)) - return; + auto ResIter = ResourceNameToIndex.find(HashMapStringKey{Attribs.Name}); + VERIFY_EXPR(ResIter != ResourceNameToIndex.end()); - 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(ShaderType, ResourceLayoutDesc, Attribs, CombinedSamplerSuffix); - // For immutable separate samplers we allocate VkResource instances, but they are never exposed to the app - } + 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); + // For immutable separate samplers we allocate VkResource instances, but they are never exposed to the app + } - Uint32 Binding = Attribs.Type; - Uint32 DescriptorSet = 0; - Uint32 CacheOffset = StaticResCacheSize; - StaticResCacheSize += Attribs.ArraySize; + 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, *m_pResources, Attribs, CurrResInd[VarType], VarType); - } - ::new (&GetResource(VarType, CurrResInd[VarType]++)) VkResource(*this, Attribs, VarType, Binding, DescriptorSet, CacheOffset, SamplerInd, SrcImmutableSamplerInd >= 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(*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.ArraySize, + Attribs.Type, + Attribs.GetResourceDimension(), + Attribs.IsMultisample(), + VarType, + Binding, + DescriptorSet, + CacheOffset, + SamplerInd, + SrcImmutableSamplerInd >= 0, + Attribs.BufferStaticSize, + Attribs.BufferStride // + }; + } + else + { + // merge with existing + auto& ExistingRes = GetResource(VarType, ResIter->second); + VERIFY_EXPR(ExistingRes.VariableType == VarType); + VERIFY_EXPR(ExistingRes.Type == Attribs.Type); + VERIFY_EXPR(ExistingRes.ResourceDim == Attribs.ResourceDim); + VERIFY_EXPR(ExistingRes.IsMS == Attribs.IsMS); + VERIFY_EXPR(ExistingRes.ArraySize == Attribs.ArraySize); + } + } // + ); + } #ifdef DILIGENT_DEBUG for (SHADER_RESOURCE_VARIABLE_TYPE VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC; VarType < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; VarType = static_cast<SHADER_RESOURCE_VARIABLE_TYPE>(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); @@ -293,14 +374,14 @@ void ShaderResourceLayoutVk::dvpVerifyResourceLayoutDesc(const TShaderStages& std::string ShadersStr; while (Stages != SHADER_TYPE_UNKNOWN) { - const auto ShaderType = Stages & static_cast<SHADER_TYPE>(~(static_cast<Uint32>(Stages) - 1)); - const char* ShaderName = nullptr; + const auto ShaderType = Stages & static_cast<SHADER_TYPE>(~(static_cast<Uint32>(Stages) - 1)); + String ShaderName; for (const auto& StageInfo : ShaderStages) { if ((Stages & StageInfo.Type) != 0) { - ShaderName = StageInfo.pShader->GetDesc().Name; + ShaderName = GetShaderGroupName(StageInfo.Shaders); break; } } @@ -309,10 +390,10 @@ void ShaderResourceLayoutVk::dvpVerifyResourceLayoutDesc(const TShaderStages& ShadersStr.append(", "); ShadersStr.append(GetShaderTypeLiteralName(ShaderType)); ShadersStr.append(" ("); - if (ShaderName) + if (ShaderName.size()) { ShadersStr.push_back('\''); - ShadersStr.append(ShaderName ? ShaderName : "<Not enabled in PSO>"); + ShadersStr.append(ShaderName); ShadersStr.push_back('\''); } else @@ -340,9 +421,15 @@ void ShaderResourceLayoutVk::dvpVerifyResourceLayoutDesc(const TShaderStages& bool VariableFound = false; for (size_t s = 0; s < ShaderStages.size() && !VariableFound; ++s) { - const auto& Resources = *ShaderStages[s].pShader->GetShaderResources(); - if ((VarDesc.ShaderStages & Resources.GetShaderType()) != 0) + 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); @@ -373,28 +460,34 @@ void ShaderResourceLayoutVk::dvpVerifyResourceLayoutDesc(const TShaderStages& bool SamplerFound = false; for (size_t s = 0; s < ShaderStages.size() && !SamplerFound; ++s) { - const auto& Resources = *ShaderStages[s].pShader->GetShaderResources(); - if ((ImtblSamDesc.ShaderStages & Resources.GetShaderType()) == 0) + const auto& Stage = ShaderStages[s]; + if ((Stage.Type & ImtblSamDesc.ShaderStages) == 0) continue; - // Irrespective of whether HLSL-style combined image samplers are used, - // an immutable sampler can be assigned to a GLSL sampled image (i.e. sampler2D g_tex) - for (Uint32 i = 0; i < Resources.GetNumSmpldImgs() && !SamplerFound; ++i) + for (size_t j = 0; j < Stage.Shaders.size() && !SamplerFound; ++j) { - const auto& SmplImg = Resources.GetSmpldImg(i); - SamplerFound = (strcmp(SmplImg.Name, ImtblSamDesc.SamplerOrTextureName) == 0); - } + const auto& Resources = *Stage.Shaders[j]->GetShaderResources(); + VERIFY_EXPR(Resources.GetShaderType() == Stage.Type); - if (!SamplerFound) - { - // Check if an immutable sampler 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) + // 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) { - const auto& SepSmpl = Resources.GetSepSmplr(i); - SamplerFound = StreqSuff(SepSmpl.Name, ImtblSamDesc.SamplerOrTextureName, CombinedSamplerSuffix); + // 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); + } } } } @@ -423,15 +516,22 @@ void ShaderResourceLayoutVk::Initialize(IRenderDevice* pRende dvpVerifyResourceLayoutDesc(ShaderStages, ResourceLayoutDesc, VerifyVariables, VerifyImmutableSamplers); #endif - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes = nullptr; - const Uint32 NumAllowedTypes = 0; - const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); - constexpr bool AllocateImmutableSamplers = true; + std::array<ResourceNameToIndex_t, MAX_SHADERS_IN_PIPELINE> ResourceNameToIndexArray; + + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes = nullptr; + const Uint32 NumAllowedTypes = 0; + const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); + constexpr bool AllocateImmutableSamplers = true; + + std::vector<StringPool> stringPools; + stringPools.reserve(ShaderStages.size()); for (size_t s = 0; s < ShaderStages.size(); ++s) { - Layouts[s].AllocateMemory(ShaderStages[s].pShader, LayoutDataAllocator, ResourceLayoutDesc, - AllowedVarTypes, NumAllowedTypes, AllocateImmutableSamplers); + stringPools.emplace_back( + Layouts[s].AllocateMemory(ShaderStages[s].Shaders, LayoutDataAllocator, ResourceLayoutDesc, + AllowedVarTypes, NumAllowedTypes, ResourceNameToIndexArray[s], + AllocateImmutableSamplers)); } //VERIFY_EXPR(NumShaders <= MAX_SHADERS_IN_PIPELINE); @@ -441,77 +541,123 @@ void ShaderResourceLayoutVk::Initialize(IRenderDevice* pRende std::unordered_map<Uint32, std::pair<Uint32, Uint32>> dbgBindings_CacheOffsets; #endif - auto AddResource = [&](Uint32 ShaderInd, + auto AddResource = [&](const Uint32 ShaderInd, ShaderResourceLayoutVk& ResLayout, const SPIRVShaderResources& Resources, - const SPIRVShaderResourceAttribs& Attribs) // + const SPIRVShaderResourceAttribs& Attribs, + ResourceNameToIndex_t& ResourceNameToIndex, + std::vector<uint32_t>& SPIRV) // { const auto ShaderType = Resources.GetShaderType(); const SHADER_RESOURCE_VARIABLE_TYPE VarType = FindShaderVariableType(ShaderType, Attribs, ResourceLayoutDesc, Resources.GetCombinedSamplerSuffix()); if (!IsAllowedType(VarType, AllowedTypeBits)) return; - Uint32 Binding = 0; - Uint32 DescriptorSet = 0; - Uint32 CacheOffset = 0; - Uint32 SamplerInd = VkResource::InvalidSamplerInd; + auto ResIter = ResourceNameToIndex.find(HashMapStringKey{Attribs.Name}); + VERIFY_EXPR(ResIter != ResourceNameToIndex.end()); - if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage) + const VkResource* pResource = nullptr; + if (ResIter->second == InvalidResourceIndex) { - // 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[ShaderInd][VarType], VarType); - } + // add new resource + Uint32 Binding = 0; + Uint32 DescriptorSet = 0; + Uint32 CacheOffset = 0; + Uint32 SamplerInd = VkResource::InvalidSamplerInd; - 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) + 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[ShaderInd][VarType], VarType); + } + + VkSampler vkImmutableSampler = VK_NULL_HANDLE; + if (Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || + Attribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler) { - auto& ImmutableSampler = ResLayout.GetImmutableSampler(CurrImmutableSamplerInd[ShaderInd]++); - VERIFY(!ImmutableSampler, "Immutable sampler has already been initialized!"); - const auto& ImmutableSamplerDesc = ResourceLayoutDesc.ImmutableSamplers[SrcImmutableSamplerInd].Desc; - pRenderDevice->CreateSampler(ImmutableSamplerDesc, &ImmutableSampler); - vkImmutableSampler = ImmutableSampler.RawPtr<SamplerVkImpl>()->GetVkSampler(); + // Only search for the immutable sampler for combined image samplers and separate samplers + Int32 SrcImmutableSamplerInd = FindImmutableSampler(ShaderType, ResourceLayoutDesc, Attribs, Resources.GetCombinedSamplerSuffix()); + if (SrcImmutableSamplerInd >= 0) + { + auto& ImmutableSampler = ResLayout.GetImmutableSampler(CurrImmutableSamplerInd[ShaderInd]++); + VERIFY(!ImmutableSampler, "Immutable sampler has already been initialized!"); + const auto& ImmutableSamplerDesc = ResourceLayoutDesc.ImmutableSamplers[SrcImmutableSamplerInd].Desc; + pRenderDevice->CreateSampler(ImmutableSamplerDesc, &ImmutableSampler); + vkImmutableSampler = ImmutableSampler.RawPtr<SamplerVkImpl>()->GetVkSampler(); + } } - } - auto& ShaderSPIRV = ShaderStages[ShaderInd].SPIRV; - PipelineLayout.AllocateResourceSlot(Attribs, VarType, vkImmutableSampler, Resources.GetShaderType(), DescriptorSet, Binding, CacheOffset, ShaderSPIRV); - VERIFY(DescriptorSet <= std::numeric_limits<decltype(VkResource::DescriptorSet)>::max(), "Descriptor set (", DescriptorSet, ") excceeds maximum representable value"); - VERIFY(Binding <= std::numeric_limits<decltype(VkResource::Binding)>::max(), "Binding (", Binding, ") excceeds maximum representable value"); + PipelineLayout.AllocateResourceSlot(Attribs, VarType, vkImmutableSampler, Resources.GetShaderType(), DescriptorSet, Binding, CacheOffset); + VERIFY(DescriptorSet <= std::numeric_limits<decltype(VkResource::DescriptorSet)>::max(), "Descriptor set (", DescriptorSet, ") excceeds maximum representable value"); + VERIFY(Binding <= std::numeric_limits<decltype(VkResource::Binding)>::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); + // 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[ShaderInd][VarType]; - ::new (&ResLayout.GetResource(VarType, ResInd++)) VkResource(ResLayout, Attribs, VarType, Binding, DescriptorSet, CacheOffset, SamplerInd, vkImmutableSampler != VK_NULL_HANDLE ? 1 : 0); + auto& ResInd = CurrResInd[ShaderInd][VarType]; + ResIter->second = ResInd; + + pResource = ::new (&ResLayout.GetResource(VarType, ResInd++)) VkResource // + { + ResLayout, + stringPools[ShaderInd].CopyString(Attribs.Name), + Attribs.ArraySize, + Attribs.Type, + Attribs.GetResourceDimension(), + Attribs.IsMultisample(), + VarType, + Binding, + DescriptorSet, + CacheOffset, + SamplerInd, + vkImmutableSampler != VK_NULL_HANDLE, + Attribs.BufferStaticSize, + Attribs.BufferStride // + }; + } + else + { + // merge with existing + pResource = &ResLayout.GetResource(VarType, ResIter->second); + + VERIFY_EXPR(pResource->VariableType == VarType); + VERIFY_EXPR(pResource->Type == Attribs.Type); + VERIFY_EXPR(pResource->ResourceDim == Attribs.ResourceDim); + VERIFY_EXPR(pResource->IsMS == Attribs.IsMS); + VERIFY_EXPR(pResource->ArraySize == Attribs.ArraySize); + } + 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; auto& Layout = Layouts[s]; - auto* pShaderVk = ShaderStages[s].pShader; - auto& Resources = *pShaderVk->GetShaderResources(); - for (Uint32 n = 0; n < Resources.GetNumUBs(); ++n) + auto& NameToIdx = ResourceNameToIndexArray[s]; + for (size_t i = 0; i < Shaders.size(); ++i) { - const auto& UB = Resources.GetUB(n); - auto VarType = GetShaderVariableType(Resources.GetShaderType(), UB.Name, ResourceLayoutDesc); - if (IsAllowedType(VarType, AllowedTypeBits)) + auto& SPIRV = ShaderStages[s].SPIRVs[i]; + auto& Resources = *Shaders[i]->GetShaderResources(); + for (Uint32 n = 0; n < Resources.GetNumUBs(); ++n) { - AddResource(static_cast<Uint32>(s), Layout, Resources, UB); + const auto& UB = Resources.GetUB(n); + auto VarType = GetShaderVariableType(Resources.GetShaderType(), UB.Name, ResourceLayoutDesc); + if (IsAllowedType(VarType, AllowedTypeBits)) + { + AddResource(static_cast<Uint32>(s), Layout, Resources, UB, NameToIdx, SPIRV); + } } } } @@ -519,15 +665,21 @@ void ShaderResourceLayoutVk::Initialize(IRenderDevice* pRende // Second, process all storage buffers for (size_t s = 0; s < ShaderStages.size(); ++s) { + auto& Shaders = ShaderStages[s].Shaders; auto& Layout = Layouts[s]; - auto& Resources = *ShaderStages[s].pShader->GetShaderResources(); - for (Uint32 n = 0; n < Resources.GetNumSBs(); ++n) + auto& NameToIdx = ResourceNameToIndexArray[s]; + for (size_t i = 0; i < Shaders.size(); ++i) { - const auto& SB = Resources.GetSB(n); - auto VarType = GetShaderVariableType(Resources.GetShaderType(), SB.Name, ResourceLayoutDesc); - if (IsAllowedType(VarType, AllowedTypeBits)) + auto& Resources = *Shaders[i]->GetShaderResources(); + auto& SPIRV = ShaderStages[s].SPIRVs[i]; + for (Uint32 n = 0; n < Resources.GetNumSBs(); ++n) { - AddResource(static_cast<Uint32>(s), Layout, Resources, SB); + const auto& SB = Resources.GetSB(n); + auto VarType = GetShaderVariableType(Resources.GetShaderType(), SB.Name, ResourceLayoutDesc); + if (IsAllowedType(VarType, AllowedTypeBits)) + { + AddResource(static_cast<Uint32>(s), Layout, Resources, SB, NameToIdx, SPIRV); + } } } } @@ -536,51 +688,62 @@ void ShaderResourceLayoutVk::Initialize(IRenderDevice* pRende for (size_t s = 0; s < ShaderStages.size(); ++s) { auto& Layout = Layouts[s]; - auto& Resources = *ShaderStages[s].pShader->GetShaderResources(); - // 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<Uint32>(s), Layout, Resources, Img); - }, - [&](const SPIRVShaderResourceAttribs& SmplImg, Uint32) - { - VERIFY_EXPR(SmplImg.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || SmplImg.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer); - AddResource(static_cast<Uint32>(s), Layout, Resources, SmplImg); - }, - [&](const SPIRVShaderResourceAttribs& AC, Uint32) - { - VERIFY_EXPR(AC.Type == SPIRVShaderResourceAttribs::ResourceType::AtomicCounter); - AddResource(static_cast<Uint32>(s), Layout, Resources, AC); - }, - [&](const SPIRVShaderResourceAttribs& SepSmpl, Uint32) - { - VERIFY_EXPR(SepSmpl.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler); - AddResource(static_cast<Uint32>(s), Layout, Resources, SepSmpl); - }, - [&](const SPIRVShaderResourceAttribs& SepImg, Uint32) - { - VERIFY_EXPR(SepImg.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage || SepImg.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer); - AddResource(static_cast<Uint32>(s), Layout, Resources, SepImg); - }, - [&](const SPIRVShaderResourceAttribs& InputAtt, Uint32) - { - VERIFY_EXPR(InputAtt.Type == SPIRVShaderResourceAttribs::ResourceType::InputAttachment); - AddResource(static_cast<Uint32>(s), Layout, Resources, InputAtt); - } - ); - // clang-format on + auto& Shaders = ShaderStages[s].Shaders; + auto& NameToIdx = ResourceNameToIndexArray[s]; + 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<Uint32>(s), Layout, Resources, Img, NameToIdx, SPIRV); + }, + [&](const SPIRVShaderResourceAttribs& SmplImg, Uint32) + { + VERIFY_EXPR(SmplImg.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage || SmplImg.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer); + AddResource(static_cast<Uint32>(s), Layout, Resources, SmplImg, NameToIdx, SPIRV); + }, + [&](const SPIRVShaderResourceAttribs& AC, Uint32) + { + VERIFY_EXPR(AC.Type == SPIRVShaderResourceAttribs::ResourceType::AtomicCounter); + AddResource(static_cast<Uint32>(s), Layout, Resources, AC, NameToIdx, SPIRV); + }, + [&](const SPIRVShaderResourceAttribs& SepSmpl, Uint32) + { + VERIFY_EXPR(SepSmpl.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler); + AddResource(static_cast<Uint32>(s), Layout, Resources, SepSmpl, NameToIdx, SPIRV); + }, + [&](const SPIRVShaderResourceAttribs& SepImg, Uint32) + { + VERIFY_EXPR(SepImg.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage || SepImg.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer); + AddResource(static_cast<Uint32>(s), Layout, Resources, SepImg, NameToIdx, SPIRV); + }, + [&](const SPIRVShaderResourceAttribs& InputAtt, Uint32) + { + VERIFY_EXPR(InputAtt.Type == SPIRVShaderResourceAttribs::ResourceType::InputAttachment); + AddResource(static_cast<Uint32>(s), Layout, Resources, InputAtt, NameToIdx, SPIRV); + }, + [&](const SPIRVShaderResourceAttribs& AccelStruct, Uint32) + { + VERIFY_EXPR(AccelStruct.Type == SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure); + AddResource(static_cast<Uint32>(s), Layout, Resources, AccelStruct, NameToIdx, SPIRV); + } + ); + // clang-format on + } } #ifdef DILIGENT_DEBUG @@ -593,28 +756,32 @@ void ShaderResourceLayoutVk::Initialize(IRenderDevice* pRende } // 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 + +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 = nullptr; + 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(SpirvAttribs); + WriteDescrSet.descriptorType = PipelineLayout::GetVkDescriptorType(Type); WriteDescrSet.pImageInfo = pImageInfo; WriteDescrSet.pBufferInfo = pBufferInfo; WriteDescrSet.pTexelBufferView = pTexelBufferView; @@ -654,10 +821,18 @@ void ShaderResourceLayoutVk::VkResource::CacheUniformBuffer(IDeviceObject* Uint32 ArrayInd, Uint16& DynamicBuffersCounter) const { - VERIFY(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::UniformBuffer, "Uniform buffer resource is expected"); + VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::UniformBuffer, "Uniform buffer resource is expected"); RefCntAutoPtr<BufferVkImpl> pBufferVk{pBuffer, IID_BufferVk}; #ifdef DILIGENT_DEVELOPMENT - VerifyConstantBufferBinding(SpirvAttribs, GetVariableType(), ArrayInd, pBuffer, pBufferVk.RawPtr(), DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); + 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) { @@ -691,8 +866,8 @@ void ShaderResourceLayoutVk::VkResource::CacheStorageBuffer(IDeviceObject* Uint16& DynamicBuffersCounter) const { // clang-format off - VERIFY(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer || - SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer, + VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer || + Type == SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer, "Storage buffer resource is expected"); // clang-format on @@ -700,8 +875,8 @@ void ShaderResourceLayoutVk::VkResource::CacheStorageBuffer(IDeviceObject* #ifdef DILIGENT_DEVELOPMENT { // HLSL buffer SRVs are mapped to storge buffers in GLSL - auto RequiredViewType = SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer ? BUFFER_VIEW_SHADER_RESOURCE : BUFFER_VIEW_UNORDERED_ACCESS; - VerifyResourceViewBinding(SpirvAttribs, GetVariableType(), ArrayInd, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); + 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(); @@ -709,7 +884,25 @@ void ShaderResourceLayoutVk::VkResource::CacheStorageBuffer(IDeviceObject* 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 '", - SpirvAttribs.Name, "' in shader '", ParentResLayout.GetShaderName(), "': structured buffer view is expected."); + 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."); } } } @@ -747,8 +940,8 @@ void ShaderResourceLayoutVk::VkResource::CacheTexelBuffer(IDeviceObject* Uint16& DynamicBuffersCounter) const { // clang-format off - VERIFY(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer || - SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer, + VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer || + Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer, "Uniform or storage buffer resource is expected"); // clang-format on @@ -756,8 +949,8 @@ void ShaderResourceLayoutVk::VkResource::CacheTexelBuffer(IDeviceObject* #ifdef DILIGENT_DEVELOPMENT { // HLSL buffer SRVs are mapped to storge buffers in GLSL - auto RequiredViewType = SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer ? BUFFER_VIEW_UNORDERED_ACCESS : BUFFER_VIEW_SHADER_RESOURCE; - VerifyResourceViewBinding(SpirvAttribs, GetVariableType(), ArrayInd, pBufferView, pBufferViewVk.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); + 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(); @@ -765,7 +958,7 @@ void ShaderResourceLayoutVk::VkResource::CacheTexelBuffer(IDeviceObject* 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 '", - SpirvAttribs.Name, "' in shader '", ParentResLayout.GetShaderName(), "': formatted buffer view is expected."); + Name, "' in shader '", ParentResLayout.GetShaderName(), "': formatted buffer view is expected."); } } } @@ -805,9 +998,9 @@ void ShaderResourceLayoutVk::VkResource::CacheImage(IDeviceObject* TCacheSampler CacheSampler) const { // clang-format off - VERIFY(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::StorageImage || - SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage || - SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage, + 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 @@ -815,8 +1008,8 @@ void ShaderResourceLayoutVk::VkResource::CacheImage(IDeviceObject* #ifdef DILIGENT_DEVELOPMENT { // HLSL buffer SRVs are mapped to storge buffers in GLSL - auto RequiredViewType = SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::StorageImage ? TEXTURE_VIEW_UNORDERED_ACCESS : TEXTURE_VIEW_SHADER_RESOURCE; - VerifyResourceViewBinding(SpirvAttribs, GetVariableType(), ArrayInd, pTexView, pTexViewVk0.RawPtr(), {RequiredViewType}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); + 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*) {})) @@ -824,11 +1017,11 @@ void ShaderResourceLayoutVk::VkResource::CacheImage(IDeviceObject* // We can do RawPtr here safely since UpdateCachedResource() returned true auto* pTexViewVk = DstRes.pObject.RawPtr<TextureViewVkImpl>(); #ifdef DILIGENT_DEVELOPMENT - if (SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage && !IsImmutableSamplerAssigned()) + if (Type == SPIRVShaderResourceAttribs::ResourceType::SampledImage && !IsImmutableSamplerAssigned()) { if (pTexViewVk->GetSampler() == nullptr) { - LOG_ERROR_MESSAGE("Error binding texture view '", pTexViewVk->GetDesc().Name, "' to variable '", SpirvAttribs.GetPrintName(ArrayInd), + 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"); } } @@ -844,11 +1037,11 @@ void ShaderResourceLayoutVk::VkResource::CacheImage(IDeviceObject* if (SamplerInd != InvalidSamplerInd) { - VERIFY(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage, + 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.SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler); + VERIFY_EXPR(SamplerAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler); if (!SamplerAttribs.IsImmutableSamplerAssigned()) { auto* pSampler = pTexViewVk->GetSampler(); @@ -858,8 +1051,8 @@ void ShaderResourceLayoutVk::VkResource::CacheImage(IDeviceObject* } else { - LOG_ERROR_MESSAGE("Failed to bind sampler to sampler variable '", SamplerAttribs.SpirvAttribs.Name, - "' assigned to separate image '", SpirvAttribs.GetPrintName(ArrayInd), "' in shader '", + 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, '\''); } } @@ -872,20 +1065,20 @@ void ShaderResourceLayoutVk::VkResource::CacheSeparateSampler(IDeviceObject* VkDescriptorSet vkDescrSet, Uint32 ArrayInd) const { - VERIFY(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler, "Separate sampler resource is expected"); + VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler, "Separate sampler resource is expected"); VERIFY(!IsImmutableSamplerAssigned(), "This separate sampler is assigned an immutable sampler"); RefCntAutoPtr<SamplerVkImpl> pSamplerVk{pSampler, IID_Sampler}; #ifdef DILIGENT_DEVELOPMENT if (pSampler != nullptr && pSamplerVk == nullptr) { - LOG_ERROR_MESSAGE("Failed to bind object '", pSampler->GetDesc().Name, "' to variable '", SpirvAttribs.GetPrintName(ArrayInd), + 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 '", SpirvAttribs.GetPrintName(ArrayInd), + 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."); @@ -908,10 +1101,10 @@ void ShaderResourceLayoutVk::VkResource::CacheInputAttachment(IDeviceObject* VkDescriptorSet vkDescrSet, Uint32 ArrayInd) const { - VERIFY(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::InputAttachment, "Input attachment resource is expected"); + VERIFY(Type == SPIRVShaderResourceAttribs::ResourceType::InputAttachment, "Input attachment resource is expected"); RefCntAutoPtr<TextureViewVkImpl> pTexViewVk0{pTexView, IID_TextureViewVk}; #ifdef DILIGENT_DEVELOPMENT - VerifyResourceViewBinding(SpirvAttribs, GetVariableType(), ArrayInd, pTexView, pTexViewVk0.RawPtr(), {TEXTURE_VIEW_SHADER_RESOURCE}, DstRes.pObject.RawPtr(), ParentResLayout.GetShaderName()); + 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*) {})) { @@ -926,9 +1119,32 @@ void ShaderResourceLayoutVk::VkResource::CacheInputAttachment(IDeviceObject* } } +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<TopLevelASVkImpl> 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 < SpirvAttribs.ArraySize); + VERIFY_EXPR(ArrayIndex < ArraySize); auto& DstDescrSet = ResourceCache.GetDescriptorSet(DescriptorSet); auto vkDescrSet = DstDescrSet.GetVkDescriptorSet(); @@ -951,12 +1167,12 @@ void ShaderResourceLayoutVk::VkResource::BindResource(IDeviceObject* pObj, Uint3 } #endif auto& DstRes = DstDescrSet.GetResource(CacheOffset + ArrayIndex); - VERIFY(DstRes.Type == SpirvAttribs.Type, "Inconsistent types"); + VERIFY(DstRes.Type == Type, "Inconsistent types"); if (pObj) { - static_assert(SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes == 11, "Please handle the new resource type below"); - switch (SpirvAttribs.Type) + 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()); @@ -977,15 +1193,15 @@ void ShaderResourceLayoutVk::VkResource::BindResource(IDeviceObject* pObj, Uint3 case SPIRVShaderResourceAttribs::ResourceType::SampledImage: CacheImage(pObj, DstRes, vkDescrSet, ArrayIndex, [&](const VkResource& SeparateSampler, ISampler* pSampler) { - VERIFY(!SeparateSampler.IsImmutableSamplerAssigned(), "Separate sampler '", SeparateSampler.SpirvAttribs.Name, "' is assigned an immutable sampler"); - VERIFY_EXPR(SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateImage); - DEV_CHECK_ERR(SeparateSampler.SpirvAttribs.ArraySize == 1 || SeparateSampler.SpirvAttribs.ArraySize == SpirvAttribs.ArraySize, - "Array size (", SeparateSampler.SpirvAttribs.ArraySize, + 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.SpirvAttribs.Name, - "' must be one or the same as the array size (", SpirvAttribs.ArraySize, - ") of separate image variable '", SpirvAttribs.Name, "' it is assigned to"); - Uint32 SamplerArrInd = SeparateSampler.SpirvAttribs.ArraySize == 1 ? 0 : ArrayIndex; + 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; @@ -999,7 +1215,7 @@ void ShaderResourceLayoutVk::VkResource::BindResource(IDeviceObject* pObj, Uint3 { // 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 '", SpirvAttribs.Name, '\''); + LOG_ERROR_MESSAGE("Attempting to assign a sampler to an immutable sampler '", Name, '\''); } break; @@ -1007,14 +1223,18 @@ void ShaderResourceLayoutVk::VkResource::BindResource(IDeviceObject* pObj, Uint3 CacheInputAttachment(pObj, DstRes, vkDescrSet, ArrayIndex); break; - default: UNEXPECTED("Unknown resource type ", static_cast<Int32>(SpirvAttribs.Type)); + case SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure: + CacheAccelerationStructure(pObj, DstRes, vkDescrSet, ArrayIndex); + break; + + default: UNEXPECTED("Unknown resource type ", static_cast<Int32>(Type)); } } else { if (DstRes.pObject && GetVariableType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) { - LOG_ERROR_MESSAGE("Shader variable '", SpirvAttribs.Name, "' in shader '", ParentResLayout.GetShaderName(), + 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."); } @@ -1025,7 +1245,7 @@ void ShaderResourceLayoutVk::VkResource::BindResource(IDeviceObject* pObj, Uint3 bool ShaderResourceLayoutVk::VkResource::IsBound(Uint32 ArrayIndex, const ShaderResourceCacheVk& ResourceCache) const { - VERIFY_EXPR(ArrayIndex < SpirvAttribs.ArraySize); + VERIFY_EXPR(ArrayIndex < ArraySize); if (DescriptorSet < ResourceCache.GetNumDescriptorSets()) { @@ -1055,21 +1275,21 @@ void ShaderResourceLayoutVk::InitializeStaticResources(const ShaderResourceLayou // Get resource attributes const auto& DstRes = GetResource(SHADER_RESOURCE_VARIABLE_TYPE_STATIC, r); const auto& SrcRes = SrcLayout.GetResource(SHADER_RESOURCE_VARIABLE_TYPE_STATIC, r); - VERIFY(SrcRes.Binding == SrcRes.SpirvAttribs.Type, "Unexpected binding"); - VERIFY(SrcRes.SpirvAttribs.ArraySize == DstRes.SpirvAttribs.ArraySize, "Inconsistent array size"); + VERIFY(SrcRes.Binding == Uint32{SrcRes.Type}, "Unexpected binding"); + VERIFY(SrcRes.ArraySize == DstRes.ArraySize, "Inconsistent array size"); - if (DstRes.SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && + if (DstRes.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && DstRes.IsImmutableSamplerAssigned()) continue; // Skip immutable samplers - for (Uint32 ArrInd = 0; ArrInd < DstRes.SpirvAttribs.ArraySize; ++ArrInd) + 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<IDeviceObject>(); if (!pObject) - LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", SrcRes.SpirvAttribs.GetPrintName(ArrInd), "' in shader '", GetShaderName(), "'."); + 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; @@ -1093,15 +1313,15 @@ bool ShaderResourceLayoutVk::dvpVerifyBindings(const ShaderResourceCacheVk& Reso { const auto& Res = GetResource(VarType, r); VERIFY(Res.GetVariableType() == VarType, "Unexpected variable type"); - for (Uint32 ArrInd = 0; ArrInd < Res.SpirvAttribs.ArraySize; ++ArrInd) + 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.SpirvAttribs.Type, "Inconsistent types"); + VERIFY(CachedRes.Type == Res.Type, "Inconsistent types"); if (CachedRes.pObject == nullptr && - !(Res.SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && Res.IsImmutableSamplerAssigned())) + !(Res.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && Res.IsImmutableSamplerAssigned())) { - LOG_ERROR_MESSAGE("No resource is bound to ", GetShaderVariableTypeLiteralName(Res.GetVariableType()), " variable '", Res.SpirvAttribs.GetPrintName(ArrInd), "' in shader '", GetShaderName(), "'"); + LOG_ERROR_MESSAGE("No resource is bound to ", GetShaderVariableTypeLiteralName(Res.GetVariableType()), " variable '", Res.GetPrintName(ArrInd), "' in shader '", GetShaderName(), "'"); BindingsOK = false; } # ifdef DILIGENT_DEBUG @@ -1136,7 +1356,7 @@ void ShaderResourceLayoutVk::InitializeResourceMemoryInCache(ShaderResourceCache for (Uint32 r = 0; r < TotalResources; ++r) { const auto& Res = GetResource(r); - ResourceCache.InitializeResources(Res.DescriptorSet, Res.CacheOffset, Res.SpirvAttribs.ArraySize, Res.SpirvAttribs.Type); + ResourceCache.InitializeResources(Res.DescriptorSet, Res.CacheOffset, Res.ArraySize, Res.Type); } } @@ -1151,24 +1371,28 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& 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<VkDescriptorImageInfo, ImgUpdateBatchSize> DescrImgInfoArr; - std::array<VkDescriptorBufferInfo, BuffUpdateBatchSize> DescrBuffInfoArr; - std::array<VkBufferView, TexelBuffUpdateBatchSize> DescrBuffViewArr; - std::array<VkWriteDescriptorSet, WriteDescriptorSetBatchSize> WriteDescrSetArr; + std::array<VkDescriptorImageInfo, ImgUpdateBatchSize> DescrImgInfoArr; + std::array<VkDescriptorBufferInfo, BuffUpdateBatchSize> DescrBuffInfoArr; + std::array<VkBufferView, TexelBuffUpdateBatchSize> DescrBuffViewArr; + std::array<VkWriteDescriptorSetAccelerationStructureKHR, AccelStructBatchSize> DescrAccelStructArr; + std::array<VkWriteDescriptorSet, WriteDescriptorSetBatchSize> 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 @@ -1195,14 +1419,15 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& 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.SpirvAttribs); + WriteDescrSetIt->descriptorType = PipelineLayout::GetVkDescriptorType(Res.Type); // For every resource type, try to batch as many descriptor updates as we can - switch (Res.SpirvAttribs.Type) + 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.SpirvAttribs.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) + while (ArrElem < Res.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) { const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); *DescrBuffIt = CachedRes.GetUniformBufferDescriptorWriteInfo(); @@ -1214,7 +1439,7 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& case SPIRVShaderResourceAttribs::ResourceType::ROStorageBuffer: case SPIRVShaderResourceAttribs::ResourceType::RWStorageBuffer: WriteDescrSetIt->pBufferInfo = &(*DescrBuffIt); - while (ArrElem < Res.SpirvAttribs.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) + while (ArrElem < Res.ArraySize && DescrBuffIt != DescrBuffInfoArr.end()) { const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); *DescrBuffIt = CachedRes.GetStorageBufferDescriptorWriteInfo(); @@ -1226,7 +1451,7 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& case SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer: case SPIRVShaderResourceAttribs::ResourceType::StorageTexelBuffer: WriteDescrSetIt->pTexelBufferView = &(*BuffViewIt); - while (ArrElem < Res.SpirvAttribs.ArraySize && BuffViewIt != DescrBuffViewArr.end()) + while (ArrElem < Res.ArraySize && BuffViewIt != DescrBuffViewArr.end()) { const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); *BuffViewIt = CachedRes.GetBufferViewWriteInfo(); @@ -1239,7 +1464,7 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& case SPIRVShaderResourceAttribs::ResourceType::StorageImage: case SPIRVShaderResourceAttribs::ResourceType::SampledImage: WriteDescrSetIt->pImageInfo = &(*DescrImgIt); - while (ArrElem < Res.SpirvAttribs.ArraySize && DescrImgIt != DescrImgInfoArr.end()) + while (ArrElem < Res.ArraySize && DescrImgIt != DescrImgInfoArr.end()) { const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); *DescrImgIt = CachedRes.GetImageDescriptorWriteInfo(Res.IsImmutableSamplerAssigned()); @@ -1259,7 +1484,7 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& if (!Res.IsImmutableSamplerAssigned()) { WriteDescrSetIt->pImageInfo = &(*DescrImgIt); - while (ArrElem < Res.SpirvAttribs.ArraySize && DescrImgIt != DescrImgInfoArr.end()) + while (ArrElem < Res.ArraySize && DescrImgIt != DescrImgInfoArr.end()) { const auto& CachedRes = SetResources.GetResource(Res.CacheOffset + ArrElem); *DescrImgIt = CachedRes.GetSamplerDescriptorWriteInfo(); @@ -1269,8 +1494,19 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& } else { - ArrElem = Res.SpirvAttribs.ArraySize; - WriteDescrSetIt->dstArrayElement = Res.SpirvAttribs.ArraySize; + 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; @@ -1279,7 +1515,7 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& } WriteDescrSetIt->descriptorCount = ArrElem - WriteDescrSetIt->dstArrayElement; - if (ArrElem == Res.SpirvAttribs.ArraySize) + if (ArrElem == Res.ArraySize) { ArrElem = 0; ++ResNum; @@ -1294,6 +1530,7 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& DescrImgIt == DescrImgInfoArr.end() || DescrBuffIt == DescrBuffInfoArr.end() || BuffViewIt == DescrBuffViewArr.end() || + AccelStructIt == DescrAccelStructArr.end() || WriteDescrSetIt == WriteDescrSetArr.end()) { auto DescrWriteCount = static_cast<Uint32>(std::distance(WriteDescrSetArr.begin(), WriteDescrSetIt)); @@ -1303,9 +1540,27 @@ void ShaderResourceLayoutVk::CommitDynamicResources(const ShaderResourceCacheVk& 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 7e06bd69..062a5725 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableVk.cpp @@ -52,7 +52,7 @@ size_t ShaderVariableManagerVk::GetRequiredMemorySize(const ShaderResourceLayout // When using HLSL-style combined image samplers, we need to skip separate samplers. // Also always skip immutable separate samplers. - if (SrcRes.SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && + if (SrcRes.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && (!UsingSeparateSamplers || SrcRes.IsImmutableSamplerAssigned())) continue; @@ -96,7 +96,7 @@ void ShaderVariableManagerVk::Initialize(const ShaderResourceLayoutVk& Sr { 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.SpirvAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && + if (SrcRes.Type == SPIRVShaderResourceAttribs::ResourceType::SeparateSampler && (!UsingSeparateSamplers || SrcRes.IsImmutableSamplerAssigned())) continue; @@ -132,7 +132,7 @@ ShaderVariableVkImpl* ShaderVariableManagerVk::GetVariable(const Char* Name) con { auto& Var = m_pVariables[v]; const auto& Res = Var.m_Resource; - if (strcmp(Res.SpirvAttribs.Name, Name) == 0) + if (strcmp(Res.Name, Name) == 0) { pVar = &Var; break; @@ -190,18 +190,18 @@ void ShaderVariableManagerVk::BindResources(IResourceMapping* pResourceMapping, const auto& Res = Var.m_Resource; // There should be no immutable separate samplers - VERIFY(Res.SpirvAttribs.Type != SPIRVShaderResourceAttribs::ResourceType::SeparateSampler || !Res.IsImmutableSamplerAssigned(), + VERIFY(Res.Type != SPIRVShaderResourceAttribs::ResourceType::SeparateSampler || !Res.IsImmutableSamplerAssigned(), "There must be no shader resource variables for immutable separate samplers"); if ((Flags & (1 << Res.GetVariableType())) == 0) continue; - for (Uint32 ArrInd = 0; ArrInd < Res.SpirvAttribs.ArraySize; ++ArrInd) + for (Uint32 ArrInd = 0; ArrInd < Res.ArraySize; ++ArrInd) { if ((Flags & BIND_SHADER_RESOURCES_KEEP_EXISTING) && Res.IsBound(ArrInd, m_ResourceCache)) continue; - const auto* VarName = Res.SpirvAttribs.Name; + const auto* VarName = Res.Name; RefCntAutoPtr<IDeviceObject> pObj; pResourceMapping->GetResource(VarName, &pObj, ArrInd); if (pObj) @@ -212,7 +212,7 @@ void ShaderVariableManagerVk::BindResources(IResourceMapping* pResourceMapping, { if ((Flags & BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED) && !Res.IsBound(ArrInd, m_ResourceCache)) { - LOG_ERROR_MESSAGE("Unable to bind resource to shader variable '", Res.SpirvAttribs.GetPrintName(ArrInd), + LOG_ERROR_MESSAGE("Unable to bind resource to shader variable '", Res.GetPrintName(ArrInd), "': resource is not found in the resource mapping. " "Do not use BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED flag to suppress the message if this is not an issue."); } diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp index 65505ad3..66393e9b 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp @@ -122,9 +122,17 @@ ShaderVkImpl::ShaderVkImpl(IReferenceCounters* pRefCounters, SourceLength = GLSLSourceString.length(); } + GLSLangUtils::SpirvVersion spvVersion = GLSLangUtils::SpirvVersion::Vk100; + const auto& ExtFeats = GetDevice()->GetLogicalDevice().GetEnabledExtFeatures(); + if (ExtFeats.Spirv15) + spvVersion = GLSLangUtils::SpirvVersion::Vk120; + else if (ExtFeats.Spirv14) + spvVersion = GLSLangUtils::SpirvVersion::Vk110_Spirv14; + m_SPIRV = GLSLangUtils::GLSLtoSPIRV(m_Desc.ShaderType, ShaderSource, static_cast<int>(SourceLength), Macros, ShaderCI.pShaderSourceStreamFactory, + spvVersion, ShaderCI.ppCompilerOutput); } #endif diff --git a/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp index ab53d639..b5f7e041 100644 --- a/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp @@ -423,6 +423,7 @@ void SwapChainVkImpl::CreateVulkanSwapChain() swapchain_ci.imageColorSpace = ColorSpace; DEV_CHECK_ERR(m_SwapChainDesc.Usage != 0, "No swap chain usage flags defined"); + static_assert(SWAP_CHAIN_USAGE_LAST == SWAP_CHAIN_USAGE_COPY_SOURCE, "Please update this function to handle the new swapchain usage"); if (m_SwapChainDesc.Usage & SWAP_CHAIN_USAGE_RENDER_TARGET) swapchain_ci.imageUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; if (m_SwapChainDesc.Usage & SWAP_CHAIN_USAGE_SHADER_INPUT) @@ -639,9 +640,10 @@ VkResult SwapChainVkImpl::AcquireNextImage(DeviceContextVkImpl* pDeviceCtxVk) m_ImageAcquiredFenceSubmitted[m_SemaphoreIndex] = (res == VK_SUCCESS); if (res == VK_SUCCESS) { - // Next command in the device context must wait for the next image to be acquired - // Unlike fences or events, the act of waiting for a semaphore also unsignals that semaphore (6.4.2) - pDeviceCtxVk->AddWaitSemaphore(m_ImageAcquiredSemaphores[m_SemaphoreIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + // Next command in the device context must wait for the next image to be acquired. + // Unlike fences or events, the act of waiting for a semaphore also unsignals that semaphore (6.4.2). + // Swapchain image may be used as render target or as destination for copy command. + pDeviceCtxVk->AddWaitSemaphore(m_ImageAcquiredSemaphores[m_SemaphoreIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT); if (!m_SwapChainImagesInitialized[m_BackBufferIndex]) { // Vulkan validation layers do not like uninitialized memory. diff --git a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp index 488e4bf6..0fb3dd79 100644 --- a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp @@ -223,13 +223,13 @@ TextureVkImpl::TextureVkImpl(IReferenceCounters* pRefCounters, // For either clear or copy command, dst layout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL VkImageSubresourceRange SubresRange; - SubresRange.aspectMask = aspectMask; - SubresRange.baseArrayLayer = 0; - SubresRange.layerCount = VK_REMAINING_ARRAY_LAYERS; - SubresRange.baseMipLevel = 0; - SubresRange.levelCount = VK_REMAINING_MIP_LEVELS; - auto EnabledGraphicsShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages(); - VulkanUtilities::VulkanCommandBuffer::TransitionImageLayout(vkCmdBuff, m_VulkanImage, ImageCI.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, SubresRange, EnabledGraphicsShaderStages); + SubresRange.aspectMask = aspectMask; + SubresRange.baseArrayLayer = 0; + SubresRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + SubresRange.baseMipLevel = 0; + SubresRange.levelCount = VK_REMAINING_MIP_LEVELS; + auto EnabledShaderStages = LogicalDevice.GetEnabledShaderStages(); + VulkanUtilities::VulkanCommandBuffer::TransitionImageLayout(vkCmdBuff, m_VulkanImage, ImageCI.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, SubresRange, EnabledShaderStages); SetState(RESOURCE_STATE_COPY_DEST); const auto CurrentLayout = GetLayout(); VERIFY_EXPR(CurrentLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); @@ -350,7 +350,7 @@ TextureVkImpl::TextureVkImpl(IReferenceCounters* pRefCounters, err = LogicalDevice.BindBufferMemory(StagingBuffer, StagingBufferMemory, AlignedStagingMemOffset); CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory"); - VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledGraphicsShaderStages); + VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledShaderStages); // Copy commands MUST be recorded outside of a render pass instance. This is OK here // as copy will be the only command in the cmd buffer @@ -498,7 +498,7 @@ void TextureVkImpl::CreateViewInternal(const TextureViewDesc& ViewDesc, ITexture VERIFY(&TexViewAllocator == &m_dbgTexViewObjAllocator, "Texture view allocator does not match allocator provided during texture initialization"); auto UpdatedViewDesc = ViewDesc; - CorrectTextureViewDesc(UpdatedViewDesc); + ValidatedAndCorrectTextureViewDesc(m_Desc, UpdatedViewDesc); VulkanUtilities::ImageViewWrapper ImgView = CreateImageView(UpdatedViewDesc); auto pViewVk = NEW_RC_OBJ(TexViewAllocator, "TextureViewVkImpl instance", TextureViewVkImpl, bIsDefaultView ? this : nullptr)(GetDevice(), UpdatedViewDesc, this, std::move(ImgView), bIsDefaultView); diff --git a/Graphics/GraphicsEngineVulkan/src/TopLevelASVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/TopLevelASVkImpl.cpp new file mode 100644 index 00000000..03724a8a --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/TopLevelASVkImpl.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2019-2020 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 "TopLevelASVkImpl.hpp" +#include "VulkanTypeConversions.hpp" + +namespace Diligent +{ + +TopLevelASVkImpl::TopLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const TopLevelASDesc& Desc) : + TTopLevelASBase{pRefCounters, pRenderDeviceVk, Desc} +{ + const auto& LogicalDevice = pRenderDeviceVk->GetLogicalDevice(); + const auto& PhysicalDevice = pRenderDeviceVk->GetPhysicalDevice(); + const auto& Limits = PhysicalDevice.GetExtProperties().AccelStruct; + Uint32 AccelStructSize = m_Desc.CompactedSize; + + if (AccelStructSize == 0) + { + VkAccelerationStructureBuildGeometryInfoKHR vkBuildInfo = {}; + VkAccelerationStructureBuildSizesInfoKHR vkSizeInfo = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR}; + VkAccelerationStructureGeometryKHR vkGeometry = {}; + VkAccelerationStructureGeometryInstancesDataKHR& vkInstances = vkGeometry.geometry.instances; + const uint32_t MaxPrimitiveCount = m_Desc.MaxInstanceCount; + + vkGeometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + vkGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; + vkInstances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR; + vkInstances.arrayOfPointers = VK_FALSE; + + vkBuildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + vkBuildInfo.flags = BuildASFlagsToVkBuildAccelerationStructureFlags(m_Desc.Flags); + vkBuildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + vkBuildInfo.pGeometries = &vkGeometry; + vkBuildInfo.geometryCount = 1; + + VERIFY_EXPR(MaxPrimitiveCount <= Limits.maxInstanceCount); + + LogicalDevice.GetAccelerationStructureBuildSizes(vkBuildInfo, &MaxPrimitiveCount, vkSizeInfo); + + AccelStructSize = static_cast<Uint32>(vkSizeInfo.accelerationStructureSize); + m_ScratchSize.Build = static_cast<Uint32>(vkSizeInfo.buildScratchSize); + m_ScratchSize.Update = static_cast<Uint32>(vkSizeInfo.updateScratchSize); + } + + VkBufferCreateInfo vkBuffCI = {}; + + vkBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + vkBuffCI.flags = 0; + vkBuffCI.size = AccelStructSize; + vkBuffCI.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR; + vkBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vkBuffCI.queueFamilyIndexCount = 0; + vkBuffCI.pQueueFamilyIndices = nullptr; + + m_VulkanBuffer = LogicalDevice.CreateBuffer(vkBuffCI, m_Desc.Name); + + VkMemoryRequirements MemReqs = LogicalDevice.GetBufferMemoryRequirements(m_VulkanBuffer); + uint32_t MemoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VERIFY(IsPowerOfTwo(MemReqs.alignment), "Alignment is not power of 2!"); + m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT); + + m_MemoryAlignedOffset = Align(VkDeviceSize{m_MemoryAllocation.UnalignedOffset}, MemReqs.alignment); + VERIFY(m_MemoryAllocation.Size >= MemReqs.size + (m_MemoryAlignedOffset - m_MemoryAllocation.UnalignedOffset), "Size of memory allocation is too small"); + auto Memory = m_MemoryAllocation.Page->GetVkMemory(); + auto err = LogicalDevice.BindBufferMemory(m_VulkanBuffer, Memory, m_MemoryAlignedOffset); + CHECK_VK_ERROR_AND_THROW(err, "Failed to bind buffer memory"); + + VkAccelerationStructureCreateInfoKHR vkAccelStrCI = {}; + + vkAccelStrCI.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + vkAccelStrCI.createFlags = 0; + vkAccelStrCI.buffer = m_VulkanBuffer; + vkAccelStrCI.offset = 0; + vkAccelStrCI.size = AccelStructSize; + vkAccelStrCI.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + + m_VulkanTLAS = LogicalDevice.CreateAccelStruct(vkAccelStrCI, m_Desc.Name); + + m_DeviceAddress = LogicalDevice.GetAccelerationStructureDeviceAddress(m_VulkanTLAS); + + SetState(RESOURCE_STATE_BUILD_AS_READ); +} + +TopLevelASVkImpl::TopLevelASVkImpl(IReferenceCounters* pRefCounters, + RenderDeviceVkImpl* pRenderDeviceVk, + const TopLevelASDesc& Desc, + RESOURCE_STATE InitialState, + VkAccelerationStructureKHR vkTLAS) : + TTopLevelASBase{pRefCounters, pRenderDeviceVk, Desc}, + m_VulkanTLAS{vkTLAS} +{ + SetState(InitialState); + m_DeviceAddress = pRenderDeviceVk->GetLogicalDevice().GetAccelerationStructureDeviceAddress(m_VulkanTLAS); +} + +TopLevelASVkImpl::~TopLevelASVkImpl() +{ + // Vk object can only be destroyed when it is no longer used by the GPU + if (m_VulkanTLAS != VK_NULL_HANDLE) + m_pDevice->SafeReleaseDeviceObject(std::move(m_VulkanTLAS), m_Desc.CommandQueueMask); + if (m_VulkanBuffer != VK_NULL_HANDLE) + m_pDevice->SafeReleaseDeviceObject(std::move(m_VulkanBuffer), m_Desc.CommandQueueMask); + if (m_MemoryAllocation.Page != nullptr) + m_pDevice->SafeReleaseDeviceObject(std::move(m_MemoryAllocation), m_Desc.CommandQueueMask); +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp index 55bb7d8f..17c129c1 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp @@ -632,6 +632,21 @@ VkFormat TypeToVkFormat(VALUE_TYPE ValType, Uint32 NumComponents, Bool bIsNormal } } +VkIndexType TypeToVkIndexType(VALUE_TYPE IndexType) +{ + switch (IndexType) + { + // clang-format off + case VT_UNDEFINED: return VK_INDEX_TYPE_NONE_KHR; // only for ray tracing + case VT_UINT16: return VK_INDEX_TYPE_UINT16; + case VT_UINT32: return VK_INDEX_TYPE_UINT32; + // clang-format on + default: + UNEXPECTED("Unexpected index type"); + return VK_INDEX_TYPE_UINT32; + } +} + VkPolygonMode FillModeToVkPolygonMode(FILL_MODE FillMode) { switch (FillMode) @@ -1137,6 +1152,56 @@ VkBorderColor BorderColorToVkBorderColor(const Float32 BorderColor[]) } +static VkPipelineStageFlags ResourceStateFlagToVkPipelineStage(RESOURCE_STATE StateFlag, VkPipelineStageFlags ShaderStages) +{ + static_assert(RESOURCE_STATE_MAX_BIT == RESOURCE_STATE_RAY_TRACING, "This function must be updated to handle new resource state flag"); + VERIFY((StateFlag & (StateFlag - 1)) == 0, "Only single bit must be set"); + switch (StateFlag) + { + // clang-format off + case RESOURCE_STATE_UNDEFINED: return 0; + case RESOURCE_STATE_VERTEX_BUFFER: return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + case RESOURCE_STATE_CONSTANT_BUFFER: return ShaderStages; + case RESOURCE_STATE_INDEX_BUFFER: return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + case RESOURCE_STATE_RENDER_TARGET: return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + case RESOURCE_STATE_UNORDERED_ACCESS: return ShaderStages; + case RESOURCE_STATE_DEPTH_WRITE: return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + case RESOURCE_STATE_DEPTH_READ: return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + case RESOURCE_STATE_SHADER_RESOURCE: return ShaderStages; + case RESOURCE_STATE_STREAM_OUT: return 0; + case RESOURCE_STATE_INDIRECT_ARGUMENT: return VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + case RESOURCE_STATE_COPY_DEST: return VK_PIPELINE_STAGE_TRANSFER_BIT; + case RESOURCE_STATE_COPY_SOURCE: return VK_PIPELINE_STAGE_TRANSFER_BIT; + case RESOURCE_STATE_RESOLVE_DEST: return VK_PIPELINE_STAGE_TRANSFER_BIT; + case RESOURCE_STATE_RESOLVE_SOURCE: return VK_PIPELINE_STAGE_TRANSFER_BIT; + case RESOURCE_STATE_INPUT_ATTACHMENT: return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + case RESOURCE_STATE_PRESENT: return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + case RESOURCE_STATE_BUILD_AS_READ: return VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; + case RESOURCE_STATE_BUILD_AS_WRITE: return VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; + case RESOURCE_STATE_RAY_TRACING: return VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR; + // clang-format on + + default: + UNEXPECTED("Unexpected resource state flag"); + return 0; + } +} + +VkPipelineStageFlags ResourceStateFlagsToVkPipelineStageFlags(RESOURCE_STATE StateFlags, VkPipelineStageFlags vkShaderStages) +{ + VERIFY(Uint32{StateFlags} < (RESOURCE_STATE_MAX_BIT << 1), "Resource state flags are out of range"); + + VkPipelineStageFlags vkPipelineStages = 0; + while (StateFlags != RESOURCE_STATE_UNKNOWN) + { + auto StateBit = static_cast<RESOURCE_STATE>(1 << PlatformMisc::GetLSB(Uint32{StateFlags})); + vkPipelineStages |= ResourceStateFlagToVkPipelineStage(StateBit, vkShaderStages); + StateFlags &= ~StateBit; + } + return vkPipelineStages; +} + + static VkAccessFlags ResourceStateFlagToVkAccessFlags(RESOURCE_STATE StateFlag) { // Currently not used: @@ -1151,10 +1216,8 @@ static VkAccessFlags ResourceStateFlagToVkAccessFlags(RESOURCE_STATE StateFlag) //VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX //VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT //VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV - //VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NVX - //VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NVX - static_assert(RESOURCE_STATE_MAX_BIT == 0x10000, "This function must be updated to handle new resource state flag"); + static_assert(RESOURCE_STATE_MAX_BIT == RESOURCE_STATE_RAY_TRACING, "This function must be updated to handle new resource state flag"); VERIFY((StateFlag & (StateFlag - 1)) == 0, "Only single bit must be set"); switch (StateFlag) { @@ -1176,6 +1239,9 @@ static VkAccessFlags ResourceStateFlagToVkAccessFlags(RESOURCE_STATE StateFlag) case RESOURCE_STATE_RESOLVE_SOURCE: return VK_ACCESS_TRANSFER_READ_BIT; case RESOURCE_STATE_INPUT_ATTACHMENT: return VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; case RESOURCE_STATE_PRESENT: return 0; + case RESOURCE_STATE_BUILD_AS_READ: return VK_ACCESS_SHADER_READ_BIT; // for vertex, index, transform, AABB, instance buffers + case RESOURCE_STATE_BUILD_AS_WRITE: return VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; // for scratch buffer + case RESOURCE_STATE_RAY_TRACING: return VK_ACCESS_SHADER_READ_BIT; // for SBT // clang-format on default: @@ -1190,7 +1256,7 @@ public: StateFlagBitPosToVkAccessFlags() { static_assert((1 << MaxFlagBitPos) == RESOURCE_STATE_MAX_BIT, "This function must be updated to handle new resource state flag"); - for (Uint32 bit = 0; bit < MaxFlagBitPos; ++bit) + for (Uint32 bit = 0; bit < FlagBitPosToVkAccessFlagsMap.size(); ++bit) { FlagBitPosToVkAccessFlagsMap[bit] = ResourceStateFlagToVkAccessFlags(static_cast<RESOURCE_STATE>(1 << bit)); } @@ -1203,7 +1269,7 @@ public: } private: - static constexpr const Uint32 MaxFlagBitPos = 16; + static constexpr const Uint32 MaxFlagBitPos = 19; std::array<VkAccessFlags, MaxFlagBitPos + 1> FlagBitPosToVkAccessFlagsMap; }; @@ -1224,7 +1290,31 @@ VkAccessFlags ResourceStateFlagsToVkAccessFlags(RESOURCE_STATE StateFlags) return AccessFlags; } -RESOURCE_STATE VkAccessFlagsToResourceStates(VkAccessFlagBits AccessFlagBit) +VkAccessFlags AccelStructStateFlagsToVkAccessFlags(RESOURCE_STATE StateFlags) +{ + VERIFY(Uint32{StateFlags} < (RESOURCE_STATE_MAX_BIT << 1), "Resource state flags are out of range"); + static_assert(RESOURCE_STATE_MAX_BIT == RESOURCE_STATE_RAY_TRACING, "This function must be updated to handle new resource state flag"); + + VkAccessFlags AccessFlags = 0; + Uint32 Bits = StateFlags; + while (Bits != 0) + { + auto Bit = static_cast<RESOURCE_STATE>(1 << PlatformMisc::GetLSB(Bits)); + switch (Bit) + { + // clang-format off + case RESOURCE_STATE_BUILD_AS_READ: AccessFlags |= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR; break; + case RESOURCE_STATE_BUILD_AS_WRITE: AccessFlags |= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; break; + case RESOURCE_STATE_RAY_TRACING: AccessFlags |= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR; break; + default: UNEXPECTED("Unexpected resource state flag"); + // clang-format on + } + Bits &= ~Bit; + } + return AccessFlags; +} + +static RESOURCE_STATE VkAccessFlagToResourceStates(VkAccessFlagBits AccessFlagBit) { VERIFY((AccessFlagBit & (AccessFlagBit - 1)) == 0, "Single access flag bit is expected"); @@ -1236,7 +1326,7 @@ RESOURCE_STATE VkAccessFlagsToResourceStates(VkAccessFlagBits AccessFlagBit) case VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT: return RESOURCE_STATE_VERTEX_BUFFER; case VK_ACCESS_UNIFORM_READ_BIT: return RESOURCE_STATE_CONSTANT_BUFFER; case VK_ACCESS_INPUT_ATTACHMENT_READ_BIT: return RESOURCE_STATE_INPUT_ATTACHMENT; - case VK_ACCESS_SHADER_READ_BIT: return RESOURCE_STATE_SHADER_RESOURCE; + case VK_ACCESS_SHADER_READ_BIT: return RESOURCE_STATE_SHADER_RESOURCE; // or RESOURCE_STATE_BUILD_AS_READ case VK_ACCESS_SHADER_WRITE_BIT: return RESOURCE_STATE_UNORDERED_ACCESS; case VK_ACCESS_COLOR_ATTACHMENT_READ_BIT: return RESOURCE_STATE_RENDER_TARGET; case VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT: return RESOURCE_STATE_RENDER_TARGET; @@ -1244,6 +1334,7 @@ RESOURCE_STATE VkAccessFlagsToResourceStates(VkAccessFlagBits AccessFlagBit) case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: return RESOURCE_STATE_DEPTH_WRITE; case VK_ACCESS_TRANSFER_READ_BIT: return RESOURCE_STATE_COPY_SOURCE; case VK_ACCESS_TRANSFER_WRITE_BIT: return RESOURCE_STATE_COPY_DEST; + case VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR: return RESOURCE_STATE_BUILD_AS_WRITE; case VK_ACCESS_HOST_READ_BIT: return RESOURCE_STATE_UNKNOWN; case VK_ACCESS_HOST_WRITE_BIT: return RESOURCE_STATE_UNKNOWN; case VK_ACCESS_MEMORY_READ_BIT: return RESOURCE_STATE_UNKNOWN; @@ -1256,8 +1347,6 @@ RESOURCE_STATE VkAccessFlagsToResourceStates(VkAccessFlagBits AccessFlagBit) case VK_ACCESS_COMMAND_PREPROCESS_WRITE_BIT_NV: return RESOURCE_STATE_UNKNOWN; case VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT: return RESOURCE_STATE_UNKNOWN; case VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV: return RESOURCE_STATE_UNKNOWN; - case VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV: return RESOURCE_STATE_UNKNOWN; - case VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV: return RESOURCE_STATE_UNKNOWN; // clang-format on default: UNEXPECTED("Unknown access flag"); @@ -1271,9 +1360,9 @@ class VkAccessFlagBitPosToResourceState public: VkAccessFlagBitPosToResourceState() { - for (Uint32 bit = 0; bit < MaxFlagBitPos; ++bit) + for (Uint32 bit = 0; bit < FlagBitPosToResourceState.size(); ++bit) { - FlagBitPosToResourceState[bit] = VkAccessFlagsToResourceStates(static_cast<VkAccessFlagBits>(1 << bit)); + FlagBitPosToResourceState[bit] = VkAccessFlagToResourceStates(static_cast<VkAccessFlagBits>(1 << bit)); } } @@ -1317,7 +1406,7 @@ VkImageLayout ResourceStateToVkImageLayout(RESOURCE_STATE StateFlag, bool IsInsi //VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, //VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, - static_assert(RESOURCE_STATE_MAX_BIT == 0x10000, "This function must be updated to handle new resource state flag"); + static_assert(RESOURCE_STATE_MAX_BIT == RESOURCE_STATE_RAY_TRACING, "This function must be updated to handle new resource state flag"); VERIFY((StateFlag & (StateFlag - 1)) == 0, "Only single bit must be set"); switch (StateFlag) { @@ -1339,6 +1428,9 @@ VkImageLayout ResourceStateToVkImageLayout(RESOURCE_STATE StateFlag, bool IsInsi case RESOURCE_STATE_RESOLVE_SOURCE: return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; case RESOURCE_STATE_INPUT_ATTACHMENT: return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; case RESOURCE_STATE_PRESENT: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + case RESOURCE_STATE_BUILD_AS_READ: UNEXPECTED("Invalid resource state"); return VK_IMAGE_LAYOUT_UNDEFINED; + case RESOURCE_STATE_BUILD_AS_WRITE: UNEXPECTED("Invalid resource state"); return VK_IMAGE_LAYOUT_UNDEFINED; + case RESOURCE_STATE_RAY_TRACING: UNEXPECTED("Invalid resource state"); return VK_IMAGE_LAYOUT_UNDEFINED; // clang-format on default: @@ -1349,7 +1441,7 @@ VkImageLayout ResourceStateToVkImageLayout(RESOURCE_STATE StateFlag, bool IsInsi RESOURCE_STATE VkImageLayoutToResourceState(VkImageLayout Layout) { - static_assert(RESOURCE_STATE_MAX_BIT == 0x10000, "This function must be updated to handle new resource state flag"); + static_assert(RESOURCE_STATE_MAX_BIT == RESOURCE_STATE_RAY_TRACING, "This function must be updated to handle new resource state flag"); switch (Layout) { // clang-format off @@ -1514,7 +1606,7 @@ VkAccessFlags AccessFlagsToVkAccessFlags(ACCESS_FLAGS AccessFlags) VkShaderStageFlagBits ShaderTypeToVkShaderStageFlagBit(SHADER_TYPE ShaderType) { - static_assert(SHADER_TYPE_LAST == SHADER_TYPE_MESH, "Please update the switch below to handle the new shader type"); + static_assert(SHADER_TYPE_LAST == SHADER_TYPE_CALLABLE, "Please update the switch below to handle the new shader type"); VERIFY(IsPowerOfTwo(Uint32{ShaderType}), "More than one shader type is specified"); switch (ShaderType) { @@ -1527,6 +1619,12 @@ VkShaderStageFlagBits ShaderTypeToVkShaderStageFlagBit(SHADER_TYPE ShaderType) case SHADER_TYPE_COMPUTE: return VK_SHADER_STAGE_COMPUTE_BIT; case SHADER_TYPE_AMPLIFICATION: return VK_SHADER_STAGE_TASK_BIT_NV; case SHADER_TYPE_MESH: return VK_SHADER_STAGE_MESH_BIT_NV; + case SHADER_TYPE_RAY_GEN: return VK_SHADER_STAGE_RAYGEN_BIT_KHR; + case SHADER_TYPE_RAY_MISS: return VK_SHADER_STAGE_MISS_BIT_KHR; + case SHADER_TYPE_RAY_CLOSEST_HIT: return VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR; + case SHADER_TYPE_RAY_ANY_HIT: return VK_SHADER_STAGE_ANY_HIT_BIT_KHR; + case SHADER_TYPE_RAY_INTERSECTION: return VK_SHADER_STAGE_INTERSECTION_BIT_KHR; + case SHADER_TYPE_CALLABLE: return VK_SHADER_STAGE_CALLABLE_BIT_KHR; // clang-format on default: UNEXPECTED("Unknown shader type"); @@ -1534,4 +1632,92 @@ VkShaderStageFlagBits ShaderTypeToVkShaderStageFlagBit(SHADER_TYPE ShaderType) } } +VkBuildAccelerationStructureFlagsKHR BuildASFlagsToVkBuildAccelerationStructureFlags(RAYTRACING_BUILD_AS_FLAGS Flags) +{ + static_assert(RAYTRACING_BUILD_AS_FLAGS_LAST == RAYTRACING_BUILD_AS_LOW_MEMORY, + "Please update the switch below to handle the new ray tracing build flag"); + + VkBuildAccelerationStructureFlagsKHR Result = 0; + while (Flags != RAYTRACING_BUILD_AS_NONE) + { + auto FlagBit = static_cast<RAYTRACING_BUILD_AS_FLAGS>(1 << PlatformMisc::GetLSB(Uint32{Flags})); + switch (FlagBit) + { + // clang-format off + case RAYTRACING_BUILD_AS_ALLOW_UPDATE: Result |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR; break; + case RAYTRACING_BUILD_AS_ALLOW_COMPACTION: Result |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR; break; + case RAYTRACING_BUILD_AS_PREFER_FAST_TRACE: Result |= VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; break; + case RAYTRACING_BUILD_AS_PREFER_FAST_BUILD: Result |= VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR; break; + case RAYTRACING_BUILD_AS_LOW_MEMORY: Result |= VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_KHR; break; + // clang-format on + default: UNEXPECTED("unknown build AS flag"); + } + Flags = Flags & ~FlagBit; + } + return Result; +} + +VkGeometryFlagsKHR GeometryFlagsToVkGeometryFlags(RAYTRACING_GEOMETRY_FLAGS Flags) +{ + static_assert(RAYTRACING_GEOMETRY_FLAGS_LAST == RAYTRACING_GEOMETRY_FLAG_NO_DUPLICATE_ANY_HIT_INVOCATION, + "Please update the switch below to handle the new ray tracing geometry flag"); + + VkGeometryFlagsKHR Result = 0; + while (Flags != RAYTRACING_GEOMETRY_FLAG_NONE) + { + auto FlagBit = static_cast<RAYTRACING_GEOMETRY_FLAGS>(1 << PlatformMisc::GetLSB(Uint32{Flags})); + switch (FlagBit) + { + // clang-format off + case RAYTRACING_GEOMETRY_FLAG_OPAQUE: Result |= VK_GEOMETRY_OPAQUE_BIT_KHR; break; + case RAYTRACING_GEOMETRY_FLAG_NO_DUPLICATE_ANY_HIT_INVOCATION: Result |= VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR; break; + // clang-format on + default: UNEXPECTED("unknown geometry flag"); + } + Flags = Flags & ~FlagBit; + } + return Result; +} + +VkGeometryInstanceFlagsKHR InstanceFlagsToVkGeometryInstanceFlags(RAYTRACING_INSTANCE_FLAGS Flags) +{ + static_assert(RAYTRACING_INSTANCE_FLAGS_LAST == RAYTRACING_INSTANCE_FORCE_NO_OPAQUE, + "Please update the switch below to handle the new ray tracing instance flag"); + + VkGeometryInstanceFlagsKHR Result = 0; + while (Flags != RAYTRACING_INSTANCE_NONE) + { + auto FlagBit = static_cast<RAYTRACING_INSTANCE_FLAGS>(1 << PlatformMisc::GetLSB(Uint32{Flags})); + switch (FlagBit) + { + // clang-format off + case RAYTRACING_INSTANCE_TRIANGLE_FACING_CULL_DISABLE: Result |= VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; break; + case RAYTRACING_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE: Result |= VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR; break; + case RAYTRACING_INSTANCE_FORCE_OPAQUE: Result |= VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR; break; + case RAYTRACING_INSTANCE_FORCE_NO_OPAQUE: Result |= VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR; break; + // clang-format on + default: UNEXPECTED("unknown instance flag"); + } + Flags = Flags & ~FlagBit; + } + return Result; +} + +VkCopyAccelerationStructureModeKHR CopyASModeToVkCopyAccelerationStructureMode(COPY_AS_MODE Mode) +{ + static_assert(COPY_AS_MODE_LAST == COPY_AS_MODE_COMPACT, + "Please update the switch below to handle the new copy AS mode"); + + switch (Mode) + { + // clang-format off + case COPY_AS_MODE_CLONE: return VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR; + case COPY_AS_MODE_COMPACT: return VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR; + // clang-format on + default: + UNEXPECTED("unknown AS copy mode"); + return VK_COPY_ACCELERATION_STRUCTURE_MODE_MAX_ENUM_KHR; + } +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp index 5a1894c0..ae460cde 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUploadHeap.cpp @@ -79,7 +79,7 @@ VulkanUploadHeap::UploadPageInfo VulkanUploadHeap::CreateNewPage(VkDeviceSize Si "at least one bit set corresponding to a VkMemoryType with a propertyFlags that has both the " "VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT bit AND the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit set. (11.6)"); - auto MemAllocation = GlobalMemoryMgr.Allocate(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, true); + auto MemAllocation = GlobalMemoryMgr.Allocate(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, true, VkMemoryAllocateFlags{0}); auto AlignedOffset = (MemAllocation.UnalignedOffset + (MemReqs.alignment - 1)) & ~(MemReqs.alignment - 1); auto err = LogicalDevice.BindBufferMemory(NewBuffer, MemAllocation.Page->GetVkMemory(), AlignedOffset); diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanCommandBuffer.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanCommandBuffer.cpp index 11444c33..1f81c872 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanCommandBuffer.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanCommandBuffer.cpp @@ -32,7 +32,7 @@ namespace VulkanUtilities { static VkPipelineStageFlags PipelineStageFromAccessFlags(VkAccessFlags AccessFlags, - const VkPipelineStageFlags EnabledGraphicsShaderStages) + const VkPipelineStageFlags EnabledShaderStages) { // 6.1.3 VkPipelineStageFlags Stages = 0; @@ -65,7 +65,7 @@ static VkPipelineStageFlags PipelineStageFromAccessFlags(VkAccessFlags // Read access to a uniform buffer case VK_ACCESS_UNIFORM_READ_BIT: - Stages |= EnabledGraphicsShaderStages | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + Stages |= EnabledShaderStages; break; // Read access to an input attachment within a render pass during fragment shading @@ -75,12 +75,12 @@ static VkPipelineStageFlags PipelineStageFromAccessFlags(VkAccessFlags // Read access to a storage buffer, uniform texel buffer, storage texel buffer, sampled image, or storage image case VK_ACCESS_SHADER_READ_BIT: - Stages |= EnabledGraphicsShaderStages | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + Stages |= EnabledShaderStages; break; // Write access to a storage buffer, storage texel buffer, or storage image case VK_ACCESS_SHADER_WRITE_BIT: - Stages |= EnabledGraphicsShaderStages | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + Stages |= EnabledShaderStages; break; // Read access to a color attachment, such as via blending, logic operations, or via certain subpass load operations @@ -134,6 +134,16 @@ static VkPipelineStageFlags PipelineStageFromAccessFlags(VkAccessFlags case VK_ACCESS_MEMORY_WRITE_BIT: break; + // Read access to acceleration structure or vertex/index/instance buffer in a build AS or trace rays operations. + case VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR: + Stages |= VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR; + break; + + // Write access to acceleration structure or scratch buffer in a build AS operations. + case VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR: + Stages |= VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; + break; + default: UNEXPECTED("Unknown memory access flag"); } @@ -223,8 +233,12 @@ static VkPipelineStageFlags AccessMaskFromImageLayout(VkImageLayout Layout, AccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; break; + // When transitioning the image to VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR or VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + // there is no need to delay subsequent processing, or perform any visibility operations (as vkQueuePresentKHR + // performs automatic visibility operations). To achieve this, the dstAccessMask member of the VkImageMemoryBarrier + // should be set to 0, and the dstStageMask parameter should be set to VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT. case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: - AccessMask = VK_ACCESS_MEMORY_READ_BIT; + AccessMask = 0; break; default: @@ -240,7 +254,7 @@ void VulkanCommandBuffer::TransitionImageLayout(VkCommandBuffer C VkImageLayout OldLayout, VkImageLayout NewLayout, const VkImageSubresourceRange& SubresRange, - VkPipelineStageFlags EnabledGraphicsShaderStages, + VkPipelineStageFlags EnabledShaderStages, VkPipelineStageFlags SrcStages, VkPipelineStageFlags DestStages) { @@ -268,7 +282,7 @@ void VulkanCommandBuffer::TransitionImageLayout(VkCommandBuffer C } else if (ImgBarrier.srcAccessMask != 0) { - SrcStages = PipelineStageFromAccessFlags(ImgBarrier.srcAccessMask, EnabledGraphicsShaderStages); + SrcStages = PipelineStageFromAccessFlags(ImgBarrier.srcAccessMask, EnabledShaderStages); } else { @@ -282,11 +296,11 @@ void VulkanCommandBuffer::TransitionImageLayout(VkCommandBuffer C { if (NewLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { - DestStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + DestStages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; } else if (ImgBarrier.dstAccessMask != 0) { - DestStages = PipelineStageFromAccessFlags(ImgBarrier.dstAccessMask, EnabledGraphicsShaderStages); + DestStages = PipelineStageFromAccessFlags(ImgBarrier.dstAccessMask, EnabledShaderStages); } else { @@ -327,7 +341,7 @@ void VulkanCommandBuffer::BufferMemoryBarrier(VkCommandBuffer CmdBuffer, VkBuffer Buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, - VkPipelineStageFlags EnabledGraphicsShaderStages, + VkPipelineStageFlags EnabledShaderStages, VkPipelineStageFlags SrcStages, VkPipelineStageFlags DestStages) { @@ -344,7 +358,7 @@ void VulkanCommandBuffer::BufferMemoryBarrier(VkCommandBuffer CmdBuffer, if (SrcStages == 0) { if (BuffBarrier.srcAccessMask != 0) - SrcStages = PipelineStageFromAccessFlags(BuffBarrier.srcAccessMask, EnabledGraphicsShaderStages); + SrcStages = PipelineStageFromAccessFlags(BuffBarrier.srcAccessMask, EnabledShaderStages); else { // An execution dependency with only VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT in the source stage @@ -356,7 +370,7 @@ void VulkanCommandBuffer::BufferMemoryBarrier(VkCommandBuffer CmdBuffer, if (DestStages == 0) { VERIFY(BuffBarrier.dstAccessMask != 0, "Dst access mask must not be zero"); - DestStages = PipelineStageFromAccessFlags(BuffBarrier.dstAccessMask, EnabledGraphicsShaderStages); + DestStages = PipelineStageFromAccessFlags(BuffBarrier.dstAccessMask, EnabledShaderStages); } vkCmdPipelineBarrier(CmdBuffer, @@ -371,6 +385,55 @@ void VulkanCommandBuffer::BufferMemoryBarrier(VkCommandBuffer CmdBuffer, nullptr); } +void VulkanCommandBuffer::ASMemoryBarrier(VkCommandBuffer CmdBuffer, + VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, + VkPipelineStageFlags EnabledShaderStages, + VkPipelineStageFlags SrcStages, + VkPipelineStageFlags DestStages) +{ + VkMemoryBarrier Barrier = {}; + Barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + Barrier.pNext = nullptr; + Barrier.srcAccessMask = srcAccessMask; + Barrier.dstAccessMask = dstAccessMask; + + if (SrcStages == 0) + { + if (Barrier.srcAccessMask != 0) + SrcStages = PipelineStageFromAccessFlags(Barrier.srcAccessMask, EnabledShaderStages); + else + { + // An execution dependency with only VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT in the source stage + // mask will effectively not wait for any prior commands to complete. (6.1.2) + SrcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } + } + + if (DestStages == 0) + { + VERIFY(Barrier.dstAccessMask != 0, "Dst access mask must not be zero"); + DestStages = PipelineStageFromAccessFlags(Barrier.dstAccessMask, EnabledShaderStages); + } + + // Other stages are not valid for acceleration structures + constexpr VkPipelineStageFlags StagesMask = VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + + SrcStages &= StagesMask; + DestStages &= StagesMask; + + vkCmdPipelineBarrier(CmdBuffer, + SrcStages, // must not be 0 + DestStages, // must not be 0 + 0, // a bitmask specifying how execution and memory dependencies are formed + 1, // memoryBarrierCount + &Barrier, // pMemoryBarriers + 0, + nullptr, + 0, + nullptr); +} + void VulkanCommandBuffer::FlushBarriers() { } diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp index 4feb8692..c1c20d79 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanDebug.cpp @@ -400,6 +400,11 @@ void SetQueryPoolName(VkDevice device, VkQueryPool queryPool, const char* name) SetObjectName(device, (uint64_t)queryPool, VK_OBJECT_TYPE_QUERY_POOL, name); } +void SetAccelStructName(VkDevice device, VkAccelerationStructureKHR accelStruct, const char* name) +{ + SetObjectName(device, (uint64_t)accelStruct, VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR, name); +} + template <> void SetVulkanObjectName<VkCommandPool, VulkanHandleTypeId::CommandPool>(VkDevice device, VkCommandPool cmdPool, const char* name) @@ -527,6 +532,11 @@ void SetVulkanObjectName<VkQueryPool, VulkanHandleTypeId::QueryPool>(VkDevice de SetQueryPoolName(device, queryPool, name); } +template <> +void SetVulkanObjectName<VkAccelerationStructureKHR, VulkanHandleTypeId::AccelerationStructureKHR>(VkDevice device, VkAccelerationStructureKHR accelStruct, const char* name) +{ + SetAccelStructName(device, accelStruct, name); +} const char* VkResultToString(VkResult errorCode) @@ -558,6 +568,20 @@ const char* VkResultToString(VkResult errorCode) STR(ERROR_INCOMPATIBLE_DISPLAY_KHR); STR(ERROR_VALIDATION_FAILED_EXT); STR(ERROR_INVALID_SHADER_NV); + STR(ERROR_FRAGMENTED_POOL); + STR(ERROR_UNKNOWN); + STR(ERROR_OUT_OF_POOL_MEMORY); + STR(ERROR_INVALID_EXTERNAL_HANDLE); + STR(ERROR_FRAGMENTATION); + STR(ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); + STR(ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); + STR(ERROR_NOT_PERMITTED_EXT); + STR(ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); + STR(THREAD_IDLE_KHR); + STR(THREAD_DONE_KHR); + STR(OPERATION_DEFERRED_KHR); + STR(OPERATION_NOT_DEFERRED_KHR); + STR(PIPELINE_COMPILE_REQUIRED_EXT); #undef STR // clang-format on default: @@ -589,6 +613,8 @@ const char* VkAccessFlagBitToString(VkAccessFlagBits Bit) ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_HOST_WRITE_BIT) ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_MEMORY_READ_BIT) ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_MEMORY_WRITE_BIT) + ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR) + ACCESS_FLAG_BIT_TO_STRING(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR) #undef ACCESS_FLAG_BIT_TO_STRING default: UNEXPECTED("Unexpected bit"); return ""; // clang-format on diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanInstance.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanInstance.cpp index a03102d0..a88a5888 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanInstance.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanInstance.cpp @@ -46,12 +46,16 @@ namespace VulkanUtilities { -bool VulkanInstance::IsLayerAvailable(const char* LayerName) const +bool VulkanInstance::IsLayerAvailable(const char* LayerName, uint32_t& Version) const { for (const auto& Layer : m_Layers) + { if (strcmp(Layer.layerName, LayerName) == 0) + { + Version = Layer.specVersion; return true; - + } + } return false; } @@ -73,16 +77,18 @@ bool VulkanInstance::IsExtensionEnabled(const char* ExtensionName) const return false; } -std::shared_ptr<VulkanInstance> VulkanInstance::Create(bool EnableValidation, +std::shared_ptr<VulkanInstance> VulkanInstance::Create(uint32_t ApiVersion, + bool EnableValidation, uint32_t GlobalExtensionCount, const char* const* ppGlobalExtensionNames, VkAllocationCallbacks* pVkAllocator) { - auto Instance = new VulkanInstance{EnableValidation, GlobalExtensionCount, ppGlobalExtensionNames, pVkAllocator}; + auto Instance = new VulkanInstance{ApiVersion, EnableValidation, GlobalExtensionCount, ppGlobalExtensionNames, pVkAllocator}; return std::shared_ptr<VulkanInstance>{Instance}; } -VulkanInstance::VulkanInstance(bool EnableValidation, +VulkanInstance::VulkanInstance(uint32_t ApiVersion, + bool EnableValidation, uint32_t GlobalExtensionCount, const char* const* ppGlobalExtensionNames, VkAllocationCallbacks* pVkAllocator) : @@ -184,6 +190,16 @@ VulkanInstance::VulkanInstance(bool EnableValidation, } } +#if DILIGENT_USE_VOLK + if (vkEnumerateInstanceVersion != nullptr && ApiVersion > VK_API_VERSION_1_0) + { + uint32_t MaxApiVersion = 0; + vkEnumerateInstanceVersion(&MaxApiVersion); + ApiVersion = std::min(ApiVersion, MaxApiVersion); + LOG_INFO_MESSAGE("Using Vulkan API version ", VK_VERSION_MAJOR(ApiVersion), ".", VK_VERSION_MINOR(ApiVersion)); + } +#endif + VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; @@ -192,7 +208,7 @@ VulkanInstance::VulkanInstance(bool EnableValidation, appInfo.applicationVersion = 0; // Developer-supplied version number of the application appInfo.pEngineName = "Diligent Engine"; appInfo.engineVersion = 0; // Developer-supplied version number of the engine used to create the application. - appInfo.apiVersion = VK_API_VERSION_1_0; + appInfo.apiVersion = ApiVersion; VkInstanceCreateInfo InstanceCreateInfo = {}; @@ -208,11 +224,20 @@ VulkanInstance::VulkanInstance(bool EnableValidation, bool ValidationLayersPresent = true; for (size_t l = 0; l < _countof(VulkanUtilities::ValidationLayerNames); ++l) { - auto* pLayerName = VulkanUtilities::ValidationLayerNames[l]; - if (!IsLayerAvailable(pLayerName)) + auto* pLayerName = VulkanUtilities::ValidationLayerNames[l]; + uint32_t LayerVer = 0; + if (!IsLayerAvailable(pLayerName, LayerVer)) + { + ValidationLayersPresent = false; + LOG_WARNING_MESSAGE("Failed to find '", pLayerName, "' layer. Validation will be disabled"); + } + if (LayerVer < VK_HEADER_VERSION_COMPLETE) { ValidationLayersPresent = false; - LOG_WARNING_MESSAGE("Failed to find ", pLayerName, " layer. Validation will be disabled"); + LOG_WARNING_MESSAGE("Layer '", pLayerName, "' version (", VK_VERSION_MAJOR(LayerVer), ".", VK_VERSION_MINOR(LayerVer), ".", VK_VERSION_PATCH(LayerVer), + ") is less than header version (", + VK_VERSION_MAJOR(VK_HEADER_VERSION_COMPLETE), ".", VK_VERSION_MINOR(VK_HEADER_VERSION_COMPLETE), ".", VK_VERSION_PATCH(VK_HEADER_VERSION_COMPLETE), + "). Validation will be disabled"); } } if (ValidationLayersPresent) @@ -229,6 +254,7 @@ VulkanInstance::VulkanInstance(bool EnableValidation, #endif m_EnabledExtensions = std::move(GlobalExtensions); + m_VkVersion = ApiVersion; // If requested, we enable the default validation layers for debugging if (m_DebugUtilsEnabled) diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp index 5b720fd7..0d1534b8 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp @@ -34,11 +34,12 @@ namespace VulkanUtilities { -std::shared_ptr<VulkanLogicalDevice> VulkanLogicalDevice::Create(VkPhysicalDevice vkPhysicalDevice, +std::shared_ptr<VulkanLogicalDevice> VulkanLogicalDevice::Create(const VulkanPhysicalDevice& PhysicalDevice, const VkDeviceCreateInfo& DeviceCI, + const ExtensionFeatures& EnabledExtFeatures, const VkAllocationCallbacks* vkAllocator) { - auto* LogicalDevice = new VulkanLogicalDevice{vkPhysicalDevice, DeviceCI, vkAllocator}; + auto* LogicalDevice = new VulkanLogicalDevice{PhysicalDevice, DeviceCI, EnabledExtFeatures, vkAllocator}; return std::shared_ptr<VulkanLogicalDevice>{LogicalDevice}; } @@ -47,13 +48,15 @@ VulkanLogicalDevice::~VulkanLogicalDevice() vkDestroyDevice(m_VkDevice, m_VkAllocator); } -VulkanLogicalDevice::VulkanLogicalDevice(VkPhysicalDevice vkPhysicalDevice, +VulkanLogicalDevice::VulkanLogicalDevice(const VulkanPhysicalDevice& PhysicalDevice, const VkDeviceCreateInfo& DeviceCI, + const ExtensionFeatures& EnabledExtFeatures, const VkAllocationCallbacks* vkAllocator) : m_VkAllocator{vkAllocator}, - m_EnabledFeatures{*DeviceCI.pEnabledFeatures} + m_EnabledFeatures{*DeviceCI.pEnabledFeatures}, + m_EnabledExtFeatures{EnabledExtFeatures} { - auto res = vkCreateDevice(vkPhysicalDevice, &DeviceCI, vkAllocator, &m_VkDevice); + auto res = vkCreateDevice(PhysicalDevice.GetVkDeviceHandle(), &DeviceCI, vkAllocator, &m_VkDevice); CHECK_VK_ERROR_AND_THROW(res, "Failed to create logical device"); #if DILIGENT_USE_VOLK @@ -62,11 +65,15 @@ VulkanLogicalDevice::VulkanLogicalDevice(VkPhysicalDevice vkPhysical volkLoadDevice(m_VkDevice); #endif - m_EnabledGraphicsShaderStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + m_EnabledShaderStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; if (DeviceCI.pEnabledFeatures->geometryShader) - m_EnabledGraphicsShaderStages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + m_EnabledShaderStages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; if (DeviceCI.pEnabledFeatures->tessellationShader) - m_EnabledGraphicsShaderStages |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; + m_EnabledShaderStages |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; + if (m_EnabledExtFeatures.MeshShader.meshShader != VK_FALSE && m_EnabledExtFeatures.MeshShader.taskShader != VK_FALSE) + m_EnabledShaderStages |= VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV | VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV; + if (m_EnabledExtFeatures.RayTracingPipeline.rayTracingPipeline != VK_FALSE) + m_EnabledShaderStages |= VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR; } VkQueue VulkanLogicalDevice::GetQueue(uint32_t queueFamilyIndex, uint32_t queueIndex) @@ -222,6 +229,29 @@ PipelineWrapper VulkanLogicalDevice::CreateGraphicsPipeline(const VkGraphicsPipe return PipelineWrapper{GetSharedPtr(), std::move(vkPipeline)}; } +PipelineWrapper VulkanLogicalDevice::CreateRayTracingPipeline(const VkRayTracingPipelineCreateInfoKHR& PipelineCI, VkPipelineCache cache, const char* DebugName) const +{ +#if DILIGENT_USE_VOLK + VERIFY_EXPR(PipelineCI.sType == VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR); + + if (DebugName == nullptr) + DebugName = ""; + + VkPipeline vkPipeline = VK_NULL_HANDLE; + + auto err = vkCreateRayTracingPipelinesKHR(m_VkDevice, VK_NULL_HANDLE, cache, 1, &PipelineCI, m_VkAllocator, &vkPipeline); + CHECK_VK_ERROR_AND_THROW(err, "Failed to create ray tracing pipeline '", DebugName, '\''); + + if (*DebugName != 0) + SetPipelineName(m_VkDevice, vkPipeline, DebugName); + + return PipelineWrapper{GetSharedPtr(), std::move(vkPipeline)}; +#else + UNSUPPORTED("vkCreateRayTracingPipelinesKHR is only available through Volk"); + return PipelineWrapper{}; +#endif +} + ShaderModuleWrapper VulkanLogicalDevice::CreateShaderModule(const VkShaderModuleCreateInfo& ShaderModuleCI, const char* DebugName) const { VERIFY_EXPR(ShaderModuleCI.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO); @@ -264,6 +294,17 @@ QueryPoolWrapper VulkanLogicalDevice::CreateQueryPool(const VkQueryPoolCreateInf return CreateVulkanObject<VkQueryPool, VulkanHandleTypeId::QueryPool>(vkCreateQueryPool, QueryPoolCI, DebugName, "query pool"); } +AccelStructWrapper VulkanLogicalDevice::CreateAccelStruct(const VkAccelerationStructureCreateInfoKHR& CI, const char* DebugName) const +{ +#if DILIGENT_USE_VOLK + VERIFY_EXPR(CI.sType == VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR); + return CreateVulkanObject<VkAccelerationStructureKHR, VulkanHandleTypeId::AccelerationStructureKHR>(vkCreateAccelerationStructureKHR, CI, DebugName, "acceleration structure"); +#else + UNSUPPORTED("vkCreateAccelerationStructureKHR is only available through Volk"); + return AccelStructWrapper{}; +#endif +} + VkCommandBuffer VulkanLogicalDevice::AllocateVkCommandBuffer(const VkCommandBufferAllocateInfo& AllocInfo, const char* DebugName) const { VERIFY_EXPR(AllocInfo.sType == VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO); @@ -405,6 +446,16 @@ void VulkanLogicalDevice::ReleaseVulkanObject(QueryPoolWrapper&& QueryPool) cons QueryPool.m_VkObject = VK_NULL_HANDLE; } +void VulkanLogicalDevice::ReleaseVulkanObject(AccelStructWrapper&& AccelStruct) const +{ +#if DILIGENT_USE_VOLK + vkDestroyAccelerationStructureKHR(m_VkDevice, AccelStruct.m_VkObject, m_VkAllocator); + AccelStruct.m_VkObject = VK_NULL_HANDLE; +#else + UNSUPPORTED("vkDestroyAccelerationStructureKHR is only available through Volk"); +#endif +} + void VulkanLogicalDevice::FreeDescriptorSet(VkDescriptorPool Pool, VkDescriptorSet Set) const { VERIFY_EXPR(Pool != VK_NULL_HANDLE && Set != VK_NULL_HANDLE); @@ -438,6 +489,31 @@ VkResult VulkanLogicalDevice::BindImageMemory(VkImage image, VkDeviceMemory memo return vkBindImageMemory(m_VkDevice, image, memory, memoryOffset); } +VkDeviceAddress VulkanLogicalDevice::GetAccelerationStructureDeviceAddress(VkAccelerationStructureKHR AS) const +{ +#if DILIGENT_USE_VOLK + VkAccelerationStructureDeviceAddressInfoKHR Info = {}; + + Info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + Info.accelerationStructure = AS; + + return vkGetAccelerationStructureDeviceAddressKHR(m_VkDevice, &Info); +#else + UNSUPPORTED("vkGetAccelerationStructureDeviceAddressKHR is only available through Volk"); + return VK_ERROR_FEATURE_NOT_PRESENT; +#endif +} + +void VulkanLogicalDevice::GetAccelerationStructureBuildSizes(const VkAccelerationStructureBuildGeometryInfoKHR& BuildInfo, const uint32_t* pMaxPrimitiveCounts, VkAccelerationStructureBuildSizesInfoKHR& SizeInfo) const +{ +#if DILIGENT_USE_VOLK + vkGetAccelerationStructureBuildSizesKHR(m_VkDevice, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &BuildInfo, pMaxPrimitiveCounts, &SizeInfo); +#else + UNSUPPORTED("vkGetAccelerationStructureDeviceAddressKHR is only available through Volk"); + return VK_ERROR_FEATURE_NOT_PRESENT; +#endif +} + VkResult VulkanLogicalDevice::MapMemory(VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) const { return vkMapMemory(m_VkDevice, memory, offset, size, flags, ppData); @@ -502,4 +578,14 @@ VkResult VulkanLogicalDevice::ResetDescriptorPool(VkDescriptorPool vkD return err; } +VkResult VulkanLogicalDevice::GetRayTracingShaderGroupHandles(VkPipeline pipeline, uint32_t firstGroup, uint32_t groupCount, size_t dataSize, void* pData) const +{ +#if DILIGENT_USE_VOLK + return vkGetRayTracingShaderGroupHandlesKHR(m_VkDevice, pipeline, firstGroup, groupCount, dataSize, pData); +#else + UNSUPPORTED("vkGetRayTracingShaderGroupHandlesKHR is only available through Volk"); + return VK_ERROR_FEATURE_NOT_PRESENT; +#endif +} + } // namespace VulkanUtilities diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp index f9cdcdea..2e2cdd31 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp @@ -40,10 +40,11 @@ VulkanMemoryAllocation::~VulkanMemoryAllocation() } } -VulkanMemoryPage::VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, - VkDeviceSize PageSize, - uint32_t MemoryTypeIndex, - bool IsHostVisible) noexcept : +VulkanMemoryPage::VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, + VkDeviceSize PageSize, + uint32_t MemoryTypeIndex, + bool IsHostVisible, + VkMemoryAllocateFlags AllocateFlags) noexcept : // clang-format off m_ParentMemoryMgr{ParentMemoryMgr}, m_AllocationMgr {static_cast<AllocationsMgrOffsetType>(PageSize), ParentMemoryMgr.m_Allocator} @@ -53,13 +54,22 @@ VulkanMemoryPage::VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr, "PageSize (", PageSize, ") exceeds maximum allowed value ", std::numeric_limits<AllocationsMgrOffsetType>::max()); - VkMemoryAllocateInfo MemAlloc = {}; + VkMemoryAllocateInfo MemAlloc = {}; + VkMemoryAllocateFlagsInfo MemFlagInfo = {}; MemAlloc.pNext = nullptr; MemAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; MemAlloc.allocationSize = PageSize; MemAlloc.memoryTypeIndex = MemoryTypeIndex; + if (AllocateFlags) + { + MemAlloc.pNext = &MemFlagInfo; + MemFlagInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + MemFlagInfo.pNext = nullptr; + MemFlagInfo.flags = AllocateFlags; + } + auto MemoryName = Diligent::FormatString("Device memory page. Size: ", Diligent::FormatMemorySize(PageSize, 2), ", type: ", MemoryTypeIndex); m_VkMemory = ParentMemoryMgr.m_LogicalDevice.AllocateDeviceMemory(MemAlloc, MemoryName.c_str()); @@ -116,7 +126,7 @@ void VulkanMemoryPage::Free(VulkanMemoryAllocation&& Allocation) Allocation = VulkanMemoryAllocation{}; } -VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProps) +VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProps, VkMemoryAllocateFlags AllocateFlags) { // memoryTypeBits is a bitmask and contains one bit set for every supported memory type for the resource. // Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the @@ -145,10 +155,10 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& } bool HostVisible = (MemoryProps & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; - return Allocate(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, HostVisible); + return Allocate(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, HostVisible, AllocateFlags); } -VulkanMemoryAllocation VulkanMemoryManager::Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible) +VulkanMemoryAllocation VulkanMemoryManager::Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible, VkMemoryAllocateFlags AllocateFlags) { VulkanMemoryAllocation Allocation; @@ -159,7 +169,7 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(VkDeviceSize Size, VkDevice // even though on integrated GPUs same pages can be used for both GPU-only and staging // allocations. Staging allocations are short-living and will be released when upload is // complete, while GPU-only allocations are expected to be long-living. - MemoryPageIndex PageIdx{MemoryTypeIndex, HostVisible}; + MemoryPageIndex PageIdx{MemoryTypeIndex, HostVisible, AllocateFlags}; std::lock_guard<std::mutex> Lock{m_PagesMtx}; auto range = m_Pages.equal_range(PageIdx); @@ -180,7 +190,7 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(VkDeviceSize Size, VkDevice m_CurrAllocatedSize[stat_ind] += PageSize; m_PeakAllocatedSize[stat_ind] = std::max(m_PeakAllocatedSize[stat_ind], m_CurrAllocatedSize[stat_ind]); - auto it = m_Pages.emplace(PageIdx, VulkanMemoryPage{*this, PageSize, MemoryTypeIndex, HostVisible}); + auto it = m_Pages.emplace(PageIdx, VulkanMemoryPage{*this, PageSize, MemoryTypeIndex, HostVisible, AllocateFlags}); LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "': created new ", (HostVisible ? "host-visible" : "device-local"), " page. (", Diligent::FormatMemorySize(PageSize, 2), ", type idx: ", MemoryTypeIndex, "). Current allocated size: ", Diligent::FormatMemorySize(m_CurrAllocatedSize[stat_ind], 2)); diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanPhysicalDevice.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanPhysicalDevice.cpp index 92a5e4d4..61f9f8de 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanPhysicalDevice.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanPhysicalDevice.cpp @@ -68,12 +68,13 @@ VulkanPhysicalDevice::VulkanPhysicalDevice(VkPhysicalDevice vkDevice, VERIFY_EXPR(ExtensionCount == m_SupportedExtensions.size()); } -#ifdef VK_KHR_get_physical_device_properties2 +#if DILIGENT_USE_VOLK if (Instance.IsExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { VkPhysicalDeviceFeatures2 Feats2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; VkPhysicalDeviceProperties2 Props2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; void** NextFeat = &Feats2.pNext; + void** NextProp = &Props2.pNext; if (IsExtensionSupported(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { @@ -103,23 +104,92 @@ VulkanPhysicalDevice::VulkanPhysicalDevice(VkPhysicalDevice vkDevice, } } - // Enable mesh shader extension. + // Get mesh shader features and properties. if (IsExtensionSupported(VK_NV_MESH_SHADER_EXTENSION_NAME)) { *NextFeat = &m_ExtFeatures.MeshShader; NextFeat = &m_ExtFeatures.MeshShader.pNext; m_ExtFeatures.MeshShader.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV; + + *NextProp = &m_ExtProperties.MeshShader; + NextProp = &m_ExtProperties.MeshShader.pNext; + + m_ExtProperties.MeshShader.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV; + } + + // Get acceleration structure features and properties. + if (IsExtensionSupported(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME)) + { + *NextFeat = &m_ExtFeatures.AccelStruct; + NextFeat = &m_ExtFeatures.AccelStruct.pNext; + + m_ExtFeatures.AccelStruct.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR; + + *NextProp = &m_ExtProperties.AccelStruct; + NextProp = &m_ExtProperties.AccelStruct.pNext; + + m_ExtProperties.AccelStruct.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR; } + // Get ray tracing pipeline features and properties. + if (IsExtensionSupported(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME)) + { + *NextFeat = &m_ExtFeatures.RayTracingPipeline; + NextFeat = &m_ExtFeatures.RayTracingPipeline.pNext; + + m_ExtFeatures.RayTracingPipeline.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR; + + *NextProp = &m_ExtProperties.RayTracingPipeline; + NextProp = &m_ExtProperties.RayTracingPipeline.pNext; + + m_ExtProperties.RayTracingPipeline.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR; + } + + // Additional extension that is required for ray tracing. + if (IsExtensionSupported(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) + { + *NextFeat = &m_ExtFeatures.BufferDeviceAddress; + NextFeat = &m_ExtFeatures.BufferDeviceAddress.pNext; + + m_ExtFeatures.BufferDeviceAddress.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR; + } + + // Additional extension that is required for ray tracing. + if (IsExtensionSupported(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)) + { + *NextFeat = &m_ExtFeatures.DescriptorIndexing; + NextFeat = &m_ExtFeatures.DescriptorIndexing.pNext; + + m_ExtFeatures.DescriptorIndexing.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT; + + *NextProp = &m_ExtProperties.DescriptorIndexing; + NextProp = &m_ExtProperties.DescriptorIndexing.pNext; + + m_ExtProperties.DescriptorIndexing.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT; + } + + // Additional extension that is required for ray tracing shader. + if (IsExtensionSupported(VK_KHR_SPIRV_1_4_EXTENSION_NAME)) + m_ExtFeatures.Spirv14 = true; + + // Some features requires SPIRV 1.4 or 1.5 that added to Vulkan 1.2 core. + if (Instance.GetVkVersion() >= VK_API_VERSION_1_2) + { + m_ExtFeatures.Spirv14 = true; + m_ExtFeatures.Spirv15 = true; + } + + // make sure that last pNext is null *NextFeat = nullptr; + *NextProp = nullptr; // Initialize device extension features by current physical device features. // Some flags may not be supported by hardware. vkGetPhysicalDeviceFeatures2KHR(m_VkDevice, &Feats2); vkGetPhysicalDeviceProperties2KHR(m_VkDevice, &Props2); } -#endif +#endif // DILIGENT_USE_VOLK } uint32_t VulkanPhysicalDevice::FindQueueFamily(VkQueueFlags QueueFlags) const |
