diff options
| author | azhirnov <zh1dron@gmail.com> | 2020-11-07 19:34:49 +0000 |
|---|---|---|
| committer | assiduous <assiduous@diligentgraphics.com> | 2020-11-10 03:43:28 +0000 |
| commit | 0f35896a60c4de02ccfc91ace18bcef4450fa4d9 (patch) | |
| tree | 86a772a36c6c2258949231b756970a621b69f82b /Graphics/GraphicsEngine | |
| parent | Corrected ray rtacing vertex format handling in D3D12 (diff) | |
| download | DiligentCore-0f35896a60c4de02ccfc91ace18bcef4450fa4d9.tar.gz DiligentCore-0f35896a60c4de02ccfc91ace18bcef4450fa4d9.zip | |
Added ability to update AS.
Diffstat (limited to 'Graphics/GraphicsEngine')
| -rw-r--r-- | Graphics/GraphicsEngine/include/BottomLevelASBase.hpp | 124 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/DeviceContextBase.hpp | 25 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/PipelineStateBase.hpp | 13 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp | 312 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp | 52 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/TopLevelASBase.hpp | 257 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/BottomLevelAS.h | 61 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/DeviceContext.h | 92 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/PipelineState.h | 9 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/ShaderBindingTable.h | 169 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/TopLevelAS.h | 61 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/src/BottomLevelASBase.cpp | 46 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/src/DeviceContextBase.cpp | 213 |
13 files changed, 1041 insertions, 393 deletions
diff --git a/Graphics/GraphicsEngine/include/BottomLevelASBase.hpp b/Graphics/GraphicsEngine/include/BottomLevelASBase.hpp index caee8620..8f180004 100644 --- a/Graphics/GraphicsEngine/include/BottomLevelASBase.hpp +++ b/Graphics/GraphicsEngine/include/BottomLevelASBase.hpp @@ -42,14 +42,26 @@ namespace Diligent { +struct BLASGeomIndex +{ + Uint32 IndexInDesc = INVALID_INDEX; // geometry index in description + Uint32 ActualIndex = INVALID_INDEX; // geometry index in build operation + + BLASGeomIndex() {} + BLASGeomIndex(Uint32 _IndexInDesc, Uint32 _ActualIndex) : + IndexInDesc{_IndexInDesc}, ActualIndex{_ActualIndex} {} +}; +using BLASNameToIndex = std::unordered_map<HashMapStringKey, BLASGeomIndex, HashMapStringKey::Hasher>; + /// Validates bottom-level AS description and throws and exception in case of an error. void ValidateBottomLevelASDesc(const BottomLevelASDesc& Desc) noexcept(false); -/// Copies bottom-level AS description (except for the Name) using MemPool to allocate required dynamic space. -void CopyBottomLevelASDesc(const BottomLevelASDesc& SrcDesc, - BottomLevelASDesc& DstDesc, - LinearAllocator& MemPool, - std::unordered_map<HashMapStringKey, Uint32, HashMapStringKey::Hasher>& NameToIndex) noexcept(false); +/// Copies bottom-level AS geometry description using MemPool to allocate required dynamic space. +void CopyBLASGeometryDesc(const BottomLevelASDesc& SrcDesc, + BottomLevelASDesc& DstDesc, + LinearAllocator& MemPool, + const BLASNameToIndex* pSrcNameToIndex, + BLASNameToIndex& DstNameToIndex) noexcept(false); /// Template class implementing base functionality for a bottom-level acceleration structure object. @@ -82,32 +94,65 @@ public: } else { - CopyDescriptionUnsafe(Desc); + CopyGeometryDescriptionUnsafe(Desc, nullptr); } } ~BottomLevelASBase() { - Clear(); + ClearGeometry(); } IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_BottomLevelAS, TDeviceObjectBase) - virtual Uint32 DILIGENT_CALL_TYPE GetGeometryIndex(const char* Name) const override final + // Map geometry that used in build operation to geometry description. + // Returns geometry index in geometry description. + Uint32 UpdateGeometryIndex(const char* Name, Uint32& ActualIndex, bool OnUpdate) { VERIFY_EXPR(Name != nullptr && Name[0] != '\0'); auto iter = m_NameToIndex.find(Name); if (iter != m_NameToIndex.end()) - return iter->second; + { + if (OnUpdate) + ActualIndex = iter->second.ActualIndex; + else + iter->second.ActualIndex = ActualIndex; + return iter->second.IndexInDesc; + } + LOG_ERROR_MESSAGE("Can't find geometry with name '", Name, '\''); + return INVALID_INDEX; + } + + virtual Uint32 DILIGENT_CALL_TYPE GetGeometryDescIndex(const char* Name) const override final + { + VERIFY_EXPR(Name != nullptr && Name[0] != '\0'); + + auto iter = m_NameToIndex.find(Name); + if (iter != m_NameToIndex.end()) + return iter->second.IndexInDesc; + + LOG_ERROR_MESSAGE("Can't find geometry with name '", Name, '\''); + return INVALID_INDEX; + } + + virtual Uint32 DILIGENT_CALL_TYPE GetGeometryIndex(const char* Name) const override final + { + VERIFY_EXPR(Name != nullptr && Name[0] != '\0'); + auto iter = m_NameToIndex.find(Name); + if (iter != m_NameToIndex.end()) + { + VERIFY(iter->second.ActualIndex != INVALID_INDEX, "Geometry exists but not enabled during last build"); + return iter->second.ActualIndex; + } LOG_ERROR_MESSAGE("Can't find geometry with name '", Name, '\''); - return InvalidGeometryIndex; + return INVALID_INDEX; } virtual void DILIGENT_CALL_TYPE SetState(RESOURCE_STATE State) override final { - VERIFY(State == RESOURCE_STATE_BUILD_AS_READ || State == RESOURCE_STATE_BUILD_AS_WRITE, + VERIFY(State == RESOURCE_STATE_UNKNOWN || State == RESOURCE_STATE_BUILD_AS_READ || State == RESOURCE_STATE_BUILD_AS_WRITE, "Unsupported state for a bottom-level acceleration structure"); this->m_State = State; } @@ -118,7 +163,7 @@ public: } /// Implementation of IBottomLevelAS::GetScratchBufferSizes() - virtual ScratchBufferSizes DILIGENT_CALL_TYPE GetScratchBufferSizes() const override + virtual ScratchBufferSizes DILIGENT_CALL_TYPE GetScratchBufferSizes() const override final { return this->m_ScratchSize; } @@ -138,44 +183,48 @@ public: #ifdef DILIGENT_DEVELOPMENT void UpdateVersion() { - m_Version.fetch_add(1); + this->m_DbgVersion.fetch_add(1); } Uint32 GetVersion() const { - return m_Version.load(); - } - - bool ValidateContent() const - { - // AZ TODO - return true; + return this->m_DbgVersion.load(); } #endif // DILIGENT_DEVELOPMENT - void CopyDescription(const BottomLevelASBase& SrcBLAS) noexcept + void CopyGeometryDescription(const BottomLevelASBase& SrcBLAS) noexcept { - Clear(); + ClearGeometry(); try { - CopyDescriptionUnsafe(SrcBLAS.GetDesc()); + CopyGeometryDescriptionUnsafe(SrcBLAS.GetDesc(), &SrcBLAS.m_NameToIndex); } catch (...) { - Clear(); + ClearGeometry(); } } + void SetActualGeometryCount(Uint32 Count) + { + m_GeometryCount = Count; + } + + virtual Uint32 DILIGENT_CALL_TYPE GetActualGeometryCount() const override final + { + return m_GeometryCount; + } + private: - void CopyDescriptionUnsafe(const BottomLevelASDesc& SrcDesc) noexcept(false) + void CopyGeometryDescriptionUnsafe(const BottomLevelASDesc& SrcDesc, const BLASNameToIndex* pSrcNameToIndex) noexcept(false) { LinearAllocator MemPool{GetRawAllocator()}; - CopyBottomLevelASDesc(SrcDesc, this->m_Desc, MemPool, m_NameToIndex); + CopyBLASGeometryDesc(SrcDesc, this->m_Desc, MemPool, pSrcNameToIndex, this->m_NameToIndex); this->m_pRawPtr = MemPool.Release(); } - void Clear() noexcept + void ClearGeometry() noexcept { if (this->m_pRawPtr != nullptr) { @@ -183,25 +232,24 @@ private: this->m_pRawPtr = nullptr; } - // Preserve original name - it was allocated by DeviceObjectBase - auto* Name = this->m_Desc.Name; - this->m_Desc = BottomLevelASDesc{}; - this->m_Desc.Name = Name; + // keep Name, Flags, CompactedSize, CommandQueueMask + this->m_Desc.pTriangles = nullptr; + this->m_Desc.TriangleCount = 0; + this->m_Desc.pBoxes = nullptr; + this->m_Desc.BoxCount = 0; m_NameToIndex.clear(); } protected: - RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; - - std::unordered_map<HashMapStringKey, Uint32, HashMapStringKey::Hasher> m_NameToIndex; - - void* m_pRawPtr = nullptr; - + RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; + BLASNameToIndex m_NameToIndex; + void* m_pRawPtr = nullptr; + Uint32 m_GeometryCount = 0; ScratchBufferSizes m_ScratchSize; #ifdef DILIGENT_DEVELOPMENT - std::atomic<Uint32> m_Version{0}; + std::atomic<Uint32> m_DbgVersion{0}; #endif }; diff --git a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp index 3edb27ca..fbdb8d8f 100644 --- a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp +++ b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp @@ -66,8 +66,8 @@ bool VerifyBeginRenderPassAttribs(const BeginRenderPassAttribs& Attribs); bool VerifyStateTransitionDesc(const IRenderDevice* pDevice, const StateTransitionDesc& Barrier); bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs); -bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs); -bool VerifyCopyBLASAttribs(const CopyBLASAttribs& Attribs); +bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs, Uint32 PrevInstanceCount); +bool VerifyCopyBLASAttribs(const IRenderDevice* pDevice, const CopyBLASAttribs& Attribs); bool VerifyCopyTLASAttribs(const CopyTLASAttribs& Attribs); bool VerifyWriteBLASCompactedSizeAttribs(const IRenderDevice* pDevice, const WriteBLASCompactedSizeAttribs& Attribs); bool VerifyWriteTLASCompactedSizeAttribs(const IRenderDevice* pDevice, const WriteTLASCompactedSizeAttribs& Attribs); @@ -1482,17 +1482,10 @@ bool DeviceContextBase<BaseInterface, ImplementationTraits>::BuildTLAS(const Bui return false; } - if (!VerifyBuildTLASAttribs(Attribs)) - return false; + const Uint32 InstCount = Attribs.pTLAS ? ValidatedCast<TopLevelASType>(Attribs.pTLAS)->GetInstanceCount() : 0; - for (Uint32 i = 0; i < Attribs.InstanceCount; ++i) - { - if (!ValidatedCast<BottomLevelASType>(Attribs.pInstances[i].pBLAS)->ValidateContent()) - { - LOG_ERROR_MESSAGE("IDeviceContext::BuildTLAS: pInstances[", i, "].pBLAS is not valid"); - return false; - } - } + if (!VerifyBuildTLASAttribs(Attribs, InstCount)) + return false; #endif return true; @@ -1514,14 +1507,8 @@ bool DeviceContextBase<BaseInterface, ImplementationTraits>::CopyBLAS(const Copy return false; } - if (!VerifyCopyBLASAttribs(Attribs)) + if (!VerifyCopyBLASAttribs(m_pDevice, Attribs)) return false; - - if (!ValidatedCast<BottomLevelASType>(Attribs.pSrc)->ValidateContent()) - { - LOG_ERROR_MESSAGE("IDeviceContext::CopyBLAS: pSrc acceleration structure is not valid"); - return false; - } #endif return true; diff --git a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp index 3f92416d..829437d4 100644 --- a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp +++ b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp @@ -205,15 +205,6 @@ public: return m_pRayTracingPipelineData->Desc; } - virtual Uint32 DILIGENT_CALL_TYPE GetShaderGroupCount() const override final - { - VERIFY_EXPR(this->m_Desc.IsRayTracingPipeline()); - VERIFY_EXPR(m_pRayTracingPipelineData != nullptr); - return static_cast<Uint32>(m_pRayTracingPipelineData->NameToGroupIndex.size()); - } - - static constexpr Uint32 InvalidShaderGroupIndex = ~0u; - virtual Uint32 DILIGENT_CALL_TYPE GetShaderGroupIndex(const char* Name) const override final { VERIFY_EXPR(Name != nullptr && Name[0] != '\0'); @@ -225,10 +216,10 @@ public: return iter->second; UNEXPECTED("Can't find shader group with specified name"); - return InvalidShaderGroupIndex; + return INVALID_INDEX; } - inline void CopyShaderHandle(const char* Name, void* pData, Uint32 DataSize) const + inline void CopyShaderHandle(const char* Name, void* pData, size_t DataSize) const { VERIFY_EXPR(this->m_Desc.IsRayTracingPipeline()); VERIFY_EXPR(m_pRayTracingPipelineData != nullptr); diff --git a/Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp b/Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp index ff6d7e95..41983aa8 100644 --- a/Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp @@ -33,6 +33,7 @@ #include <unordered_map> #include "ShaderBindingTable.h" +#include "TopLevelASBase.hpp" #include "DeviceObjectBase.hpp" #include "RenderDeviceBase.hpp" #include "StringPool.hpp" @@ -50,7 +51,7 @@ void ValidateShaderBindingTableDesc(const ShaderBindingTableDesc& Desc, Uint32 S /// (Diligent::IShaderBindingTableD3D12 or Diligent::IShaderBindingTableVk). /// \tparam RenderDeviceImplType - type of the render device implementation /// (Diligent::RenderDeviceD3D12Impl or Diligent::RenderDeviceVkImpl) -template <class BaseInterface, class PipelineStateImplType, class RenderDeviceImplType> +template <class BaseInterface, class PipelineStateImplType, class TopLevelASImplType, class RenderDeviceImplType> class ShaderBindingTableBase : public DeviceObjectBase<BaseInterface, RenderDeviceImplType, ShaderBindingTableDesc> { public: @@ -81,203 +82,312 @@ public: IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_ShaderBindingTable, TDeviceObjectBase) - void DILIGENT_CALL_TYPE Reset(const ShaderBindingTableDesc& Desc) override final + + void DILIGENT_CALL_TYPE Reset(IPipelineState* pPSO) override final { +#ifdef DILIGENT_DEVELOPMENT + this->m_DbgHitGroupBindings.clear(); +#endif this->m_RayGenShaderRecord.clear(); this->m_MissShadersRecord.clear(); this->m_CallableShadersRecord.clear(); this->m_HitGroupsRecord.clear(); - this->m_Changed = true; - this->m_pPSO = nullptr; - const auto* Name = this->m_Desc.Name; // Store original name - this->m_Desc = {}; + this->m_Changed = true; + this->m_pPSO = nullptr; + + this->m_Desc.pPSO = pPSO; const auto& DeviceProps = this->m_pDevice->GetProperties(); try { - ValidateShaderBindingTableDesc(Desc, DeviceProps.ShaderGroupHandleSize, DeviceProps.MaxShaderRecordStride); + ValidateShaderBindingTableDesc(this->m_Desc, DeviceProps.ShaderGroupHandleSize, DeviceProps.MaxShaderRecordStride); } catch (const std::runtime_error&) { return; } - this->m_Desc = Desc; - this->m_Desc.Name = Name; // Restore original name this->m_pPSO = ValidatedCast<PipelineStateImplType>(this->m_Desc.pPSO); this->m_ShaderRecordSize = this->m_pPSO->GetRayTracingPipelineDesc().ShaderRecordSize; this->m_ShaderRecordStride = this->m_ShaderRecordSize + DeviceProps.ShaderGroupHandleSize; } - void DILIGENT_CALL_TYPE BindRayGenShader(const char* ShaderGroupName, const void* Data, Uint32 DataSize) override final + + void DILIGENT_CALL_TYPE ResetHitGroups() override final { - VERIFY_EXPR((Data == nullptr) == (DataSize == 0)); - VERIFY_EXPR((Data == nullptr) || (DataSize == this->m_ShaderRecordSize)); +#ifdef DILIGENT_DEVELOPMENT + this->m_DbgHitGroupBindings.clear(); +#endif + this->m_HitGroupsRecord.clear(); + this->m_Changed = true; + } + + + void DILIGENT_CALL_TYPE BindAll(const BindAllAttribs& Attribs) override final + { + // AZ TODO + } + + + void DILIGENT_CALL_TYPE BindRayGenShader(const char* pShaderGroupName, const void* pData, Uint32 DataSize) override final + { + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); this->m_RayGenShaderRecord.resize(this->m_ShaderRecordStride, Uint8{EmptyElem}); - this->m_pPSO->CopyShaderHandle(ShaderGroupName, this->m_RayGenShaderRecord.data(), this->m_ShaderRecordStride); + this->m_pPSO->CopyShaderHandle(pShaderGroupName, this->m_RayGenShaderRecord.data(), this->m_ShaderRecordStride); const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; - std::memcpy(this->m_RayGenShaderRecord.data() + GroupSize, Data, DataSize); + std::memcpy(this->m_RayGenShaderRecord.data() + GroupSize, pData, DataSize); this->m_Changed = true; } - void DILIGENT_CALL_TYPE BindMissShader(const char* ShaderGroupName, Uint32 MissIndex, const void* Data, Uint32 DataSize) override final + + void DILIGENT_CALL_TYPE BindMissShader(const char* pShaderGroupName, Uint32 MissIndex, const void* pData, Uint32 DataSize) override final { - VERIFY_EXPR((Data == nullptr) == (DataSize == 0)); - VERIFY_EXPR((Data == nullptr) || (DataSize == this->m_ShaderRecordSize)); + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; - const Uint32 Offset = MissIndex * this->m_ShaderRecordStride; - this->m_MissShadersRecord.resize(std::max(this->m_MissShadersRecord.size(), size_t{Offset} + size_t{this->m_ShaderRecordStride}), Uint8{EmptyElem}); + const size_t Stride = this->m_ShaderRecordStride; + const size_t Offset = MissIndex * Stride; + this->m_MissShadersRecord.resize(std::max(this->m_MissShadersRecord.size(), Offset + Stride), Uint8{EmptyElem}); - this->m_pPSO->CopyShaderHandle(ShaderGroupName, this->m_MissShadersRecord.data() + Offset, this->m_ShaderRecordStride); - std::memcpy(this->m_MissShadersRecord.data() + Offset + GroupSize, Data, DataSize); + this->m_pPSO->CopyShaderHandle(pShaderGroupName, this->m_MissShadersRecord.data() + Offset, Stride); + std::memcpy(this->m_MissShadersRecord.data() + Offset + GroupSize, pData, DataSize); this->m_Changed = true; } + void DILIGENT_CALL_TYPE BindHitGroup(ITopLevelAS* pTLAS, - const char* InstanceName, - const char* GeometryName, + const char* pInstanceName, + const char* pGeometryName, Uint32 RayOffsetInHitGroupIndex, - const char* ShaderGroupName, - const void* Data, + const char* pShaderGroupName, + const void* pData, Uint32 DataSize) override final { - VERIFY_EXPR((Data == nullptr) == (DataSize == 0)); - VERIFY_EXPR((Data == nullptr) || (DataSize == this->m_ShaderRecordSize)); + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); VERIFY_EXPR(pTLAS != nullptr); - VERIFY_EXPR(RayOffsetInHitGroupIndex < this->m_Desc.HitShadersPerInstance); - VERIFY_EXPR(pTLAS->GetDesc().BindingMode == SHADER_BINDING_MODE_PER_GEOMETRY); - const auto Desc = pTLAS->GetInstanceDesc(InstanceName); - VERIFY_EXPR(Desc.pBLAS != nullptr); + auto* pTLASImpl = ValidatedCast<TopLevelASImplType>(pTLAS); + const auto Desc = pTLASImpl->GetInstanceDesc(pInstanceName); + + VERIFY_EXPR(pTLASImpl->GetBindingMode() == SHADER_BINDING_MODE_PER_GEOMETRY); + VERIFY_EXPR(RayOffsetInHitGroupIndex < pTLASImpl->GetHitShadersPerInstance()); + VERIFY_EXPR(Desc.ContributionToHitGroupIndex != INVALID_INDEX); + + if (Desc.pBLAS == nullptr) + return; // this is disabled instance const Uint32 InstanceIndex = Desc.ContributionToHitGroupIndex; - const Uint32 GeometryIndex = Desc.pBLAS->GetGeometryIndex(GeometryName); - VERIFY_EXPR(GeometryIndex != ~0u); - const Uint32 Index = InstanceIndex + GeometryIndex * this->m_Desc.HitShadersPerInstance + RayOffsetInHitGroupIndex; - const Uint32 Offset = Index * this->m_ShaderRecordStride; + const Uint32 GeometryIndex = Desc.pBLAS->GetGeometryIndex(pGeometryName); + VERIFY_EXPR(GeometryIndex != INVALID_INDEX); + + const Uint32 Index = InstanceIndex + GeometryIndex * pTLASImpl->GetHitShadersPerInstance() + RayOffsetInHitGroupIndex; + const size_t Stride = this->m_ShaderRecordStride; const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + const size_t Offset = Index * Stride; - this->m_HitGroupsRecord.resize(std::max(this->m_HitGroupsRecord.size(), size_t{Offset} + size_t{this->m_ShaderRecordStride}), Uint8{EmptyElem}); + this->m_HitGroupsRecord.resize(std::max(this->m_HitGroupsRecord.size(), Offset + Stride), Uint8{EmptyElem}); - this->m_pPSO->CopyShaderHandle(ShaderGroupName, this->m_HitGroupsRecord.data() + Offset, this->m_ShaderRecordStride); - std::memcpy(this->m_HitGroupsRecord.data() + Offset + GroupSize, Data, DataSize); + this->m_pPSO->CopyShaderHandle(pShaderGroupName, this->m_HitGroupsRecord.data() + Offset, Stride); + std::memcpy(this->m_HitGroupsRecord.data() + Offset + GroupSize, pData, DataSize); this->m_Changed = true; + +#ifdef DILIGENT_DEVELOPMENT + OnBindHitGroup(pTLASImpl, Index); +#endif } + void DILIGENT_CALL_TYPE BindHitGroups(ITopLevelAS* pTLAS, - const char* InstanceName, + const char* pInstanceName, Uint32 RayOffsetInHitGroupIndex, - const char* ShaderGroupName, - const void* Data, + const char* pShaderGroupName, + const void* pData, Uint32 DataSize) override final { - VERIFY_EXPR((Data == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); VERIFY_EXPR(pTLAS != nullptr); - VERIFY_EXPR(RayOffsetInHitGroupIndex < this->m_Desc.HitShadersPerInstance); - VERIFY_EXPR(pTLAS->GetDesc().BindingMode == SHADER_BINDING_MODE_PER_GEOMETRY || - pTLAS->GetDesc().BindingMode == SHADER_BINDING_MODE_PER_INSTANCE); - const auto Desc = pTLAS->GetInstanceDesc(InstanceName); - VERIFY_EXPR(Desc.pBLAS != nullptr); + auto* pTLASImpl = ValidatedCast<TopLevelASImplType>(pTLAS); + const auto Desc = pTLASImpl->GetInstanceDesc(pInstanceName); + + VERIFY_EXPR(pTLASImpl->GetBindingMode() == SHADER_BINDING_MODE_PER_GEOMETRY || + pTLASImpl->GetBindingMode() == SHADER_BINDING_MODE_PER_INSTANCE); + VERIFY_EXPR(RayOffsetInHitGroupIndex < pTLASImpl->GetHitShadersPerInstance()); + VERIFY_EXPR(Desc.ContributionToHitGroupIndex != INVALID_INDEX); const Uint32 InstanceIndex = Desc.ContributionToHitGroupIndex; - const auto& GeometryDesc = Desc.pBLAS->GetDesc(); Uint32 GeometryCount = 0; - switch (pTLAS->GetDesc().BindingMode) + switch (pTLASImpl->GetBindingMode()) { // clang-format off - case SHADER_BINDING_MODE_PER_GEOMETRY: GeometryCount = GeometryDesc.BoxCount + GeometryDesc.TriangleCount; break; - case SHADER_BINDING_MODE_PER_INSTANCE: GeometryCount = 1; break; + case SHADER_BINDING_MODE_PER_GEOMETRY: GeometryCount = Desc.pBLAS ? Desc.pBLAS->GetActualGeometryCount() : 0; break; + case SHADER_BINDING_MODE_PER_INSTANCE: GeometryCount = 1; break; default: UNEXPECTED("unknown binding mode"); // clang-format on } - VERIFY_EXPR((Data == nullptr) || (DataSize == this->m_ShaderRecordSize * GeometryCount)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize * GeometryCount)); - const Uint32 BeginIndex = InstanceIndex + 0 * this->m_Desc.HitShadersPerInstance + RayOffsetInHitGroupIndex; - const Uint32 EndIndex = InstanceIndex + GeometryCount * this->m_Desc.HitShadersPerInstance + RayOffsetInHitGroupIndex; + const Uint32 BeginIndex = InstanceIndex + RayOffsetInHitGroupIndex; + const size_t EndIndex = InstanceIndex + GeometryCount * pTLASImpl->GetHitShadersPerInstance() + RayOffsetInHitGroupIndex; const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; - const auto* DataPtr = static_cast<const Uint8*>(Data); + const size_t Stride = this->m_ShaderRecordStride; + const auto* DataPtr = static_cast<const Uint8*>(pData); - this->m_HitGroupsRecord.resize(std::max(this->m_HitGroupsRecord.size(), size_t{EndIndex} * size_t{this->m_ShaderRecordStride}), Uint8{EmptyElem}); + this->m_HitGroupsRecord.resize(std::max(this->m_HitGroupsRecord.size(), EndIndex * Stride), Uint8{EmptyElem}); for (Uint32 i = 0; i < GeometryCount; ++i) { - Uint32 Offset = (BeginIndex + i) * this->m_ShaderRecordStride; - this->m_pPSO->CopyShaderHandle(ShaderGroupName, this->m_HitGroupsRecord.data() + Offset, this->m_ShaderRecordStride); + size_t Offset = (BeginIndex + i) * Stride; + this->m_pPSO->CopyShaderHandle(pShaderGroupName, this->m_HitGroupsRecord.data() + Offset, Stride); std::memcpy(this->m_HitGroupsRecord.data() + Offset + GroupSize, DataPtr, this->m_ShaderRecordSize); DataPtr += this->m_ShaderRecordSize; + +#ifdef DILIGENT_DEVELOPMENT + OnBindHitGroup(pTLASImpl, BeginIndex + i); +#endif } this->m_Changed = true; } - void DILIGENT_CALL_TYPE BindCallableShader(const char* ShaderGroupName, + + void DILIGENT_CALL_TYPE BindHitGroupForAll(ITopLevelAS* pTLAS, + Uint32 RayOffsetInHitGroupIndex, + const char* pShaderGroupName, + const void* pData, + Uint32 DataSize) override final + { + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); + VERIFY_EXPR(pTLAS != nullptr); + + auto* pTLASImpl = ValidatedCast<TopLevelASImplType>(pTLAS); + VERIFY_EXPR(pTLASImpl->GetBindingMode() == SHADER_BINDING_MODE_PER_GEOMETRY || + pTLASImpl->GetBindingMode() == SHADER_BINDING_MODE_PER_INSTANCE || + pTLASImpl->GetBindingMode() == SHADER_BINDING_MODE_PER_ACCEL_STRUCT); + VERIFY_EXPR(RayOffsetInHitGroupIndex < pTLASImpl->GetHitShadersPerInstance()); + + Uint32 FirstContributionToHitGroupIndex, LastContributionToHitGroupIndex; + pTLASImpl->GetContributionToHitGroupIndex(FirstContributionToHitGroupIndex, LastContributionToHitGroupIndex); + + const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + const size_t Stride = this->m_ShaderRecordStride; + this->m_HitGroupsRecord.resize(std::max(this->m_HitGroupsRecord.size(), (LastContributionToHitGroupIndex + 1) * Stride), Uint8{EmptyElem}); + this->m_Changed = true; + + for (Uint32 Index = FirstContributionToHitGroupIndex; Index <= LastContributionToHitGroupIndex; ++Index) + { + const size_t Offset = Index * Stride; + this->m_pPSO->CopyShaderHandle(pShaderGroupName, this->m_HitGroupsRecord.data() + Offset, Stride); + std::memcpy(this->m_HitGroupsRecord.data() + Offset + GroupSize, pData, DataSize); + +#ifdef DILIGENT_DEVELOPMENT + OnBindHitGroup(pTLASImpl, Index); +#endif + } + } + + + void DILIGENT_CALL_TYPE BindCallableShader(const char* pShaderGroupName, Uint32 CallableIndex, - const void* Data, + const void* pData, Uint32 DataSize) override final { - VERIFY_EXPR((Data == nullptr) == (DataSize == 0)); - VERIFY_EXPR((Data == nullptr) || (DataSize == this->m_ShaderRecordSize)); + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; - const Uint32 Offset = CallableIndex * this->m_ShaderRecordStride; - this->m_CallableShadersRecord.resize(std::max(this->m_CallableShadersRecord.size(), size_t{Offset} + size_t{this->m_ShaderRecordStride}), Uint8{EmptyElem}); + const size_t Offset = CallableIndex * this->m_ShaderRecordStride; + this->m_CallableShadersRecord.resize(std::max(this->m_CallableShadersRecord.size(), Offset + this->m_ShaderRecordStride), Uint8{EmptyElem}); - this->m_pPSO->CopyShaderHandle(ShaderGroupName, this->m_CallableShadersRecord.data() + Offset, this->m_ShaderRecordStride); - std::memcpy(this->m_CallableShadersRecord.data() + Offset + GroupSize, Data, DataSize); + this->m_pPSO->CopyShaderHandle(pShaderGroupName, this->m_CallableShadersRecord.data() + Offset, this->m_ShaderRecordStride); + std::memcpy(this->m_CallableShadersRecord.data() + Offset + GroupSize, pData, DataSize); this->m_Changed = true; } - Bool DILIGENT_CALL_TYPE Verify() const override final + + Bool DILIGENT_CALL_TYPE Verify(SHADER_BINDING_VALIDATION_FLAGS Flags) const override final { - Uint32 ShCounter = 0; - Uint32 RecCounter = 0; const auto Stride = this->m_ShaderRecordStride; const auto ShSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; - const auto FindPattern = [&ShCounter, &RecCounter, Stride, ShSize](const std::vector<Uint8>& Data, const char* Name) -> bool // + const auto FindPattern = [&](const std::vector<Uint8>& Data, const char* GroupName) -> bool // { for (size_t i = 0; i < Data.size(); i += Stride) { - Uint32 Count = 0; - for (size_t j = 0; j < ShSize; ++j) - Count += (Data[i + j] == EmptyElem); - - if (Count == ShSize) + if (Flags & SHADER_BINDING_VALIDATION_SHADER_ONLY) { - LOG_ERROR_MESSAGE("Shader binding table is not valid: shader in '", Name, "'(", i / Stride, ") is not bound"); - return false; + Uint32 Count = 0; + for (size_t j = 0; j < ShSize; ++j) + Count += (Data[i + j] == EmptyElem); + + if (Count == ShSize) + { + LOG_INFO_MESSAGE("Shader binding table '", this->m_Desc.Name, "' is not valid: shader in '", GroupName, "'(", i / Stride, ") is not bound"); + return false; + } } - Count = 0; - for (size_t j = ShSize; j < Stride; ++j) - Count += (Data[i + j] == EmptyElem); - - if (Count > Stride - ShSize) - LOG_WARNING_MESSAGE("Shader binding table is not valid: shader record data in '", Name, "'(", i / Stride, ") is not initialized"); + if ((Flags & SHADER_BINDING_VALIDATION_SHADER_RECORD) && this->m_ShaderRecordSize > 0) + { + Uint32 Count = 0; + for (size_t j = ShSize; j < Stride; ++j) + Count += (Data[i + j] == EmptyElem); + + // shader record data may not used in shader + if (Count == Stride - ShSize) + { + LOG_INFO_MESSAGE("Shader binding table '", this->m_Desc.Name, "' is not valid: shader record data in '", GroupName, "'(", i / Stride, ") is not initialized"); + return false; + } + } } return true; }; if (m_RayGenShaderRecord.empty()) { - LOG_ERROR_MESSAGE("Shader binding table is not valid: ray generation shader is not bound"); + LOG_INFO_MESSAGE("Shader binding table '", this->m_Desc.Name, "' is not valid: ray generation shader is not bound"); return false; } - if (!FindPattern(m_RayGenShaderRecord, "ray generation") || - !FindPattern(m_MissShadersRecord, "miss") || - !FindPattern(m_CallableShadersRecord, "callable") || - !FindPattern(m_HitGroupsRecord, "hit groups")) - return false; - - return true; +#ifdef DILIGENT_DEVELOPMENT + if (Flags & SHADER_BINDING_VALIDATION_TLAS) + { + for (size_t i = 0; i < m_DbgHitGroupBindings.size(); ++i) + { + auto& Binding = m_DbgHitGroupBindings[i]; + auto pTLAS = Binding.pTLAS.Lock(); + if (!pTLAS) + { + LOG_INFO_MESSAGE("Shader binding table '", this->m_Desc.Name, "' is not valid: TLAS that was used to bind hit group at index (", i, ") was deleted"); + return false; + } + if (pTLAS->GetVersion() != Binding.Version) + { + LOG_INFO_MESSAGE("Shader binding table '", this->m_Desc.Name, "' is not valid: TLAS that was used to bind hit group at index '(", i, + ") with name '", pTLAS->GetDesc().Name, " was changed and no longer compatible with SBT"); + return false; + } + } + } +#endif + + bool valid = true; + valid = valid && FindPattern(m_RayGenShaderRecord, "ray generation"); + valid = valid && FindPattern(m_MissShadersRecord, "miss"); + valid = valid && FindPattern(m_CallableShadersRecord, "callable"); + valid = valid && FindPattern(m_HitGroupsRecord, "hit groups"); + return valid; } + struct BindingTable { const void* pData = nullptr; @@ -362,6 +472,7 @@ public: this->m_Changed = false; } + protected: std::vector<Uint8> m_RayGenShaderRecord; std::vector<Uint8> m_MissShadersRecord; @@ -376,6 +487,25 @@ protected: bool m_Changed = true; static constexpr Uint8 EmptyElem = 0xA7; + +private: +#ifdef DILIGENT_DEVELOPMENT + struct HitGroupBinding + { + RefCntWeakPtr<TopLevelASImplType> pTLAS; + Uint32 Version = ~0u; + }; + mutable std::vector<HitGroupBinding> m_DbgHitGroupBindings; + + void OnBindHitGroup(TopLevelASImplType* pTLAS, Uint32 Index) + { + this->m_DbgHitGroupBindings.resize(Index + 1); + + auto& Binding = this->m_DbgHitGroupBindings[Index]; + Binding.pTLAS = pTLAS; + Binding.Version = pTLAS->GetVersion(); + } +#endif }; } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp index 9fef2399..fa322e30 100644 --- a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp @@ -364,6 +364,58 @@ bool VerifyResourceViewBinding(const ResourceAttribsType& Attribs, return BindingOK; } +template <typename ResourceAttribsType> +bool VerifyTLASResourceBinding(const ResourceAttribsType& Attribs, + SHADER_RESOURCE_VARIABLE_TYPE VarType, + Uint32 ArrayIndex, + const ITopLevelAS* pTLAS, + const IDeviceObject* pCachedAS, + const char* ShaderName = nullptr) +{ + if (!pTLAS) + { + std::stringstream ss; + ss << "Failed to bind resource '" << pTLAS->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". Invalid resource type: TLAS is expected."; + LOG_ERROR_MESSAGE(ss.str()); + return false; + } + + bool BindingOK = true; + + if (VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && pCachedAS != nullptr && pCachedAS != pTLAS) + { + const auto* VarTypeStr = GetShaderVariableTypeLiteralName(VarType); + + std::stringstream ss; + ss << "Non-null resource '" << pCachedAS->GetDesc().Name << "' is already bound to " << VarTypeStr + << " shader variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; + if (ShaderName != nullptr) + { + ss << " in shader '" << ShaderName << '\''; + } + ss << ". Attempting to bind "; + if (pTLAS) + { + ss << "another resource ('" << pTLAS->GetDesc().Name << "')"; + } + else + { + ss << "null"; + } + ss << " is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."; + LOG_ERROR_MESSAGE(ss.str()); + + BindingOK = false; + } + + return BindingOK; +} + inline void VerifyAndCorrectSetArrayArguments(const char* Name, Uint32 ArraySize, Uint32& FirstElement, Uint32& NumElements) { if (FirstElement >= ArraySize) diff --git a/Graphics/GraphicsEngine/include/TopLevelASBase.hpp b/Graphics/GraphicsEngine/include/TopLevelASBase.hpp index efba30c7..bfc89dd2 100644 --- a/Graphics/GraphicsEngine/include/TopLevelASBase.hpp +++ b/Graphics/GraphicsEngine/include/TopLevelASBase.hpp @@ -31,8 +31,10 @@ /// Implementation of the Diligent::TopLevelASBase template class #include <unordered_map> +#include <atomic> #include "TopLevelAS.h" +#include "BottomLevelASBase.hpp" #include "DeviceObjectBase.hpp" #include "RenderDeviceBase.hpp" #include "StringPool.hpp" @@ -53,6 +55,17 @@ void ValidateTopLevelASDesc(const TopLevelASDesc& Desc) noexcept(false); template <class BaseInterface, class BottomLevelASType, class RenderDeviceImplType> class TopLevelASBase : public DeviceObjectBase<BaseInterface, RenderDeviceImplType, TopLevelASDesc> { +private: + struct InstanceDesc + { + Uint32 ContributionToHitGroupIndex = 0; + Uint32 InstanceIndex = 0; + RefCntAutoPtr<BottomLevelASType> pBLAS; +#ifdef DILIGENT_DEVELOPMENT + Uint32 Version = 0; +#endif + }; + public: using TDeviceObjectBase = DeviceObjectBase<BaseInterface, RenderDeviceImplType, TopLevelASDesc>; @@ -76,14 +89,16 @@ public: IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_TopLevelAS, TDeviceObjectBase) - void SetInstanceData(const TLASBuildInstanceData* pInstances, Uint32 InstanceCount, Uint32 HitShadersPerInstance) noexcept + bool SetInstanceData(const TLASBuildInstanceData* pInstances, + const Uint32 InstanceCount, + const Uint32 BaseContributionToHitGroupIndex, + const Uint32 HitShadersPerInstance, + const SHADER_BINDING_MODE BindingMode) noexcept { try { ClearInstanceData(); - this->m_HitShadersPerInstance = HitShadersPerInstance; - size_t StringPoolSize = 0; for (Uint32 i = 0; i < InstanceCount; ++i) { @@ -93,56 +108,113 @@ public: this->m_StringPool.Reserve(StringPoolSize, GetRawAllocator()); - Uint32 InstanceOffset = 0; + Uint32 InstanceOffset = BaseContributionToHitGroupIndex; for (Uint32 i = 0; i < InstanceCount; ++i) { - auto& inst = pInstances[i]; - const char* NameCopy = this->m_StringPool.CopyString(inst.InstanceName); + const auto& Inst = pInstances[i]; + const char* NameCopy = this->m_StringPool.CopyString(Inst.InstanceName); InstanceDesc Desc = {}; - Desc.ContributionToHitGroupIndex = inst.ContributionToHitGroupIndex; - Desc.pBLAS = ValidatedCast<BottomLevelASType>(inst.pBLAS); + Desc.pBLAS = ValidatedCast<BottomLevelASType>(Inst.pBLAS); + Desc.ContributionToHitGroupIndex = Inst.ContributionToHitGroupIndex; + Desc.InstanceIndex = i; + CalculateHitGroupIndex(Desc, InstanceOffset, HitShadersPerInstance, BindingMode); #ifdef DILIGENT_DEVELOPMENT - Desc.Version = Desc.pBLAS->GetVersion(); + Desc.Version = Desc.pBLAS ? Desc.pBLAS->GetVersion() : ~0u; #endif - - if (Desc.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO) - { - Desc.ContributionToHitGroupIndex = InstanceOffset; - auto& BLASDesc = Desc.pBLAS->GetDesc(); - switch (this->m_Desc.BindingMode) - { - // clang-format off - case SHADER_BINDING_MODE_PER_GEOMETRY: InstanceOffset += (BLASDesc.TriangleCount + BLASDesc.BoxCount) * HitShadersPerInstance; break; - case SHADER_BINDING_MODE_PER_INSTANCE: InstanceOffset += HitShadersPerInstance; break; - case SHADER_BINDING_USER_DEFINED: UNEXPECTED("TLAS_INSTANCE_OFFSET_AUTO is not compatible with SHADER_BINDING_USER_DEFINED"); break; - default: UNEXPECTED("Unknown ray tracing shader binding mode"); - // clang-format on - } - } - bool IsUniqueName = this->m_Instances.emplace(NameCopy, Desc).second; if (!IsUniqueName) LOG_ERROR_AND_THROW("Instance name must be unique!"); } VERIFY_EXPR(this->m_StringPool.GetRemainingSize() == 0); + + this->m_HitShadersPerInstance = HitShadersPerInstance; + this->m_FirstContributionToHitGroupIndex = BaseContributionToHitGroupIndex; + this->m_LastContributionToHitGroupIndex = InstanceOffset; + this->m_BindingMode = BindingMode; + +#ifdef DILIGENT_DEVELOPMENT + this->m_DbgVersion.fetch_add(1); +#endif + return true; } catch (...) { +#ifdef DILIGENT_DEVELOPMENT + this->m_DbgVersion.fetch_add(1); +#endif ClearInstanceData(); + return false; } } + bool UpdateInstances(const TLASBuildInstanceData* pInstances, + const Uint32 InstanceCount, + const Uint32 BaseContributionToHitGroupIndex, + const Uint32 HitShadersPerInstance, + const SHADER_BINDING_MODE BindingMode) noexcept + { +#ifdef DILIGENT_DEVELOPMENT + bool Changed = false; +#endif + Uint32 InstanceOffset = BaseContributionToHitGroupIndex; + + for (Uint32 i = 0; i < InstanceCount; ++i) + { + const auto& Inst = pInstances[i]; + auto Iter = this->m_Instances.find(Inst.InstanceName); + + if (Iter == this->m_Instances.end()) + { + UNEXPECTED("Failed to find instance with name '", Inst.InstanceName, "' in instances from previous build"); + return false; + } + + auto& Desc = Iter->second; + const auto PrevIndex = Desc.ContributionToHitGroupIndex; + const auto* pPrevBLAS = Desc.pBLAS.template RawPtr<IBottomLevelAS>(); + + Desc.pBLAS = ValidatedCast<BottomLevelASType>(Inst.pBLAS); + Desc.ContributionToHitGroupIndex = Inst.ContributionToHitGroupIndex; + //Desc.InstanceIndex = i; // keep Desc.InstanceIndex unmodified + CalculateHitGroupIndex(Desc, InstanceOffset, HitShadersPerInstance, BindingMode); + +#ifdef DILIGENT_DEVELOPMENT + Changed = Changed || (pPrevBLAS != Inst.pBLAS); + Changed = Changed || (Desc.pBLAS ? Desc.Version != Desc.pBLAS->GetVersion() : false); + Changed = Changed || (PrevIndex != Desc.ContributionToHitGroupIndex); + Desc.Version = Desc.pBLAS ? Desc.pBLAS->GetVersion() : ~0u; +#endif + } + +#ifdef DILIGENT_DEVELOPMENT + Changed = Changed || (this->m_HitShadersPerInstance != HitShadersPerInstance); + Changed = Changed || (this->m_FirstContributionToHitGroupIndex != BaseContributionToHitGroupIndex); + Changed = Changed || (this->m_LastContributionToHitGroupIndex != InstanceOffset); + Changed = Changed || (this->m_BindingMode != BindingMode); + if (Changed) + this->m_DbgVersion.fetch_add(1); +#endif + this->m_HitShadersPerInstance = HitShadersPerInstance; + this->m_FirstContributionToHitGroupIndex = BaseContributionToHitGroupIndex; + this->m_LastContributionToHitGroupIndex = InstanceOffset; + this->m_BindingMode = BindingMode; + + return true; + } + void CopyInstancceData(const TopLevelASBase& Src) noexcept { ClearInstanceData(); this->m_StringPool.Reserve(Src.m_StringPool.GetReservedSize(), GetRawAllocator()); - this->m_HitShadersPerInstance = Src.m_HitShadersPerInstance; - this->m_Desc.BindingMode = Src.m_Desc.BindingMode; + this->m_HitShadersPerInstance = Src.m_HitShadersPerInstance; + this->m_FirstContributionToHitGroupIndex = Src.m_FirstContributionToHitGroupIndex; + this->m_LastContributionToHitGroupIndex = Src.m_LastContributionToHitGroupIndex; + this->m_BindingMode = Src.m_BindingMode; for (auto& SrcInst : Src.m_Instances) { @@ -151,6 +223,25 @@ public: } VERIFY_EXPR(this->m_StringPool.GetRemainingSize() == 0); + +#ifdef DILIGENT_DEVELOPMENT + this->m_DbgVersion.fetch_add(1); +#endif + } + + Uint32 GetInstanceCount() const + { + return static_cast<Uint32>(this->m_Instances.size()); + } + + Uint32 GetHitShadersPerInstance() const + { + return this->m_HitShadersPerInstance; + } + + SHADER_BINDING_MODE GetBindingMode() const + { + return this->m_BindingMode; } virtual TLASInstanceDesc DILIGENT_CALL_TYPE GetInstanceDesc(const char* Name) const override final @@ -159,23 +250,36 @@ public: TLASInstanceDesc Result = {}; - auto iter = this->m_Instances.find(Name); - if (iter != this->m_Instances.end()) + auto Iter = this->m_Instances.find(Name); + if (Iter != this->m_Instances.end()) { - Result.ContributionToHitGroupIndex = iter->second.ContributionToHitGroupIndex; - Result.pBLAS = iter->second.pBLAS.template RawPtr<IBottomLevelAS>(); + const auto& Inst = Iter->second; + Result.ContributionToHitGroupIndex = Inst.ContributionToHitGroupIndex; + Result.InstanceIndex = Inst.InstanceIndex; + Result.pBLAS = Inst.pBLAS.template RawPtr<IBottomLevelAS>(); } else { - UNEXPECTED("Can't find instance with the specified name ('", Name, "')"); + Result.ContributionToHitGroupIndex = INVALID_INDEX; + Result.InstanceIndex = INVALID_INDEX; + LOG_ERROR_MESSAGE("Can't find instance with the specified name ('", Name, "')"); } return Result; } + virtual void DILIGENT_CALL_TYPE GetContributionToHitGroupIndex(Uint32& FirstContributionToHitGroupIndex, + Uint32& LastContributionToHitGroupIndex) const override final + { + FirstContributionToHitGroupIndex = this->m_FirstContributionToHitGroupIndex; + LastContributionToHitGroupIndex = this->m_LastContributionToHitGroupIndex; + + VERIFY_EXPR(FirstContributionToHitGroupIndex <= LastContributionToHitGroupIndex); + } + virtual void DILIGENT_CALL_TYPE SetState(RESOURCE_STATE State) override final { - VERIFY(State == RESOURCE_STATE_BUILD_AS_READ || State == RESOURCE_STATE_BUILD_AS_WRITE || State == RESOURCE_STATE_RAY_TRACING, + VERIFY(State == RESOURCE_STATE_UNKNOWN || State == RESOURCE_STATE_BUILD_AS_READ || State == RESOURCE_STATE_BUILD_AS_WRITE || State == RESOURCE_STATE_RAY_TRACING, "Unsupported state for top-level acceleration structure"); this->m_State = State; } @@ -186,9 +290,9 @@ public: } /// Implementation of ITopLevelAS::GetScratchBufferSizes(). - virtual ScratchBufferSizes DILIGENT_CALL_TYPE GetScratchBufferSizes() const override + virtual ScratchBufferSizes DILIGENT_CALL_TYPE GetScratchBufferSizes() const override final { - return m_ScratchSize; + return this->m_ScratchSize; } bool IsInKnownState() const @@ -208,66 +312,97 @@ public: { bool result = true; - if (m_Instances.empty()) + if (this->m_Instances.empty()) { LOG_ERROR_MESSAGE("TLAS with name ('", this->m_Desc.Name, "') doesn't have instances, use IDeviceContext::BuildTLAS() or IDeviceContext::CopyTLAS() to initialize TLAS content"); result = false; } // Validate instances - for (const auto& NameAndInst : m_Instances) + for (const auto& NameAndInst : this->m_Instances) { - const InstanceDesc& Inst = NameAndInst.second; - const BottomLevelASDesc& Desc = Inst.pBLAS->GetDesc(); + const InstanceDesc& Inst = NameAndInst.second; + + if (Inst.pBLAS == nullptr) + continue; if (Inst.Version != Inst.pBLAS->GetVersion()) { - LOG_ERROR_MESSAGE("Instance with name ('", NameAndInst.first.GetStr(), "') has BLAS with name ('", Desc.Name, "') that was changed after TLAS build, you must rebuild TLAS"); + LOG_ERROR_MESSAGE("Instance with name ('", NameAndInst.first.GetStr(), "') has BLAS with name ('", Inst.pBLAS->GetDesc().Name, + "') that was changed after TLAS build, you must rebuild TLAS"); result = false; } - if (Inst.pBLAS->GetState() != RESOURCE_STATE_BUILD_AS_READ) + if (Inst.pBLAS->IsInKnownState() && Inst.pBLAS->GetState() != RESOURCE_STATE_BUILD_AS_READ) { - LOG_ERROR_MESSAGE("Instance with name ('", NameAndInst.first.GetStr(), "') has BLAS with name ('", Desc.Name, "') that must be in BUILD_AS_READ state, but current state is ", + LOG_ERROR_MESSAGE("Instance with name ('", NameAndInst.first.GetStr(), "') has BLAS with name ('", Inst.pBLAS->GetDesc().Name, + "') that must be in BUILD_AS_READ state, but current state is ", GetResourceStateFlagString(Inst.pBLAS->GetState())); result = false; } - - if (!Inst.pBLAS->ValidateContent()) - { - LOG_ERROR_MESSAGE("Instance with name ('", NameAndInst.first.GetStr(), "') has BLAS with name ('", Desc.Name, "') that is not valid"); - result = false; - } } return result; } -#endif + + Uint32 GetVersion() const + { + return this->m_DbgVersion.load(); + } +#endif // DILIGENT_DEVELOPMENT private: void ClearInstanceData() { this->m_Instances.clear(); this->m_StringPool.Clear(); + + this->m_BindingMode = SHADER_BINDING_MODE_LAST; + this->m_HitShadersPerInstance = 0; + this->m_FirstContributionToHitGroupIndex = INVALID_INDEX; + this->m_LastContributionToHitGroupIndex = INVALID_INDEX; } -protected: - RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; - Uint32 m_HitShadersPerInstance = 0; + static void CalculateHitGroupIndex(InstanceDesc& Desc, Uint32& InstanceOffset, const Uint32 HitShadersPerInstance, const SHADER_BINDING_MODE BindingMode) + { + static_assert(SHADER_BINDING_MODE_LAST == SHADER_BINDING_USER_DEFINED, "Please update the switch below to handle the new shader binding mode"); - ScratchBufferSizes m_ScratchSize; + if (Desc.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO) + { + Desc.ContributionToHitGroupIndex = InstanceOffset; + switch (BindingMode) + { + // clang-format off + case SHADER_BINDING_MODE_PER_GEOMETRY: InstanceOffset += Desc.pBLAS ? Desc.pBLAS->GetActualGeometryCount() * HitShadersPerInstance : 0; break; + case SHADER_BINDING_MODE_PER_INSTANCE: InstanceOffset += HitShadersPerInstance; break; + case SHADER_BINDING_MODE_PER_ACCEL_STRUCT: /* InstanceOffset is a constant */ break; + case SHADER_BINDING_USER_DEFINED: UNEXPECTED("TLAS_INSTANCE_OFFSET_AUTO is not compatible with SHADER_BINDING_USER_DEFINED"); break; + default: UNEXPECTED("Unknown ray tracing shader binding mode"); + // clang-format on + } + } + else + { + VERIFY(BindingMode == SHADER_BINDING_USER_DEFINED, "BindingMode must be SHADER_BINDING_USER_DEFINED"); + } - StringPool m_StringPool; + constexpr Uint32 MaxIndex = (1u << 24); + VERIFY(Desc.ContributionToHitGroupIndex < MaxIndex, "ContributionToHitGroupIndex must be less than ", MaxIndex); + } - struct InstanceDesc - { - Uint32 ContributionToHitGroupIndex = 0; - RefCntAutoPtr<BottomLevelASType> pBLAS; +protected: + RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; + SHADER_BINDING_MODE m_BindingMode = SHADER_BINDING_MODE_LAST; + Uint32 m_HitShadersPerInstance = 0; + Uint32 m_FirstContributionToHitGroupIndex = INVALID_INDEX; + Uint32 m_LastContributionToHitGroupIndex = INVALID_INDEX; + ScratchBufferSizes m_ScratchSize; + + std::unordered_map<HashMapStringKey, InstanceDesc, HashMapStringKey::Hasher> m_Instances; + StringPool m_StringPool; #ifdef DILIGENT_DEVELOPMENT - Uint32 Version = 0; + std::atomic<Uint32> m_DbgVersion{0}; #endif - }; - std::unordered_map<HashMapStringKey, InstanceDesc, HashMapStringKey::Hasher> m_Instances; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngine/interface/BottomLevelAS.h b/Graphics/GraphicsEngine/interface/BottomLevelAS.h index be9eb0c1..4aafefb9 100644 --- a/Graphics/GraphicsEngine/interface/BottomLevelAS.h +++ b/Graphics/GraphicsEngine/interface/BottomLevelAS.h @@ -44,6 +44,9 @@ static const INTERFACE_ID IID_BottomLevelAS = // clang-format off +static const Uint32 INVALID_INDEX = ~0u; + + /// Defines bottom level acceleration structure triangles description. /// Triangle geometry description. @@ -112,12 +115,15 @@ DILIGENT_TYPED_ENUM(RAYTRACING_BUILD_AS_FLAGS, Uint8) { RAYTRACING_BUILD_AS_NONE = 0, - /// AZ TODO: not supported yet + /// Indicates that the specified acceleration structure can be updated + /// via IDeviceContext::BuildBLAS() or IDeviceContext::BuildTLAS(). + /// With this flag acculeration structure may allocate more memory and take more time on build. RAYTRACING_BUILD_AS_ALLOW_UPDATE = 0x01, /// Indicates that the specified acceleration structure can act as the source for /// a copy acceleration structure command IDeviceContext::CopyBLAS() or IDeviceContext::CopyTLAS() /// with COPY_AS_MODE_COMPACT mode to produce a compacted acceleration structure. + /// With this flag acculeration structure may allocate more memory and take more time on build. RAYTRACING_BUILD_AS_ALLOW_COMPACTION = 0x02, /// Indicates that the given acceleration structure build should prioritize trace performance over build time. @@ -170,10 +176,15 @@ typedef struct BottomLevelASDesc BottomLevelASDesc; /// Defines the scratch buffer info for acceleration structure. struct ScratchBufferSizes { - /// Scratch buffer size for acceleration structure building. + /// Scratch buffer size for acceleration structure building, + /// see IDeviceContext::BuildBLAS(), IDeviceContext::BuildTLAS(). + /// May be zero if acceleration structure created with non-zero CompactedSize. Uint32 Build DEFAULT_INITIALIZER(0); - - /// AZ TODO: not supported yet + + /// Scratch buffer size for acceleration structure updating, + /// see IDeviceContext::BuildBLAS(), IDeviceContext::BuildTLAS(). + /// May be zero if acceleration structure created without RAYTRACING_BUILD_AS_ALLOW_UPDATE flag. + /// May be zero if acceleration structure created with non-zero CompactedSize. Uint32 Update DEFAULT_INITIALIZER(0); #if DILIGENT_CPP_INTERFACE @@ -182,7 +193,6 @@ struct ScratchBufferSizes }; typedef struct ScratchBufferSizes ScratchBufferSizes; -static const Uint32 InvalidGeometryIndex = ~0u; #define DILIGENT_INTERFACE_NAME IBottomLevelAS #include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" @@ -201,24 +211,48 @@ DILIGENT_BEGIN_INTERFACE(IBottomLevelAS, IDeviceObject) virtual const BottomLevelASDesc& DILIGENT_CALL_TYPE GetDesc() const override = 0; #endif + /// Returns the geometry description index in BottomLevelASDesc::pTriangles or BottomLevelASDesc::pBoxes. + + /// \param [in] Name - Geometry name that is specified in BLASTriangleDesc or BLASBoundingBoxDesc. + /// \return Geometry index or INVALID_INDEX if geometry does not exist. + /// + /// \note Access to the BLAS must be externally synchronized. + VIRTUAL Uint32 METHOD(GetGeometryDescIndex)(THIS_ + const char* Name) CONST PURE; + + /// Returns the geometry index that can be used in a shader binding table. /// \param [in] Name - Geometry name that is specified in BLASTriangleDesc or BLASBoundingBoxDesc. - /// \return Geometry index. + /// \return Geometry index or INVALID_INDEX if geometry does not exist. + /// + /// \note Access to the BLAS must be externally synchronized. VIRTUAL Uint32 METHOD(GetGeometryIndex)(THIS_ const char* Name) CONST PURE; + + /// Returns the geometry count that was used to build AS. + /// Same as BuildBLASAttribs::TriangleDataCount or BuildBLASAttribs::BoxDataCount. + + /// \return The number of geometries that was used to build AS. + /// + /// \note Access to the BLAS must be externally synchronized. + VIRTUAL Uint32 METHOD(GetActualGeometryCount)(THIS) CONST PURE; + + /// Returns the scratch buffer info for the current acceleration structure. /// \return ScratchBufferSizes object, see Diligent::ScratchBufferSizes. VIRTUAL ScratchBufferSizes METHOD(GetScratchBufferSizes)(THIS) CONST PURE; + /// Returns the native acceleration structure handle specific to the underlying graphics API /// \return pointer to ID3D12Resource interface, for D3D12 implementation\n - /// VkAccelerationStructureKHR handle, for Vulkan implementation + /// VkAccelerationStructure handle, for Vulkan implementation VIRTUAL void* METHOD(GetNativeHandle)(THIS) PURE; + /// Sets the acceleration structure usage state. /// \note This method does not perform state transition, but @@ -229,6 +263,7 @@ DILIGENT_BEGIN_INTERFACE(IBottomLevelAS, IDeviceObject) VIRTUAL void METHOD(SetState)(THIS_ RESOURCE_STATE State) PURE; + /// Returns the internal acceleration structure state VIRTUAL RESOURCE_STATE METHOD(GetState)(THIS) CONST PURE; }; @@ -240,11 +275,13 @@ DILIGENT_END_INTERFACE // clang-format off -# define IBottomLevelAS_GetGeometryIndex(This, ...) CALL_IFACE_METHOD(BottomLevelAS, GetGeometryIndex, This, __VA_ARGS__) -# define IBottomLevelAS_GetScratchBufferSizes(This) CALL_IFACE_METHOD(BottomLevelAS, GetScratchBufferSizes, This) -# define IBottomLevelAS_GetNativeHandle(This) CALL_IFACE_METHOD(BottomLevelAS, GetNativeHandle, This) -# define IBottomLevelAS_SetState(This, ...) CALL_IFACE_METHOD(BottomLevelAS, SetState, This, __VA_ARGS__) -# define IBottomLevelAS_GetState(This) CALL_IFACE_METHOD(BottomLevelAS, GetState, This) +# define IBottomLevelAS_GetGeometryDescIndex(This, ...) CALL_IFACE_METHOD(BottomLevelAS, GetGeometryDescIndex, This, __VA_ARGS__) +# define IBottomLevelAS_GetGeometryIndex(This, ...) CALL_IFACE_METHOD(BottomLevelAS, GetGeometryIndex, This, __VA_ARGS__) +# define IBottomLevelAS_GetActualGeometryCount(This) CALL_IFACE_METHOD(BottomLevelAS, GetActualGeometryCount, This) +# define IBottomLevelAS_GetScratchBufferSizes(This) CALL_IFACE_METHOD(BottomLevelAS, GetScratchBufferSizes, This) +# define IBottomLevelAS_GetNativeHandle(This) CALL_IFACE_METHOD(BottomLevelAS, GetNativeHandle, This) +# define IBottomLevelAS_SetState(This, ...) CALL_IFACE_METHOD(BottomLevelAS, SetState, This, __VA_ARGS__) +# define IBottomLevelAS_GetState(This) CALL_IFACE_METHOD(BottomLevelAS, GetState, This) // clang-format on diff --git a/Graphics/GraphicsEngine/interface/DeviceContext.h b/Graphics/GraphicsEngine/interface/DeviceContext.h index de0ce74f..83b67e70 100644 --- a/Graphics/GraphicsEngine/interface/DeviceContext.h +++ b/Graphics/GraphicsEngine/interface/DeviceContext.h @@ -805,10 +805,11 @@ struct BLASBuildTriangleData Uint32 VertexCount DEFAULT_INITIALIZER(0); /// The type of the vertex components. - /// This is an optional values. Must be undefined or same as in BLASTriangleDesc. + /// This is an optional value. Must be undefined or same as in BLASTriangleDesc. VALUE_TYPE VertexValueType DEFAULT_INITIALIZER(VT_UNDEFINED); - /// The number of vertex components + /// The number of vertex components. + /// This is an optional value. Must be undefined or same as in BLASTriangleDesc. Uint8 VertexComponentCount DEFAULT_INITIALIZER(0); /// The number of triangles. @@ -881,6 +882,7 @@ typedef struct BLASBuildBoundingBoxData BLASBuildBoundingBoxData; struct BuildBLASAttribs { /// Target bottom-level AS. + /// Access to the BLAS must be externally synchronized. IBottomLevelAS* pBLAS DEFAULT_INITIALIZER(nullptr); /// Bottom-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). @@ -890,17 +892,27 @@ struct BuildBLASAttribs RESOURCE_STATE_TRANSITION_MODE GeometryTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); /// A pointer to an array of TriangleDataCount BLASBuildTriangleData structures that contains triangle geometry data. + /// If Update is true: + /// - Only vertex positions (in pVertexBuffer) and transformation (in pTransformBuffer) can be changed. + /// - All other content in BLASBuildTriangleData and buffers must be same as used to build BLAS. + /// - To disable geometry make all triangles inactive, see BLASBuildTriangleData::pVertexBuffer description. BLASBuildTriangleData const* pTriangleData DEFAULT_INITIALIZER(nullptr); /// The number of triangle grometries. /// Must be less than or equal to BottomLevelASDesc::TriangleCount. + /// If Update is true then count must be the same as used to build BLAS. Uint32 TriangleDataCount DEFAULT_INITIALIZER(0); /// A pointer to an array of BoxDataCount BLASBuildBoundingBoxData structures that contain AABB geometry data. + /// If Update is true: + /// - AABB coordinates (in pBoxBuffer) can be changed. + /// - All other content in BLASBuildBoundingBoxData must be same as used to build BLAS. + /// - To disable geometry make all AAABBs inactive, see BLASBuildBoundingBoxData::pBoxBuffer description. BLASBuildBoundingBoxData const* pBoxData DEFAULT_INITIALIZER(nullptr); /// The number of AABB geometries. /// Must be less than or equal to BottomLevelASDesc::BoxCount. + /// If Update is true then count must be the same as used to build BLAS. Uint32 BoxDataCount DEFAULT_INITIALIZER(0); /// The buffer that is used for acceleration structure building. @@ -914,6 +926,12 @@ struct BuildBLASAttribs /// Scratch buffer state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). RESOURCE_STATE_TRANSITION_MODE ScratchBufferTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + /// if false then BLAS will be built from scratch. + /// If true then previous content of BLAS will be updated. + /// pBLAS must be created with RAYTRACING_BUILD_AS_ALLOW_UPDATE flag. + /// An update will be faster than building an acceleration structure from scratch. + Bool Update DEFAULT_INITIALIZER(False); + #if DILIGENT_CPP_INTERFACE BuildBLASAttribs() noexcept {} #endif @@ -921,10 +939,11 @@ struct BuildBLASAttribs typedef struct BuildBLASAttribs BuildBLASAttribs; -/// Can be used in TLASBuildInstanceData::ContributionToHitGroupIndex to calculate the index -/// depending on geometry count in TLASBuildInstanceData::pBLAS and shader binding mode in TopLevelASDesc::BindingMode. +/// Can be used to calculate the TLASBuildInstanceData::ContributionToHitGroupIndex depending on instance count, +/// geometry count in each instance (in TLASBuildInstanceData::pBLAS) and shader binding mode in BuildTLASAttribs::BindingMode. /// /// Example: +/// InstanceOffset = BaseContributionToHitGroupIndex; /// For each instance in TLAS /// if (Instance.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO) /// Instance.ContributionToHitGroupIndex = InstanceOffset; @@ -981,6 +1000,8 @@ struct TLASBuildInstanceData const char* InstanceName DEFAULT_INITIALIZER(nullptr); /// Bottom-level AS that represents instance geometry. + /// Once built, TLAS will hold strong reference to pBLAS until next build or copy operation. + /// Access to the BLAS must be externally synchronized. IBottomLevelAS* pBLAS DEFAULT_INITIALIZER(nullptr); /// Instace to world transformation. @@ -994,12 +1015,11 @@ struct TLASBuildInstanceData RAYTRACING_INSTANCE_FLAGS Flags DEFAULT_INITIALIZER(RAYTRACING_INSTANCE_NONE); /// Visibility mask for the geometry, the instance may only be hit if rayMask & instance.Mask != 0. - /// (rayMask in GLSL is a cullMask argument of traceRayEXT(), rayMask in HLSL is an InstanceInclusionMask argument of TraceRay()). + /// ('rayMask' in GLSL is a 'cullMask' argument of traceRay(), 'rayMask' in HLSL is an 'InstanceInclusionMask' argument of TraceRay()). Uint8 Mask DEFAULT_INITIALIZER(0xFF); /// The index used to calculate the hit group location in the shader binding table. - /// Must be TLAS_INSTANCE_OFFSET_AUTO if TLAS is created with BindingMode SHADER_BINDING_MODE_PER_GEOMETRY, - /// or SHADER_BINDING_MODE_PER_INSTANCE otherwise. + /// Must be TLAS_INSTANCE_OFFSET_AUTO if BuildTLASAttribs::BindingMode that is not a SHADER_BINDING_USER_DEFINED. /// Only the lower 24 bits are used. Uint32 ContributionToHitGroupIndex DEFAULT_INITIALIZER(TLAS_INSTANCE_OFFSET_AUTO); @@ -1010,15 +1030,38 @@ struct TLASBuildInstanceData typedef struct TLASBuildInstanceData TLASBuildInstanceData; -/// Instance size in GPU side. +/// Top-level AS instance size in bytes in GPU side. /// Used to calculate size of BuildTLASAttribs::pInstanceBuffer. static const Uint32 TLAS_INSTANCE_DATA_SIZE = 64; +/// Defines shader binding mode. +DILIGENT_TYPED_ENUM(SHADER_BINDING_MODE, Uint8) +{ + /// Each geometry in each instance can have a unique hit shader. + /// See IShaderBindingTable::BindHitGroup(). + SHADER_BINDING_MODE_PER_GEOMETRY = 0, + + /// Each instance can have a unique hit shader. In this mode SBT buffer will use less memory. + /// See IShaderBindingTable::BindHitGroups(). + SHADER_BINDING_MODE_PER_INSTANCE, + + /// Single hit shader for top-level acceleration structure. + /// See IShaderBindingTable::BindHitGroupForAll(). + SHADER_BINDING_MODE_PER_ACCEL_STRUCT, + + /// The user must specify TLASBuildInstanceData::ContributionToHitGroupIndex and only use IShaderBindingTable::BindAll(). + SHADER_BINDING_USER_DEFINED, + + SHADER_BINDING_MODE_LAST = SHADER_BINDING_USER_DEFINED, +}; + + /// This structure is used by IDeviceContext::BuildTLAS(). struct BuildTLASAttribs { /// Target top-level AS. + /// Access to the TLAS must be externally synchronized. ITopLevelAS* pTLAS DEFAULT_INITIALIZER(nullptr); /// Top-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). @@ -1028,10 +1071,14 @@ struct BuildTLASAttribs RESOURCE_STATE_TRANSITION_MODE BLASTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); /// A pointer to an array of InstanceCount TLASBuildInstanceData structures that contain instance data. + /// If Update is true: + /// - Any instance data can be changed. + /// - To disable instance set pBLAS to null. TLASBuildInstanceData const* pInstances DEFAULT_INITIALIZER(nullptr); /// The number of instances. /// Must be less than or equal to TopLevelASDesc::MaxInstanceCount. + /// If Update is true then count must be the same as used to build TLAS. Uint32 InstanceCount DEFAULT_INITIALIZER(0); /// The buffer that will be used to store instance data during AS building. @@ -1044,13 +1091,28 @@ struct BuildTLASAttribs /// Instance buffer state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). RESOURCE_STATE_TRANSITION_MODE InstanceBufferTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); - - /// AZ TODO + + /// The number of hit shaders that can be binded for single geometry or instance (depend on BindingMode). + /// Used to calculate TLASBuildInstanceData::ContributionToHitGroupIndex. + /// Ignored if BindingMode is SHADER_BINDING_USER_DEFINED. + /// You should use the same value in shader: + /// 'MultiplierForGeometryContributionToHitGroupIndex' argument in TraceRay() in HLSL, 'sbtRecordStride' argument in traceRay() in GLSL. Uint32 HitShadersPerInstance DEFAULT_INITIALIZER(1); + /// Base offset for hit group location. + /// Can be used to bind hit shaders for multiple acceleration structures, see IShaderBindingTable::BindHitGroup(). + /// Used to calculate TLASBuildInstanceData::ContributionToHitGroupIndex. + /// Ignored if BindingMode is SHADER_BINDING_USER_DEFINED. + Uint32 BaseContributionToHitGroupIndex DEFAULT_INITIALIZER(0); + + /// Hit shader binding mode, see Diligent::SHADER_BINDING_MODE. + /// Used to calculate TLASBuildInstanceData::ContributionToHitGroupIndex. + SHADER_BINDING_MODE BindingMode DEFAULT_INITIALIZER(SHADER_BINDING_MODE_PER_GEOMETRY); + /// Buffer that is used for acceleration structure building. /// Must be created with BIND_RAY_TRACING. /// Call ITopLevelAS::GetScratchBufferSizes().Build to get the minimal size for the scratch buffer. + /// Access to the TLAS must be externally synchronized. IBuffer* pScratchBuffer DEFAULT_INITIALIZER(nullptr); /// Offset from the beginning of the buffer. @@ -1059,6 +1121,12 @@ struct BuildTLASAttribs /// Scratch buffer state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). RESOURCE_STATE_TRANSITION_MODE ScratchBufferTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + /// if false then TLAS will be built from scratch. + /// If true then previous content of TLAS will be updated. + /// pTLAS must be created with RAYTRACING_BUILD_AS_ALLOW_UPDATE flag. + /// An update will be faster than building an acceleration structure from scratch. + Bool Update DEFAULT_INITIALIZER(False); + #if DILIGENT_CPP_INTERFACE BuildTLASAttribs() noexcept {} #endif @@ -1070,11 +1138,13 @@ typedef struct BuildTLASAttribs BuildTLASAttribs; struct CopyBLASAttribs { /// Source bottom-level AS. + /// Access to the BLAS must be externally synchronized. IBottomLevelAS* pSrc DEFAULT_INITIALIZER(nullptr); /// Destination bottom-level AS. /// If Mode is COPY_AS_MODE_COMPACT then pDst must be created with CompactedSize /// that is greater or equal to the size returned by IDeviceContext::WriteBLASCompactedSize. + /// Access to the BLAS must be externally synchronized. IBottomLevelAS* pDst DEFAULT_INITIALIZER(nullptr); /// Acceleration structure copy mode, see Diligent::COPY_AS_MODE. @@ -1097,11 +1167,13 @@ typedef struct CopyBLASAttribs CopyBLASAttribs; struct CopyTLASAttribs { /// Source top-level AS. + /// Access to the TLAS must be externally synchronized. ITopLevelAS* pSrc DEFAULT_INITIALIZER(nullptr); /// Destination top-level AS. /// If Mode is COPY_AS_MODE_COMPACT then pDst must be created with CompactedSize /// that is greater or equal to size that returned by IDeviceContext::WriteTLASCompactedSize. + /// Access to the TLAS must be externally synchronized. ITopLevelAS* pDst DEFAULT_INITIALIZER(nullptr); /// Acceleration structure copy mode, see Diligent::COPY_AS_MODE. diff --git a/Graphics/GraphicsEngine/interface/PipelineState.h b/Graphics/GraphicsEngine/interface/PipelineState.h index 7e592ba7..56e72643 100644 --- a/Graphics/GraphicsEngine/interface/PipelineState.h +++ b/Graphics/GraphicsEngine/interface/PipelineState.h @@ -472,12 +472,15 @@ struct RayTracingPipelineStateCreateInfo DILIGENT_DERIVE(PipelineStateCreateInfo /// Direct3D12 only: the name of the constant buffer that will be used by the local root signature. /// Ignored if RayTracingPipelineDesc::ShaderRecordSize is zero. + /// In Vulkan backend in HLSL add [[vk::shader_record_nv]] attribute to the constant buffer, in GLSL add shaderRecord layout to buffer. const char* pShaderRecordName DEFAULT_INITIALIZER(nullptr); /// Direct3D12 only: the maximum hit shader attribute size in bytes. + /// If zero then maximum allowed size will be used. Uint32 MaxAttributeSize DEFAULT_INITIALIZER(0); /// Direct3D12 only: the maximum payload size in bytes. + /// If zero then maximum allowed size will be used. Uint32 MaxPayloadSize DEFAULT_INITIALIZER(0); }; typedef struct RayTracingPipelineStateCreateInfo RayTracingPipelineStateCreateInfo; @@ -595,12 +598,9 @@ DILIGENT_BEGIN_INTERFACE(IPipelineState, IDeviceObject) /// This method must only be called for a ray tracing pipeline. /// \param [in] Name - Shader group name. + /// \return Shader group index or INVALID_INDEX if group does not exist. VIRTUAL Uint32 METHOD(GetShaderGroupIndex)(THIS_ const char* Name) CONST PURE; - - - /// AZ TODO: remove ? - VIRTUAL Uint32 METHOD(GetShaderGroupCount)(THIS) CONST PURE; }; DILIGENT_END_INTERFACE @@ -621,7 +621,6 @@ DILIGENT_END_INTERFACE # define IPipelineState_CreateShaderResourceBinding(This, ...) CALL_IFACE_METHOD(PipelineState, CreateShaderResourceBinding, This, __VA_ARGS__) # define IPipelineState_IsCompatibleWith(This, ...) CALL_IFACE_METHOD(PipelineState, IsCompatibleWith, This, __VA_ARGS__) # define IPipelineState_GetShaderGroupIndex(This, ...) CALL_IFACE_METHOD(PipelineState, GetShaderGroupIndex, This, __VA_ARGS__) -# define IPipelineState_GetShaderGroupCount(This) CALL_IFACE_METHOD(PipelineState, GetShaderGroupCount, This) // clang-format on diff --git a/Graphics/GraphicsEngine/interface/ShaderBindingTable.h b/Graphics/GraphicsEngine/interface/ShaderBindingTable.h index da650349..56455989 100644 --- a/Graphics/GraphicsEngine/interface/ShaderBindingTable.h +++ b/Graphics/GraphicsEngine/interface/ShaderBindingTable.h @@ -52,15 +52,32 @@ struct ShaderBindingTableDesc DILIGENT_DERIVE(DeviceObjectAttribs) /// Ray tracing pipeline state object from which shaders will be taken. IPipelineState* pPSO DEFAULT_INITIALIZER(nullptr); - /// AZ TODO - Uint32 HitShadersPerInstance DEFAULT_INITIALIZER(1); - #if DILIGENT_CPP_INTERFACE ShaderBindingTableDesc() noexcept {} #endif }; typedef struct ShaderBindingTableDesc ShaderBindingTableDesc; + +/// Defines shader binding table validation flags, see IShaderBindingTable::Verify(). +DILIGENT_TYPED_ENUM(SHADER_BINDING_VALIDATION_FLAGS, Uint8) +{ + /// Checks that all shaders are binded or inactive. + SHADER_BINDING_VALIDATION_SHADER_ONLY = 0x1, + + /// AZ TODO + SHADER_BINDING_VALIDATION_SHADER_RECORD = 0x2, + + /// AZ TODO + SHADER_BINDING_VALIDATION_TLAS = 0x4, + + SHADER_BINDING_VALIDATION_ALL = SHADER_BINDING_VALIDATION_SHADER_ONLY | + SHADER_BINDING_VALIDATION_SHADER_RECORD | + SHADER_BINDING_VALIDATION_TLAS +}; +DEFINE_FLAG_ENUM_OPERATORS(SHADER_BINDING_VALIDATION_FLAGS) + + /// AZ TODO struct BindAllAttribs { @@ -110,56 +127,147 @@ DILIGENT_BEGIN_INTERFACE(IShaderBindingTable, IDeviceObject) virtual const ShaderBindingTableDesc& DILIGENT_CALL_TYPE GetDesc() const override = 0; #endif - /// AZ TODO - VIRTUAL Bool METHOD(Verify)(THIS) CONST PURE; + /// Check that all shaders are binded, instances and geometries are not changed, shader record data are initialized. - /// AZ TODO + /// \param [in] Flags - Flags that used for validation. + /// \return True if SBT content are valid. + /// + /// \note Access to the SBT must be externally synchronized. + VIRTUAL Bool METHOD(Verify)(THIS_ + SHADER_BINDING_VALIDATION_FLAGS Flags) CONST PURE; + + + /// Reset SBT with new pipeline state. This is more effectively than creating new SBT. + + /// \note Access to the SBT must be externally synchronized. VIRTUAL void METHOD(Reset)(THIS_ - const ShaderBindingTableDesc REF Desc) PURE; + IPipelineState* pPSO) PURE; - /// AZ TODO - VIRTUAL void METHOD(ResetHitGroups)(THIS_ - Uint32 HitShadersPerInstance) PURE; + + /// When TLAS or BLAS was rebuilded or updated, hit group shader bindings may become invalid, + /// you can reset only hit groups and keep ray-gen, miss and callable shader bindings. - /// AZ TODO + /// \note Access to the SBT must be externally synchronized. + VIRTUAL void METHOD(ResetHitGroups)(THIS) PURE; + + + /// Bind ray-generation shader. + + /// \param [in] pShaderGroupName - ray-generation shader name that specified in RayTracingGeneralShaderGroup::Name. + /// \param [in] pData - shader record data, can be null. + /// \param [in] DataSize - shader record data size, should equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. VIRTUAL void METHOD(BindRayGenShader)(THIS_ - const char* ShaderGroupName, - const void* Data DEFAULT_INITIALIZER(nullptr), + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; - /// AZ TODO + + /// Bind ray-miss shader. + + /// \param [in] pShaderGroupName - ray-miss shader name that specified in RayTracingGeneralShaderGroup::Name, + /// can be null to make shader inactive. + /// \param [in] MissIndex - miss shader offset in shader binding table, use the same value as in shader: + /// 'MissShaderIndex' argument in TraceRay() in HLSL, 'missIndex' in traceRay() in GLSL. + /// \param [in] pData - shader record data, can be null. + /// \param [in] DataSize - shader record data size, should equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. VIRTUAL void METHOD(BindMissShader)(THIS_ - const char* ShaderGroupName, + const char* pShaderGroupName, Uint32 MissIndex, - const void* Data DEFAULT_INITIALIZER(nullptr), + const void* pData DEFAULT_INITIALIZER(nullptr), Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; - /// AZ TODO + + /// Bind hit group for specified geometry in instance. + + /// \param [in] pTLAS - top-level AS, used to calculate offset for instance. + /// \param [in] pInstanceName - instance name, see TLASBuildInstanceData::InstanceName. + /// \param [in] pGeometryName - geometry name, see BLASBuildTriangleData::GeometryName and BLASBuildBoundingBoxData::GeometryName. + /// \param [in] RayOffsetInHitGroupIndex - ray offset in shader binding table, use the same value as in shader: + /// 'RayContributionToHitGroupIndex' argument in TraceRay() in HLSL, 'sbtRecordOffset' argument in traceRay() in GLSL. + /// Must be less than HitShadersPerInstance. + /// \param [in] pShaderGroupName - hit group name that specified in RayTracingTriangleHitShaderGroup::Name and RayTracingProceduralHitShaderGroup::Name, + /// can be null to make shader inactive. + /// \param [in] pData - shader record data, can be null. + /// \param [in] DataSize - shader record data size, should equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. + /// Access to the TLAS must be externally synchronized. + /// Access to the BLAS that was used in TLAS instance with name pInstanceName must be externally synchronized. VIRTUAL void METHOD(BindHitGroup)(THIS_ ITopLevelAS* pTLAS, - const char* InstanceName, - const char* GeometryName, + const char* pInstanceName, + const char* pGeometryName, Uint32 RayOffsetInHitGroupIndex, - const char* ShaderGroupName, - const void* Data DEFAULT_INITIALIZER(nullptr), + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; - /// AZ TODO + + /// Bind hit group for each geometries in specified instance. + + /// \param [in] pTLAS - top-level AS, used to calculate offset for instance. + /// \param [in] pInstanceName - instance name, see TLASBuildInstanceData::InstanceName. + /// \param [in] RayOffsetInHitGroupIndex - ray offset in shader binding table, use the same value as in shader: + /// 'RayContributionToHitGroupIndex' argument in TraceRay() in HLSL, 'sbtRecordOffset' argument in traceRay() in GLSL. + /// Must be less than HitShadersPerInstance. + /// \param [in] pShaderGroupName - hit group name that specified in RayTracingTriangleHitShaderGroup::Name and RayTracingProceduralHitShaderGroup::Name, + /// can be null to make shader inactive. + /// \param [in] pData - shader record data, can be null. + /// \param [in] DataSize - shader record data size, should equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. + /// Access to the TLAS must be externally synchronized. VIRTUAL void METHOD(BindHitGroups)(THIS_ ITopLevelAS* pTLAS, - const char* InstanceName, + const char* pInstanceName, Uint32 RayOffsetInHitGroupIndex, - const char* ShaderGroupName, - const void* Data DEFAULT_INITIALIZER(nullptr), + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; - /// AZ TODO + + /// Bind hit group for each instances in top-level AS. + + /// \param [in] pTLAS - top-level AS, used to calculate offset for instance. + /// \param [in] RayOffsetInHitGroupIndex - ray offset in shader binding table, use the same value as in shader: + /// 'RayContributionToHitGroupIndex' argument in TraceRay() in HLSL, 'sbtRecordOffset' argument in traceRay() in GLSL. + /// Must be less than HitShadersPerInstance. + /// \param [in] pShaderGroupName - hit group name that specified in RayTracingTriangleHitShaderGroup::Name and RayTracingProceduralHitShaderGroup::Name, + /// can be null to make shader inactive. + /// \param [in] pData - shader record data, can be null. + /// \param [in] DataSize - shader record data size, should equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. + /// Access to the TLAS must be externally synchronized. + VIRTUAL void METHOD(BindHitGroupForAll)(THIS_ + ITopLevelAS* pTLAS, + Uint32 RayOffsetInHitGroupIndex, + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), + Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; + + + /// Bind callable shader. + + /// \param [in] pShaderGroupName - callable shader name that specified in RayTracingGeneralShaderGroup::Name, + /// can be null to make shader inactive. + /// \param [in] CallableIndex - callable shader offset in shader binding table, use the same value as in shader: + /// 'ShaderIndex' argument in CallShader() in HLSL, 'callable' argument in executeCallable() in GLSL. + /// \param [in] pData - shader record data, can be null. + /// \param [in] DataSize - shader record data size, should equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. VIRTUAL void METHOD(BindCallableShader)(THIS_ - const char* ShaderGroupName, + const char* pShaderGroupName, Uint32 CallableIndex, - const void* Data DEFAULT_INITIALIZER(nullptr), + const void* pData DEFAULT_INITIALIZER(nullptr), Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; + /// AZ TODO VIRTUAL void METHOD(BindAll)(THIS_ const BindAllAttribs REF Attribs) PURE; @@ -172,13 +280,14 @@ DILIGENT_END_INTERFACE // clang-format off -# define IShaderBindingTable_Verify(This) CALL_IFACE_METHOD(ShaderBindingTable, Verify, This) +# define IShaderBindingTable_Verify(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, Verify, This, __VA_ARGS__) # define IShaderBindingTable_Reset(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, Reset, This, __VA_ARGS__) -# define IShaderBindingTable_ResetHitGroups(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, ResetHitGroups, This, __VA_ARGS__) +# define IShaderBindingTable_ResetHitGroups(This) CALL_IFACE_METHOD(ShaderBindingTable, ResetHitGroups, This) # define IShaderBindingTable_BindRayGenShader(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindRayGenShader, This, __VA_ARGS__) # define IShaderBindingTable_BindMissShader(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindMissShader, This, __VA_ARGS__) # define IShaderBindingTable_BindHitGroup(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroup, This, __VA_ARGS__) # define IShaderBindingTable_BindHitGroups(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroups, This, __VA_ARGS__) +# define IShaderBindingTable_BindHitGroupForAll(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroupForAll, This, __VA_ARGS__) # define IShaderBindingTable_BindCallableShader(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindCallableShader, This, __VA_ARGS__) # define IShaderBindingTable_BindAll(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindAll, This, __VA_ARGS__) diff --git a/Graphics/GraphicsEngine/interface/TopLevelAS.h b/Graphics/GraphicsEngine/interface/TopLevelAS.h index 0c02b920..6eaee93b 100644 --- a/Graphics/GraphicsEngine/interface/TopLevelAS.h +++ b/Graphics/GraphicsEngine/interface/TopLevelAS.h @@ -45,20 +45,6 @@ static const INTERFACE_ID IID_TopLevelAS = // clang-format off -/// Defines shader binding mode. -DILIGENT_TYPED_ENUM(SHADER_BINDING_MODE, Uint8) -{ - /// Each geometry in each instance can have a unique shader. - SHADER_BINDING_MODE_PER_GEOMETRY = 0, - - /// Each instance can have a unique shader. In this mode SBT buffer will use less memory. - SHADER_BINDING_MODE_PER_INSTANCE, - - /// The user must specify TLASBuildInstanceData::InstanceContributionToHitGroupIndex and only use IShaderBindingTable::BindAll(). - SHADER_BINDING_USER_DEFINED, -}; - - /// Top-level AS description. struct TopLevelASDesc DILIGENT_DERIVE(DeviceObjectAttribs) @@ -69,12 +55,8 @@ struct TopLevelASDesc DILIGENT_DERIVE(DeviceObjectAttribs) RAYTRACING_BUILD_AS_FLAGS Flags DEFAULT_INITIALIZER(RAYTRACING_BUILD_AS_NONE); /// The size returned by IDeviceContext::WriteTLASCompactedSize(), if this acceleration structure - /// is going to be the target of a compacting copy (IDeviceContext::CopyTLAS() with COPY_AS_MODE_COMPACT). + /// is going to be the target of a compacting copy command (IDeviceContext::CopyTLAS() with COPY_AS_MODE_COMPACT). Uint32 CompactedSize DEFAULT_INITIALIZER(0); - - /// Binding mode that i used for TLASBuildInstanceData::ContributionToHitGroupIndex calculation, - /// see Diligent::SHADER_BINDING_MODE. - SHADER_BINDING_MODE BindingMode DEFAULT_INITIALIZER(SHADER_BINDING_MODE_PER_GEOMETRY); /// Defines which command queues this BLAS can be used with. Uint64 CommandQueueMask DEFAULT_INITIALIZER(1); @@ -91,6 +73,10 @@ struct TLASInstanceDesc { /// Index that corresponds to the one specified in TLASBuildInstanceData::ContributionToHitGroupIndex. Uint32 ContributionToHitGroupIndex DEFAULT_INITIALIZER(0); + + /// The autogenerated index of the instance. + /// Same as InstanceIndex() in HLSL and gl_InstanceID in GLSL. + Uint32 InstanceIndex DEFAULT_INITIALIZER(0); /// Bottom-level AS that is specified in TLASBuildInstanceData::pBLAS. IBottomLevelAS* pBLAS DEFAULT_INITIALIZER(nullptr); @@ -122,21 +108,40 @@ DILIGENT_BEGIN_INTERFACE(ITopLevelAS, IDeviceObject) /// Returns instance description that can be used in shader binding table. /// \param [in] Name - Instance name that is specified in TLASBuildInstanceData::InstanceName. - /// \return structure object. + /// \return TLASInstanceDesc object, see Diligent::TLASInstanceDesc. + /// If instance does not exist then TLASInstanceDesc::ContributionToHitGroupIndex + /// and TLASInstanceDesc::InstanceIndex set to INVALID_INDEX. + /// + /// \note Access to the TLAS must be externally synchronized. VIRTUAL TLASInstanceDesc METHOD(GetInstanceDesc)(THIS_ const char* Name) CONST PURE; + + /// Returns the first and last hit group location that is calculated during build or update operation, + /// see IDeviceContext::BuildTLAS(). + + /// \param [out] FirstContributionToHitGroupIndex - Returns the BuildTLASAttribs::BaseContributionToHitGroupIndex value + /// as used in last build or copy operation. + /// \param [out] LastContributionToHitGroupIndex - Returns the maximum value that used in hit group shader location calculation. + /// + /// \note Access to the TLAS must be externally synchronized. + VIRTUAL void METHOD(GetContributionToHitGroupIndex)(THIS_ + Uint32 REF FirstContributionToHitGroupIndex, + Uint32 REF LastContributionToHitGroupIndex) CONST PURE; + + /// Returns scratch buffer info for the current acceleration structure. - /// \return structure object. + /// \return ScratchBufferSizes object, see Diligent::ScratchBufferSizes. VIRTUAL ScratchBufferSizes METHOD(GetScratchBufferSizes)(THIS) CONST PURE; /// Returns native acceleration structure handle specific to the underlying graphics API /// \return pointer to ID3D12Resource interface, for D3D12 implementation\n - /// VkAccelerationStructureKHR handle, for Vulkan implementation + /// VkAccelerationStructure handle, for Vulkan implementation VIRTUAL void* METHOD(GetNativeHandle)(THIS) PURE; + /// Sets the acceleration structure usage state. /// \note This method does not perform state transition, but @@ -147,6 +152,7 @@ DILIGENT_BEGIN_INTERFACE(ITopLevelAS, IDeviceObject) VIRTUAL void METHOD(SetState)(THIS_ RESOURCE_STATE State) PURE; + /// Returns the internal acceleration structure state VIRTUAL RESOURCE_STATE METHOD(GetState)(THIS) CONST PURE; }; @@ -158,11 +164,12 @@ DILIGENT_END_INTERFACE // clang-format off -# define ITopLevelAS_GetInstanceDesc(This, ...) CALL_IFACE_METHOD(TopLevelAS, GetInstanceDesc, This, __VA_ARGS__) -# define ITopLevelAS_GetScratchBufferSizes(This) CALL_IFACE_METHOD(TopLevelAS, GetScratchBufferSizes, This) -# define ITopLevelAS_GetNativeHandle(This) CALL_IFACE_METHOD(TopLevelAS, GetNativeHandle, This) -# define ITopLevelAS_SetState(This, ...) CALL_IFACE_METHOD(TopLevelAS, SetState, This, __VA_ARGS__) -# define ITopLevelAS_GetState(This) CALL_IFACE_METHOD(TopLevelAS, GetState, This) +# define ITopLevelAS_GetInstanceDesc(This, ...) CALL_IFACE_METHOD(TopLevelAS, GetInstanceDesc, This, __VA_ARGS__) +# define ITopLevelAS_GetContributionToHitGroupIndex(This, ...) CALL_IFACE_METHOD(TopLevelAS, GetContributionToHitGroupIndex, This, __VA_ARGS__) +# define ITopLevelAS_GetScratchBufferSizes(This) CALL_IFACE_METHOD(TopLevelAS, GetScratchBufferSizes, This) +# define ITopLevelAS_GetNativeHandle(This) CALL_IFACE_METHOD(TopLevelAS, GetNativeHandle, This) +# define ITopLevelAS_SetState(This, ...) CALL_IFACE_METHOD(TopLevelAS, SetState, This, __VA_ARGS__) +# define ITopLevelAS_GetState(This) CALL_IFACE_METHOD(TopLevelAS, GetState, This) // clang-format on diff --git a/Graphics/GraphicsEngine/src/BottomLevelASBase.cpp b/Graphics/GraphicsEngine/src/BottomLevelASBase.cpp index 1059f451..81168f61 100644 --- a/Graphics/GraphicsEngine/src/BottomLevelASBase.cpp +++ b/Graphics/GraphicsEngine/src/BottomLevelASBase.cpp @@ -103,16 +103,12 @@ void ValidateBottomLevelASDesc(const BottomLevelASDesc& Desc) noexcept(false) #undef LOG_BLAS_ERROR_AND_THROW } -void CopyBottomLevelASDesc(const BottomLevelASDesc& SrcDesc, - BottomLevelASDesc& DstDesc, - LinearAllocator& MemPool, - std::unordered_map<HashMapStringKey, Uint32, HashMapStringKey::Hasher>& NameToIndex) noexcept(false) +void CopyBLASGeometryDesc(const BottomLevelASDesc& SrcDesc, + BottomLevelASDesc& DstDesc, + LinearAllocator& MemPool, + const BLASNameToIndex* pSrcNameToIndex, + BLASNameToIndex& DstNameToIndex) noexcept(false) { - // Preserve original name - const auto* Name = DstDesc.Name; - DstDesc = SrcDesc; - DstDesc.Name = Name; - if (SrcDesc.pTriangles != nullptr) { MemPool.AddSpace<decltype(*SrcDesc.pTriangles)>(SrcDesc.TriangleCount); @@ -129,13 +125,24 @@ void CopyBottomLevelASDesc(const BottomLevelASDesc& { const auto* SrcGeoName = SrcDesc.pTriangles[i].GeometryName; pTriangles[i].GeometryName = MemPool.CopyString(SrcGeoName); - bool IsUniqueName = NameToIndex.emplace(SrcGeoName, i).second; + Uint32 ActualIndex = INVALID_INDEX; + + if (pSrcNameToIndex) + { + auto iter = pSrcNameToIndex->find(SrcGeoName); + VERIFY_EXPR(iter != pSrcNameToIndex->end()); + ActualIndex = iter->second.ActualIndex; + } + + bool IsUniqueName = DstNameToIndex.emplace(SrcGeoName, BLASGeomIndex{i, ActualIndex}).second; if (!IsUniqueName) LOG_ERROR_AND_THROW("Geometry name '", SrcGeoName, "' is not unique"); } - DstDesc.pTriangles = pTriangles; - DstDesc.pBoxes = nullptr; - DstDesc.BoxCount = 0; + + DstDesc.pTriangles = pTriangles; + DstDesc.TriangleCount = SrcDesc.TriangleCount; + DstDesc.pBoxes = nullptr; + DstDesc.BoxCount = 0; } else if (SrcDesc.pBoxes != nullptr) { @@ -153,11 +160,22 @@ void CopyBottomLevelASDesc(const BottomLevelASDesc& { const auto* SrcGeoName = SrcDesc.pBoxes[i].GeometryName; pBoxes[i].GeometryName = MemPool.CopyString(SrcGeoName); - bool IsUniqueName = NameToIndex.emplace(SrcGeoName, i).second; + Uint32 ActualIndex = INVALID_INDEX; + + if (pSrcNameToIndex) + { + auto iter = pSrcNameToIndex->find(SrcGeoName); + VERIFY_EXPR(iter != pSrcNameToIndex->end()); + ActualIndex = iter->second.ActualIndex; + } + + bool IsUniqueName = DstNameToIndex.emplace(SrcGeoName, BLASGeomIndex{i, ActualIndex}).second; if (!IsUniqueName) LOG_ERROR_AND_THROW("Geometry name '", SrcGeoName, "' is not unique"); } + DstDesc.pBoxes = pBoxes; + DstDesc.BoxCount = SrcDesc.BoxCount; DstDesc.pTriangles = nullptr; DstDesc.TriangleCount = 0; } diff --git a/Graphics/GraphicsEngine/src/DeviceContextBase.cpp b/Graphics/GraphicsEngine/src/DeviceContextBase.cpp index a526bd37..beb02e00 100644 --- a/Graphics/GraphicsEngine/src/DeviceContextBase.cpp +++ b/Graphics/GraphicsEngine/src/DeviceContextBase.cpp @@ -349,14 +349,26 @@ bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs) CHECK_BUILD_BLAS_ATTRIBS(Attribs.BoxDataCount <= BLASDesc.BoxCount, "BoxDataCount must be less than or equal to pBLAS->GetDesc().BoxCount"); CHECK_BUILD_BLAS_ATTRIBS(Attribs.TriangleDataCount <= BLASDesc.TriangleCount, "TriangleDataCount must be less than or equal to pBLAS->GetDesc().TriangleCount"); + if (Attribs.Update) + { + CHECK_BUILD_BLAS_ATTRIBS((BLASDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_UPDATE) == RAYTRACING_BUILD_AS_ALLOW_UPDATE, + "Update is true, but BLAS created without RAYTRACING_BUILD_AS_ALLOW_UPDATE flag"); + + const Uint32 GeomCount = Attribs.pBLAS->GetActualGeometryCount(); + CHECK_BUILD_BLAS_ATTRIBS(Attribs.BoxDataCount == 0 || Attribs.BoxDataCount == GeomCount, + "Update is true, but BoxDataCount does not match with a previous value (", GeomCount, ")"); + CHECK_BUILD_BLAS_ATTRIBS(Attribs.TriangleDataCount == 0 || Attribs.TriangleDataCount == GeomCount, + "Update is true, but TriangleDataCount does not match with a previous value (", GeomCount, ")"); + } + for (Uint32 i = 0; i < Attribs.TriangleDataCount; ++i) { const auto& tri = Attribs.pTriangleData[i]; const Uint32 VertexSize = GetValueSize(tri.VertexValueType) * tri.VertexComponentCount; const Uint32 VertexDataSize = tri.VertexStride * tri.VertexCount; - const Uint32 GeomIndex = Attribs.pBLAS->GetGeometryIndex(tri.GeometryName); + const Uint32 GeomIndex = Attribs.pBLAS->GetGeometryDescIndex(tri.GeometryName); - CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != InvalidGeometryIndex, + CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != INVALID_INDEX, "pTriangleData[", i, "].GeometryName (", tri.GeometryName, ") is not found in BLAS description"); const auto& TriDesc = BLASDesc.pTriangles[GeomIndex]; @@ -376,10 +388,11 @@ bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs) CHECK_BUILD_BLAS_ATTRIBS(tri.pVertexBuffer != nullptr, "pTriangleData[", i, "].pVertexBuffer must not be null"); - CHECK_BUILD_BLAS_ATTRIBS((tri.pVertexBuffer->GetDesc().BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, + const BufferDesc& VertBufDesc = tri.pVertexBuffer->GetDesc(); + CHECK_BUILD_BLAS_ATTRIBS((VertBufDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, "pTriangleData[", i, "].pVertexBuffer was not created with BIND_RAY_TRACING flag"); - CHECK_BUILD_BLAS_ATTRIBS(tri.VertexOffset + VertexDataSize <= tri.pVertexBuffer->GetDesc().uiSizeInBytes, + CHECK_BUILD_BLAS_ATTRIBS(tri.VertexOffset + VertexDataSize <= VertBufDesc.uiSizeInBytes, "pTriangleData[", i, "].pVertexBuffer is too small for the specified VertexStride (", tri.VertexStride, ") and VertexCount (", tri.VertexCount, "): at least ", tri.VertexOffset + VertexDataSize, " bytes are required"); @@ -395,11 +408,13 @@ bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs) { CHECK_BUILD_BLAS_ATTRIBS(tri.pIndexBuffer != nullptr, "pTriangleData[", i, "].pIndexBuffer must not be null"); - CHECK_BUILD_BLAS_ATTRIBS((tri.pIndexBuffer->GetDesc().BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, + const BufferDesc& InstBufDesc = tri.pIndexBuffer->GetDesc(); + const Uint32 IndexDataSize = tri.PrimitiveCount * 3 * GetValueSize(tri.IndexType); + + CHECK_BUILD_BLAS_ATTRIBS((InstBufDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, "pTriangleData[", i, "].pIndexBuffer was not created with BIND_RAY_TRACING flag"); - const Uint32 IndexDataSize = tri.PrimitiveCount * 3 * GetValueSize(tri.IndexType); - CHECK_BUILD_BLAS_ATTRIBS(tri.IndexOffset + IndexDataSize <= tri.pIndexBuffer->GetDesc().uiSizeInBytes, + CHECK_BUILD_BLAS_ATTRIBS(tri.IndexOffset + IndexDataSize <= InstBufDesc.uiSizeInBytes, "pTriangleData[", i, "].pIndexBuffer is too small for specified IndexType and IndexCount: at least", tri.IndexOffset + IndexDataSize, " bytes are required"); } @@ -425,9 +440,9 @@ bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs) { const auto& box = Attribs.pBoxData[i]; const Uint32 BoxSize = sizeof(float) * 6; - const Uint32 GeomIndex = Attribs.pBLAS->GetGeometryIndex(box.GeometryName); + const Uint32 GeomIndex = Attribs.pBLAS->GetGeometryDescIndex(box.GeometryName); - CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != InvalidGeometryIndex, + CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != INVALID_INDEX, "pBoxData[", i, "].GeometryName (", box.GeometryName, ") is not found in BLAS description"); const auto& BoxDesc = BLASDesc.pBoxes[GeomIndex]; @@ -449,8 +464,12 @@ bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs) CHECK_BUILD_BLAS_ATTRIBS(Attribs.ScratchBufferOffset <= ScratchDesc.uiSizeInBytes, "ScratchBufferOffset (", Attribs.ScratchBufferOffset, ") is greater than the buffer size (", ScratchDesc.uiSizeInBytes, ")"); - CHECK_BUILD_BLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pBLAS->GetScratchBufferSizes().Build, - "pScratchBuffer size is too small, use pBLAS->GetScratchBufferSizes().Build to get the required size for the scratch buffer"); + if (Attribs.Update) + CHECK_BUILD_BLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pBLAS->GetScratchBufferSizes().Update, + "pScratchBuffer size is too small, use pBLAS->GetScratchBufferSizes().Update to get the required size for the scratch buffer"); + else + CHECK_BUILD_BLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pBLAS->GetScratchBufferSizes().Build, + "pScratchBuffer size is too small, use pBLAS->GetScratchBufferSizes().Build to get the required size for the scratch buffer"); CHECK_BUILD_BLAS_ATTRIBS((ScratchDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, "pScratchBuffer was not created with BIND_RAY_TRACING flag"); @@ -461,7 +480,7 @@ bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs) } -bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs) +bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs, Uint32 PrevInstanceCount) { #define CHECK_BUILD_TLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Build TLAS attribs are invalid: ", __VA_ARGS__) @@ -469,7 +488,9 @@ bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs) CHECK_BUILD_TLAS_ATTRIBS(Attribs.pScratchBuffer != nullptr, "pScratchBuffer must not be null"); CHECK_BUILD_TLAS_ATTRIBS(Attribs.pInstances != nullptr, "pInstances must not be null"); CHECK_BUILD_TLAS_ATTRIBS(Attribs.pInstanceBuffer != nullptr, "pInstanceBuffer must not be null"); - CHECK_BUILD_TLAS_ATTRIBS(Attribs.HitShadersPerInstance != 0, "HitShadersPerInstance must be greater than 0"); + + CHECK_BUILD_TLAS_ATTRIBS(Attribs.BindingMode == SHADER_BINDING_USER_DEFINED || Attribs.HitShadersPerInstance != 0, + "HitShadersPerInstance must be greater than 0 if BindingMode is not SHADER_BINDING_USER_DEFINED"); const auto& TLASDesc = Attribs.pTLAS->GetDesc(); @@ -477,30 +498,49 @@ bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs) "InstanceCount (", Attribs.InstanceCount, ") must be less than or equal to pTLAS->GetDesc().MaxInstanceCount (", TLASDesc.MaxInstanceCount, ")"); - const auto& InstDesc = Attribs.pInstanceBuffer->GetDesc(); - const auto InstDataSize = size_t{Attribs.InstanceCount} * size_t{TLAS_INSTANCE_DATA_SIZE}; + if (Attribs.Update) + { + CHECK_BUILD_TLAS_ATTRIBS((TLASDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_UPDATE) == RAYTRACING_BUILD_AS_ALLOW_UPDATE, + "Update is true, but TLAS created without RAYTRACING_BUILD_AS_ALLOW_UPDATE flag"); + CHECK_BUILD_TLAS_ATTRIBS(PrevInstanceCount == Attribs.InstanceCount, + "Update is true, but InstanceCount (", Attribs.InstanceCount, ") does not match with the previous value (", PrevInstanceCount, ")"); + } - Uint32 AutoOffsetCounter = 0; + const auto& InstDesc = Attribs.pInstanceBuffer->GetDesc(); + const auto InstDataSize = size_t{Attribs.InstanceCount} * size_t{TLAS_INSTANCE_DATA_SIZE}; + Uint32 AutoOffsetCounter = 0; // Calculate instance data size for (Uint32 i = 0; i < Attribs.InstanceCount; ++i) { - VERIFY((Attribs.pInstances[i].CustomId & ~0x00FFFFFF) == 0, "Only the lower 24 bits are used"); + constexpr Uint32 BitMask = (1u << 24) - 1; + const auto& Inst = Attribs.pInstances[i]; + + VERIFY((Inst.CustomId & ~BitMask) == 0, "Only the lower 24 bits are used"); - VERIFY(Attribs.pInstances[i].ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO || - (Attribs.pInstances[i].ContributionToHitGroupIndex & ~0x00FFFFFF) == 0, + VERIFY(Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO || + (Inst.ContributionToHitGroupIndex & ~BitMask) == 0, "Only the lower 24 bits are used"); - CHECK_BUILD_TLAS_ATTRIBS(Attribs.pInstances[i].InstanceName != nullptr, "pInstances[", i, "].InstanceName must not be null"); - CHECK_BUILD_TLAS_ATTRIBS(Attribs.pInstances[i].pBLAS != nullptr, "pInstances[", i, "].pBLAS must not be null"); + CHECK_BUILD_TLAS_ATTRIBS(Inst.InstanceName != nullptr, "pInstances[", i, "].InstanceName must not be null"); - if (Attribs.pInstances[i].ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO) + if (Attribs.Update) + { + const TLASInstanceDesc IDesc = Attribs.pTLAS->GetInstanceDesc(Inst.InstanceName); + CHECK_BUILD_TLAS_ATTRIBS(IDesc.InstanceIndex != INVALID_INDEX, "Update is true, but pInstances[", i, "].InstanceName does not exists"); + } + else + { + CHECK_BUILD_TLAS_ATTRIBS(Inst.pBLAS != nullptr, "pInstances[", i, "].pBLAS must not be null"); + } + + if (Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO) ++AutoOffsetCounter; - CHECK_BUILD_TLAS_ATTRIBS(TLASDesc.BindingMode == SHADER_BINDING_USER_DEFINED || Attribs.pInstances[i].ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO, + CHECK_BUILD_TLAS_ATTRIBS(Attribs.BindingMode == SHADER_BINDING_USER_DEFINED || Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO, "pInstances[", i, "].ContributionToHitGroupIndex must be TLAS_INSTANCE_OFFSET_AUTO " - "if TLAS is created with BindingMode that is not SHADER_BINDING_USER_DEFINED"); + "if BindingMode is not SHADER_BINDING_USER_DEFINED"); } CHECK_BUILD_TLAS_ATTRIBS(AutoOffsetCounter == 0 || AutoOffsetCounter == Attribs.InstanceCount, @@ -521,8 +561,12 @@ bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs) CHECK_BUILD_TLAS_ATTRIBS(Attribs.ScratchBufferOffset <= ScratchDesc.uiSizeInBytes, "ScratchBufferOffset (", Attribs.ScratchBufferOffset, ") is greater than the buffer size (", ScratchDesc.uiSizeInBytes, ")"); - CHECK_BUILD_TLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pTLAS->GetScratchBufferSizes().Build, - "pScratchBuffer size is too small, use pTLAS->GetScratchBufferSizes().Build to get the required size for scratch buffer"); + if (Attribs.Update) + CHECK_BUILD_TLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pTLAS->GetScratchBufferSizes().Update, + "pScratchBuffer size is too small, use pTLAS->GetScratchBufferSizes().Update to get the required size for scratch buffer"); + else + CHECK_BUILD_TLAS_ATTRIBS(ScratchDesc.uiSizeInBytes - Attribs.ScratchBufferOffset >= Attribs.pTLAS->GetScratchBufferSizes().Build, + "pScratchBuffer size is too small, use pTLAS->GetScratchBufferSizes().Build to get the required size for scratch buffer"); CHECK_BUILD_TLAS_ATTRIBS((ScratchDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, "pScratchBuffer was not created with BIND_RAY_TRACING flag"); @@ -532,7 +576,7 @@ bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs) } -bool VerifyCopyBLASAttribs(const CopyBLASAttribs& Attribs) +bool VerifyCopyBLASAttribs(const IRenderDevice* pDevice, const CopyBLASAttribs& Attribs) { #define CHECK_COPY_BLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Copy BLAS attribs are invalid: ", __VA_ARGS__) @@ -541,48 +585,63 @@ bool VerifyCopyBLASAttribs(const CopyBLASAttribs& Attribs) if (Attribs.Mode == COPY_AS_MODE_CLONE) { - auto& SrcDesc = Attribs.pSrc->GetDesc(); - auto& DstDesc = Attribs.pDst->GetDesc(); - - CHECK_COPY_BLAS_ATTRIBS(SrcDesc.TriangleCount == DstDesc.TriangleCount, - "Src BLAS triangle count (", SrcDesc.TriangleCount, ") must be equal to the dst BLAS triangle count (", DstDesc.TriangleCount, ")"); - - CHECK_COPY_BLAS_ATTRIBS(SrcDesc.BoxCount == DstDesc.BoxCount, - "Src BLAS box count (", SrcDesc.BoxCount, ") must be equal to the dst BLAS box count (", DstDesc.BoxCount, ")"); - - CHECK_COPY_BLAS_ATTRIBS(SrcDesc.Flags == DstDesc.Flags, - "Source and destination BLASes must have been created with the same flags"); - - for (Uint32 i = 0; i < SrcDesc.TriangleCount; ++i) - { - auto& SrcTri = SrcDesc.pTriangles[i]; - auto& DstTri = DstDesc.pTriangles[i]; - - CHECK_COPY_BLAS_ATTRIBS(SrcTri.MaxVertexCount == DstTri.MaxVertexCount, - "MaxVertexCount value (", SrcTri.MaxVertexCount, ") in source triangle description at index ", i, - " does not match MaxVertexCount value (", DstTri.MaxVertexCount, ") in the destination description"); - CHECK_COPY_BLAS_ATTRIBS(SrcTri.VertexValueType == DstTri.VertexValueType, - "VertexValueType value (", GetValueTypeString(SrcTri.VertexValueType), ") in source triangle description at index ", i, - " does not match VertexValueType value (", GetValueTypeString(DstTri.VertexValueType), ") in destination description"); - CHECK_COPY_BLAS_ATTRIBS(SrcTri.VertexComponentCount == DstTri.VertexComponentCount, - "VertexComponentCount value (", Uint32{SrcTri.VertexComponentCount}, ") in source triangle description at index ", i, - " does not match VertexComponentCount value (", Uint32{DstTri.VertexComponentCount}, ") in destination description"); - CHECK_COPY_BLAS_ATTRIBS(SrcTri.MaxPrimitiveCount == DstTri.MaxPrimitiveCount, - "MaxPrimitiveCount value (", SrcTri.MaxPrimitiveCount, ") in source triangle description at index ", i, - " does not match MaxPrimitiveCount value (", DstTri.MaxPrimitiveCount, ") in destination description"); - CHECK_COPY_BLAS_ATTRIBS(SrcTri.IndexType == DstTri.IndexType, - "IndexType value (", GetValueTypeString(SrcTri.IndexType), ") in source triangle description at index ", i, - " does not match IndexType value (", GetValueTypeString(DstTri.IndexType), ") in destination description"); - CHECK_COPY_BLAS_ATTRIBS(SrcTri.AllowsTransforms == DstTri.AllowsTransforms, - "AllowsTransforms value (", (SrcTri.AllowsTransforms ? "true" : "false"), ") in source triangle description at index ", i, - " does not match AllowsTransforms value (", (DstTri.AllowsTransforms ? "true" : "false"), ") in destination description"); - } - - for (Uint32 i = 0; i < SrcDesc.BoxCount; ++i) + if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_VULKAN) { - CHECK_COPY_BLAS_ATTRIBS(SrcDesc.pBoxes[i].MaxBoxCount == DstDesc.pBoxes[i].MaxBoxCount, - "MaxBoxCountt value (", SrcDesc.pBoxes[i].MaxBoxCount, ") in source box description at index ", i, - " does not match MaxBoxCount value (", DstDesc.pBoxes[i].MaxBoxCount, ") in destination description"); + auto& SrcDesc = Attribs.pSrc->GetDesc(); + auto& DstDesc = Attribs.pDst->GetDesc(); + + CHECK_COPY_BLAS_ATTRIBS(SrcDesc.TriangleCount == DstDesc.TriangleCount, + "Src BLAS triangle count (", SrcDesc.TriangleCount, ") must be equal to the dst BLAS triangle count (", DstDesc.TriangleCount, ")"); + + CHECK_COPY_BLAS_ATTRIBS(SrcDesc.BoxCount == DstDesc.BoxCount, + "Src BLAS box count (", SrcDesc.BoxCount, ") must be equal to the dst BLAS box count (", DstDesc.BoxCount, ")"); + + CHECK_COPY_BLAS_ATTRIBS(SrcDesc.Flags == DstDesc.Flags, + "Source and destination BLASes must have been created with the same flags"); + + for (Uint32 i = 0; i < SrcDesc.TriangleCount; ++i) + { + const BLASTriangleDesc& SrcTri = SrcDesc.pTriangles[i]; + const Uint32 Index = Attribs.pDst->GetGeometryDescIndex(SrcTri.GeometryName); + CHECK_COPY_BLAS_ATTRIBS(Index != INVALID_INDEX, + "Src GeometryName ('", SrcTri.GeometryName, "') at index ", i, " is not found in pDst"); + const BLASTriangleDesc& DstTri = DstDesc.pTriangles[Index]; + + CHECK_COPY_BLAS_ATTRIBS(SrcTri.MaxVertexCount == DstTri.MaxVertexCount, + "MaxVertexCount value (", SrcTri.MaxVertexCount, ") in source triangle description at index ", i, + " does not match MaxVertexCount value (", DstTri.MaxVertexCount, ") in the destination description"); + CHECK_COPY_BLAS_ATTRIBS(SrcTri.VertexValueType == DstTri.VertexValueType, + "VertexValueType value (", GetValueTypeString(SrcTri.VertexValueType), ") in source triangle description at index ", i, + " does not match VertexValueType value (", GetValueTypeString(DstTri.VertexValueType), ") in destination description"); + CHECK_COPY_BLAS_ATTRIBS(SrcTri.VertexComponentCount == DstTri.VertexComponentCount, + "VertexComponentCount value (", Uint32{SrcTri.VertexComponentCount}, ") in source triangle description at index ", i, + " does not match VertexComponentCount value (", Uint32{DstTri.VertexComponentCount}, ") in destination description"); + CHECK_COPY_BLAS_ATTRIBS(SrcTri.MaxPrimitiveCount == DstTri.MaxPrimitiveCount, + "MaxPrimitiveCount value (", SrcTri.MaxPrimitiveCount, ") in source triangle description at index ", i, + " does not match MaxPrimitiveCount value (", DstTri.MaxPrimitiveCount, ") in destination description"); + CHECK_COPY_BLAS_ATTRIBS(SrcTri.IndexType == DstTri.IndexType, + "IndexType value (", GetValueTypeString(SrcTri.IndexType), ") in source triangle description at index ", i, + " does not match IndexType value (", GetValueTypeString(DstTri.IndexType), ") in destination description"); + CHECK_COPY_BLAS_ATTRIBS(SrcTri.AllowsTransforms == DstTri.AllowsTransforms, + "AllowsTransforms value (", (SrcTri.AllowsTransforms ? "true" : "false"), ") in source triangle description at index ", i, + " does not match AllowsTransforms value (", (DstTri.AllowsTransforms ? "true" : "false"), ") in destination description"); + } + + for (Uint32 i = 0; i < SrcDesc.BoxCount; ++i) + { + const BLASBoundingBoxDesc& SrcBox = SrcDesc.pBoxes[i]; + const Uint32 Index = Attribs.pDst->GetGeometryDescIndex(SrcBox.GeometryName); + if (Index == INVALID_INDEX) + { + LOG_ERROR_MESSAGE("Copy BLAS attribs are invalid: pSrc->GetDesc().pBoxes[", i, "].GeometryName ('", SrcBox.GeometryName, "') is not found in pDst"); + return false; + } + const BLASBoundingBoxDesc& DstBox = DstDesc.pBoxes[Index]; + + CHECK_COPY_BLAS_ATTRIBS(SrcBox.MaxBoxCount == DstBox.MaxBoxCount, + "MaxBoxCountt value (", SrcBox.MaxBoxCount, ") in source box description at index ", i, + " does not match MaxBoxCount value (", DstBox.MaxBoxCount, ") in destination description"); + } } } else if (Attribs.Mode == COPY_AS_MODE_COMPACT) @@ -617,7 +676,7 @@ bool VerifyCopyTLASAttribs(const CopyTLASAttribs& Attribs) auto& SrcDesc = Attribs.pSrc->GetDesc(); auto& DstDesc = Attribs.pDst->GetDesc(); - CHECK_COPY_TLAS_ATTRIBS(SrcDesc.MaxInstanceCount == DstDesc.MaxInstanceCount || SrcDesc.Flags == DstDesc.Flags, + CHECK_COPY_TLAS_ATTRIBS(SrcDesc.MaxInstanceCount == DstDesc.MaxInstanceCount && SrcDesc.Flags == DstDesc.Flags, "pDst must have been created with the same parameters as pSrc"); } else if (Attribs.Mode == COPY_AS_MODE_COMPACT) @@ -647,12 +706,13 @@ bool VerifyWriteBLASCompactedSizeAttribs(const IRenderDevice* pDevice, const Wri "pBLAS was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag"); CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.pDestBuffer != nullptr, "pDestBuffer must not be null"); - CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.DestBufferOffset + sizeof(Uint64) <= Attribs.pDestBuffer->GetDesc().uiSizeInBytes, - "pDestBuffer is too small"); + + const BufferDesc& DstDesc = Attribs.pDestBuffer->GetDesc(); + CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.DestBufferOffset + sizeof(Uint64) <= DstDesc.uiSizeInBytes, "pDestBuffer is too small"); if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_D3D12) { - CHECK_WRITE_BLAS_SIZE_ATTRIBS((Attribs.pDestBuffer->GetDesc().BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS, + CHECK_WRITE_BLAS_SIZE_ATTRIBS((DstDesc.BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS, "pDestBuffer must have been created with BIND_UNORDERED_ACCESS flag in Direct3D12"); } @@ -670,11 +730,13 @@ bool VerifyWriteTLASCompactedSizeAttribs(const IRenderDevice* pDevice, const Wri "pTLAS was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag"); CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.pDestBuffer != nullptr, "pDestBuffer must not be null"); - CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.DestBufferOffset + sizeof(Uint64) <= Attribs.pDestBuffer->GetDesc().uiSizeInBytes, "pDestBuffer is too small"); + + const BufferDesc& DstDesc = Attribs.pDestBuffer->GetDesc(); + CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.DestBufferOffset + sizeof(Uint64) <= DstDesc.uiSizeInBytes, "pDestBuffer is too small"); if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_D3D12) { - CHECK_WRITE_TLAS_SIZE_ATTRIBS((Attribs.pDestBuffer->GetDesc().BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS, + CHECK_WRITE_TLAS_SIZE_ATTRIBS((DstDesc.BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS, "pDestBuffer must have been created with BIND_UNORDERED_ACCESS flag"); } @@ -689,7 +751,8 @@ bool VerifyTraceRaysAttribs(const TraceRaysAttribs& Attribs) CHECK_TRACE_RAYS_ATTRIBS(Attribs.pSBT != nullptr, "pSBT must not be null"); #ifdef DILIGENT_DEVELOPMENT - CHECK_TRACE_RAYS_ATTRIBS(Attribs.pSBT->Verify(), "pSBT content is not valid"); + CHECK_TRACE_RAYS_ATTRIBS(Attribs.pSBT->Verify(SHADER_BINDING_VALIDATION_SHADER_ONLY | SHADER_BINDING_VALIDATION_TLAS), + "pSBT not all shaders are binded or instance to shader mapping are incorrect"); #endif // DILIGENT_DEVELOPMENT CHECK_TRACE_RAYS_ATTRIBS(Attribs.DimensionX != 0, "DimensionX must not be zero."); |
