diff options
| author | assiduous <assiduous@diligentgraphics.com> | 2020-12-15 19:32:55 +0000 |
|---|---|---|
| committer | assiduous <assiduous@diligentgraphics.com> | 2020-12-15 19:32:55 +0000 |
| commit | 2cbe12c8541f63719ae3662cda827ed53004c0b2 (patch) | |
| tree | 6ff68d7ee7d386d1c0e30c00fcd551fcbf819c6c /Graphics/GraphicsEngine | |
| parent | Math Lib: added Matrix4x4 constructor from float4 rows (diff) | |
| parent | BasicMath: minor code formatting (diff) | |
| download | DiligentCore-2cbe12c8541f63719ae3662cda827ed53004c0b2.tar.gz DiligentCore-2cbe12c8541f63719ae3662cda827ed53004c0b2.zip | |
Merge branch 'azhirnov-ray_tracing_2'
Diffstat (limited to 'Graphics/GraphicsEngine')
39 files changed, 5524 insertions, 1097 deletions
diff --git a/Graphics/GraphicsEngine/CMakeLists.txt b/Graphics/GraphicsEngine/CMakeLists.txt index d95257fe..604f3442 100644 --- a/Graphics/GraphicsEngine/CMakeLists.txt +++ b/Graphics/GraphicsEngine/CMakeLists.txt @@ -14,7 +14,6 @@ set(INCLUDE include/EngineMemory.h include/FenceBase.hpp include/FramebufferBase.hpp - include/pch.h include/PipelineStateBase.hpp include/QueryBase.hpp include/RenderDeviceBase.hpp @@ -28,6 +27,9 @@ set(INCLUDE include/SwapChainBase.hpp include/TextureBase.hpp include/TextureViewBase.hpp + include/BottomLevelASBase.hpp + include/TopLevelASBase.hpp + include/ShaderBindingTableBase.hpp ) set(INTERFACE @@ -58,18 +60,25 @@ set(INTERFACE interface/SwapChain.h interface/Texture.h interface/TextureView.h + interface/BottomLevelAS.h + interface/TopLevelAS.h + interface/ShaderBindingTable.h ) set(SOURCE src/APIInfo.cpp + src/BottomLevelASBase.cpp src/BufferBase.cpp src/DefaultShaderSourceStreamFactory.cpp + src/DeviceContextBase.cpp src/EngineMemory.cpp src/FramebufferBase.cpp src/PipelineStateBase.cpp src/ResourceMappingBase.cpp + src/ShaderBindingTableBase.cpp src/RenderPassBase.cpp src/TextureBase.cpp + src/TopLevelASBase.cpp ) add_library(Diligent-GraphicsEngine STATIC ${SOURCE} ${INTERFACE} ${INCLUDE}) diff --git a/Graphics/GraphicsEngine/include/BottomLevelASBase.hpp b/Graphics/GraphicsEngine/include/BottomLevelASBase.hpp new file mode 100644 index 00000000..4e061c4c --- /dev/null +++ b/Graphics/GraphicsEngine/include/BottomLevelASBase.hpp @@ -0,0 +1,256 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::BottomLevelASBase template class + +#include <unordered_map> +#include <atomic> + +#include "BottomLevelAS.h" +#include "DeviceObjectBase.hpp" +#include "RenderDeviceBase.hpp" +#include "FixedLinearAllocator.hpp" +#include "HashUtils.hpp" + +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 geometry description using MemPool to allocate required dynamic space. +void CopyBLASGeometryDesc(const BottomLevelASDesc& SrcDesc, + BottomLevelASDesc& DstDesc, + FixedLinearAllocator& MemPool, + const BLASNameToIndex* pSrcNameToIndex, + BLASNameToIndex& DstNameToIndex) noexcept(false); + + +/// Template class implementing base functionality for a bottom-level acceleration structure object. + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IBottomLevelASD3D12 or Diligent::IBottomLevelASVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D12Impl or Diligent::RenderDeviceVkImpl) +template <class BaseInterface, class RenderDeviceImplType> +class BottomLevelASBase : public DeviceObjectBase<BaseInterface, RenderDeviceImplType, BottomLevelASDesc> +{ +public: + using TDeviceObjectBase = DeviceObjectBase<BaseInterface, RenderDeviceImplType, BottomLevelASDesc>; + + /// \param pRefCounters - reference counters object that controls the lifetime of this BLAS. + /// \param pDevice - pointer to the device. + /// \param Desc - BLAS description. + /// \param bIsDeviceInternal - flag indicating if the BLAS is an internal device object and + /// must not keep a strong reference to the device. + BottomLevelASBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const BottomLevelASDesc& Desc, + bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} + { + ValidateBottomLevelASDesc(this->m_Desc); + + if (Desc.CompactedSize > 0) + { + } + else + { + CopyGeometryDescriptionUnsafe(Desc, nullptr); + } + } + + ~BottomLevelASBase() + { + ClearGeometry(); + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_BottomLevelAS, TDeviceObjectBase) + + // Maps geometry that was used in build operation to geometry description. + // Returns the 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()) + { + 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 with name '", Name, "', exists but was not enabled during last build"); + return iter->second.ActualIndex; + } + LOG_ERROR_MESSAGE("Can't find geometry with name '", Name, '\''); + return INVALID_INDEX; + } + + virtual void DILIGENT_CALL_TYPE SetState(RESOURCE_STATE State) override final + { + 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; + } + + virtual RESOURCE_STATE DILIGENT_CALL_TYPE GetState() const override final + { + return this->m_State; + } + + /// Implementation of IBottomLevelAS::GetScratchBufferSizes() + virtual ScratchBufferSizes DILIGENT_CALL_TYPE GetScratchBufferSizes() const override final + { + return this->m_ScratchSize; + } + + bool IsInKnownState() const + { + return this->m_State != RESOURCE_STATE_UNKNOWN; + } + + bool CheckState(RESOURCE_STATE State) const + { + VERIFY((State & (State - 1)) == 0, "Single state is expected"); + VERIFY(IsInKnownState(), "BLAS state is unknown"); + return (this->m_State & State) == State; + } + +#ifdef DILIGENT_DEVELOPMENT + void UpdateVersion() + { + this->m_DbgVersion.fetch_add(1); + } + + Uint32 GetVersion() const + { + return this->m_DbgVersion.load(); + } +#endif // DILIGENT_DEVELOPMENT + + void CopyGeometryDescription(const BottomLevelASBase& SrcBLAS) noexcept + { + ClearGeometry(); + + try + { + CopyGeometryDescriptionUnsafe(SrcBLAS.GetDesc(), &SrcBLAS.m_NameToIndex); + } + catch (...) + { + ClearGeometry(); + } + } + + void SetActualGeometryCount(Uint32 Count) + { + m_GeometryCount = Count; + } + + virtual Uint32 DILIGENT_CALL_TYPE GetActualGeometryCount() const override final + { + return m_GeometryCount; + } + +private: + void CopyGeometryDescriptionUnsafe(const BottomLevelASDesc& SrcDesc, const BLASNameToIndex* pSrcNameToIndex) noexcept(false) + { + FixedLinearAllocator MemPool{GetRawAllocator()}; + CopyBLASGeometryDesc(SrcDesc, this->m_Desc, MemPool, pSrcNameToIndex, this->m_NameToIndex); + this->m_pRawPtr = MemPool.Release(); + } + + void ClearGeometry() noexcept + { + if (this->m_pRawPtr != nullptr) + { + GetRawAllocator().Free(this->m_pRawPtr); + this->m_pRawPtr = nullptr; + } + + // 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; + BLASNameToIndex m_NameToIndex; + void* m_pRawPtr = nullptr; + Uint32 m_GeometryCount = 0; + ScratchBufferSizes m_ScratchSize; + +#ifdef DILIGENT_DEVELOPMENT + std::atomic<Uint32> m_DbgVersion{0}; +#endif +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/BufferBase.hpp b/Graphics/GraphicsEngine/include/BufferBase.hpp index 605bc2ed..f09e8fc5 100644 --- a/Graphics/GraphicsEngine/include/BufferBase.hpp +++ b/Graphics/GraphicsEngine/include/BufferBase.hpp @@ -42,8 +42,15 @@ namespace Diligent { -void ValidateBufferInitData(const BufferDesc& Desc, const BufferData* pBuffData); -void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps); +/// Validates buffer description and throws an exception in case of an error. +void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps) noexcept(false); + +/// Validates initial buffer data parameters and throws an exception in case of an error. +void ValidateBufferInitData(const BufferDesc& Desc, const BufferData* pBuffData) noexcept(false); + +/// Validates and corrects buffer view description; throws an exception in case of an error. +void ValidateAndCorrectBufferViewDesc(const BufferDesc& BuffDesc, BufferViewDesc& ViewDesc) noexcept(false); + /// Template class implementing base functionality for a buffer object @@ -93,10 +100,31 @@ public: /// Implementation of IBuffer::CreateView(); calls CreateViewInternal() virtual function /// that creates buffer view for the specific engine implementation. - virtual void DILIGENT_CALL_TYPE CreateView(const struct BufferViewDesc& ViewDesc, IBufferView** ppView) override; + virtual void DILIGENT_CALL_TYPE CreateView(const struct BufferViewDesc& ViewDesc, IBufferView** ppView) override + { + DEV_CHECK_ERR(ViewDesc.ViewType != BUFFER_VIEW_UNDEFINED, "Buffer view type is not specified"); + if (ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_SHADER_RESOURCE, "Attempting to create SRV for buffer '", this->m_Desc.Name, "' that was not created with BIND_SHADER_RESOURCE flag"); + else if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) + DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS, "Attempting to create UAV for buffer '", this->m_Desc.Name, "' that was not created with BIND_UNORDERED_ACCESS flag"); + else + UNEXPECTED("Unexpected buffer view type"); + + CreateViewInternal(ViewDesc, ppView, false); + } + /// Implementation of IBuffer::GetDefaultView(). - virtual IBufferView* DILIGENT_CALL_TYPE GetDefaultView(BUFFER_VIEW_TYPE ViewType) override; + virtual IBufferView* DILIGENT_CALL_TYPE GetDefaultView(BUFFER_VIEW_TYPE ViewType) override + { + switch (ViewType) + { + case BUFFER_VIEW_SHADER_RESOURCE: return m_pDefaultSRV.get(); + case BUFFER_VIEW_UNORDERED_ACCESS: return m_pDefaultUAV.get(); + default: UNEXPECTED("Unknown view type"); return nullptr; + } + } + /// Creates default buffer views. @@ -105,7 +133,53 @@ public: /// - Creates default unordered access view addressing the entire buffer if Diligent::BIND_UNORDERED_ACCESS flag is set /// /// The function calls CreateViewInternal(). - void CreateDefaultViews(); + void CreateDefaultViews() + { + // Create default views for structured and raw buffers. For formatted buffers we do not know the view format, so + // cannot create views. + + auto CreateDefaultView = [&](BUFFER_VIEW_TYPE ViewType) // + { + BufferViewDesc ViewDesc; + ViewDesc.ViewType = ViewType; + + std::string ViewName; + switch (ViewType) + { + case BUFFER_VIEW_UNORDERED_ACCESS: + ViewName = "Default UAV of buffer '"; + break; + + case BUFFER_VIEW_SHADER_RESOURCE: + ViewName = "Default SRV of buffer '"; + break; + + default: + UNEXPECTED("Unexpected buffer view type"); + } + + ViewName += this->m_Desc.Name; + ViewName += '\''; + ViewDesc.Name = ViewName.c_str(); + + IBufferView* pView = nullptr; + CreateViewInternal(ViewDesc, &pView, true); + VERIFY(pView != nullptr, "Failed to create default view for buffer '", this->m_Desc.Name, "'"); + VERIFY(pView->GetDesc().ViewType == ViewType, "Unexpected view type"); + + return static_cast<BufferViewImplType*>(pView); + }; + + if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) + { + m_pDefaultUAV.reset(CreateDefaultView(BUFFER_VIEW_UNORDERED_ACCESS)); + } + + if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) + { + m_pDefaultSRV.reset(CreateDefaultView(BUFFER_VIEW_SHADER_RESOURCE)); + } + } virtual void DILIGENT_CALL_TYPE SetState(RESOURCE_STATE State) override final { @@ -133,9 +207,6 @@ protected: /// Pure virtual function that creates buffer view for the specific engine implementation. virtual void CreateViewInternal(const struct BufferViewDesc& ViewDesc, IBufferView** ppView, bool bIsDefaultView) = 0; - /// Corrects buffer view description and validates view parameters. - void CorrectBufferViewDesc(struct BufferViewDesc& ViewDesc); - #ifdef DILIGENT_DEBUG TBuffViewObjAllocator& m_dbgBuffViewAllocator; #endif @@ -149,112 +220,4 @@ protected: std::unique_ptr<BufferViewImplType, STDDeleter<BufferViewImplType, TBuffViewObjAllocator>> m_pDefaultSRV; }; - - - -template <class BaseInterface, class RenderDeviceImplType, class BufferViewImplType, class TBuffViewObjAllocator> -void BufferBase<BaseInterface, RenderDeviceImplType, BufferViewImplType, TBuffViewObjAllocator>::CreateView(const struct BufferViewDesc& ViewDesc, IBufferView** ppView) -{ - DEV_CHECK_ERR(ViewDesc.ViewType != BUFFER_VIEW_UNDEFINED, "Buffer view type is not specified"); - if (ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_SHADER_RESOURCE, "Attempting to create SRV for buffer '", this->m_Desc.Name, "' that was not created with BIND_SHADER_RESOURCE flag"); - else if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) - DEV_CHECK_ERR(this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS, "Attempting to create UAV for buffer '", this->m_Desc.Name, "' that was not created with BIND_UNORDERED_ACCESS flag"); - else - UNEXPECTED("Unexpected buffer view type"); - - CreateViewInternal(ViewDesc, ppView, false); -} - - -template <class BaseInterface, class RenderDeviceImplType, class BufferViewImplType, class TBuffViewObjAllocator> -void BufferBase<BaseInterface, RenderDeviceImplType, BufferViewImplType, TBuffViewObjAllocator>::CorrectBufferViewDesc(struct BufferViewDesc& ViewDesc) -{ - if (ViewDesc.ByteWidth == 0) - { - DEV_CHECK_ERR(this->m_Desc.uiSizeInBytes > ViewDesc.ByteOffset, "Byte offset (", ViewDesc.ByteOffset, ") exceeds buffer size (", this->m_Desc.uiSizeInBytes, ")"); - ViewDesc.ByteWidth = this->m_Desc.uiSizeInBytes - ViewDesc.ByteOffset; - } - if (ViewDesc.ByteOffset + ViewDesc.ByteWidth > this->m_Desc.uiSizeInBytes) - LOG_ERROR_AND_THROW("Buffer view range [", ViewDesc.ByteOffset, ", ", ViewDesc.ByteOffset + ViewDesc.ByteWidth, ") is out of the buffer boundaries [0, ", this->m_Desc.uiSizeInBytes, ")."); - if ((this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || - (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE)) - { - if (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_FORMATTED) - { - VERIFY(this->m_Desc.ElementByteStride != 0, "Element byte stride is zero"); - if ((ViewDesc.ByteOffset % this->m_Desc.ElementByteStride) != 0) - LOG_ERROR_AND_THROW("Buffer view byte offset (", ViewDesc.ByteOffset, ") is not multiple of element byte stride (", this->m_Desc.ElementByteStride, ")."); - if ((ViewDesc.ByteWidth % this->m_Desc.ElementByteStride) != 0) - LOG_ERROR_AND_THROW("Buffer view byte width (", ViewDesc.ByteWidth, ") is not multiple of element byte stride (", this->m_Desc.ElementByteStride, ")."); - } - - if (this->m_Desc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType == VT_UNDEFINED) - LOG_ERROR_AND_THROW("Format must be specified when creating a view of a formatted buffer"); - - if (this->m_Desc.Mode == BUFFER_MODE_FORMATTED || (this->m_Desc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType != VT_UNDEFINED)) - { - if (ViewDesc.Format.NumComponents <= 0 || ViewDesc.Format.NumComponents > 4) - LOG_ERROR_AND_THROW("Incorrect number of components (", Uint32{ViewDesc.Format.NumComponents}, "). 1, 2, 3, or 4 are allowed values"); - if (ViewDesc.Format.ValueType == VT_FLOAT32 || ViewDesc.Format.ValueType == VT_FLOAT16) - ViewDesc.Format.IsNormalized = false; - auto ViewElementStride = GetValueSize(ViewDesc.Format.ValueType) * Uint32{ViewDesc.Format.NumComponents}; - if (this->m_Desc.Mode == BUFFER_MODE_RAW && this->m_Desc.ElementByteStride == 0) - LOG_ERROR_AND_THROW("To enable formatted views of a raw buffer, element byte must be specified during buffer initialization"); - if (ViewElementStride != this->m_Desc.ElementByteStride) - LOG_ERROR_AND_THROW("Buffer element byte stride (", this->m_Desc.ElementByteStride, ") is not consistent with the size (", ViewElementStride, ") defined by the format of the view (", GetBufferFormatString(ViewDesc.Format), ')'); - } - - if (this->m_Desc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType == VT_UNDEFINED) - { - if ((ViewDesc.ByteOffset % 16) != 0) - LOG_ERROR_AND_THROW("When creating a RAW view, the offset of the first element from the start of the buffer (", ViewDesc.ByteOffset, ") must be a multiple of 16 bytes"); - } - } -} - -template <class BaseInterface, class RenderDeviceImplType, class BufferViewImplType, class TBuffViewObjAllocator> -IBufferView* BufferBase<BaseInterface, RenderDeviceImplType, BufferViewImplType, TBuffViewObjAllocator>::GetDefaultView(BUFFER_VIEW_TYPE ViewType) -{ - switch (ViewType) - { - case BUFFER_VIEW_SHADER_RESOURCE: return m_pDefaultSRV.get(); - case BUFFER_VIEW_UNORDERED_ACCESS: return m_pDefaultUAV.get(); - default: UNEXPECTED("Unknown view type"); return nullptr; - } -} - -template <class BaseInterface, class RenderDeviceImplType, class BufferViewImplType, class TBuffViewObjAllocator> -void BufferBase<BaseInterface, RenderDeviceImplType, BufferViewImplType, TBuffViewObjAllocator>::CreateDefaultViews() -{ - // Create default views for structured and raw buffers. For formatted buffers we do not know the view format, so - // cannot create views. - - if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) - { - BufferViewDesc ViewDesc; - ViewDesc.ViewType = BUFFER_VIEW_UNORDERED_ACCESS; - auto UAVName = FormatString("Default UAV of buffer '", this->m_Desc.Name, '\''); - ViewDesc.Name = UAVName.c_str(); - - IBufferView* pUAV = nullptr; - CreateViewInternal(ViewDesc, &pUAV, true); - m_pDefaultUAV.reset(static_cast<BufferViewImplType*>(pUAV)); - VERIFY(m_pDefaultUAV->GetDesc().ViewType == BUFFER_VIEW_UNORDERED_ACCESS, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE && (this->m_Desc.Mode == BUFFER_MODE_STRUCTURED || this->m_Desc.Mode == BUFFER_MODE_RAW)) - { - BufferViewDesc ViewDesc; - ViewDesc.ViewType = BUFFER_VIEW_SHADER_RESOURCE; - auto SRVName = FormatString("Default SRV of buffer '", this->m_Desc.Name, '\''); - ViewDesc.Name = SRVName.c_str(); - - IBufferView* pSRV = nullptr; - CreateViewInternal(ViewDesc, &pSRV, true); - m_pDefaultSRV.reset(static_cast<BufferViewImplType*>(pSRV)); - VERIFY(m_pDefaultSRV->GetDesc().ViewType == BUFFER_VIEW_SHADER_RESOURCE, "Unexpected view type"); - } -} - } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp index dcc186b5..e57b8c8c 100644 --- a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp +++ b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp @@ -45,6 +45,36 @@ namespace Diligent { +// clang-format off +bool VerifyDrawAttribs (const DrawAttribs& Attribs); +bool VerifyDrawIndexedAttribs (const DrawIndexedAttribs& Attribs); +bool VerifyDrawIndirectAttribs (const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer); +bool VerifyDrawIndexedIndirectAttribs(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer); + +bool VerifyDispatchComputeAttribs (const DispatchComputeAttribs& Attribs); +bool VerifyDispatchComputeIndirectAttribs(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer); +// clang-format on + +bool VerifyDrawMeshAttribs(Uint32 MaxDrawMeshTasksCount, const DrawMeshAttribs& Attribs); +bool VerifyDrawMeshIndirectAttribs(const DrawMeshIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer); + +bool VerifyResolveTextureSubresourceAttribs(const ResolveTextureSubresourceAttribs& ResolveAttribs, + const TextureDesc& SrcTexDesc, + const TextureDesc& DstTexDesc); + +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 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); +bool VerifyTraceRaysAttribs(const TraceRaysAttribs& Attribs); + + + /// Describes input vertex stream template <typename BufferImplType> struct VertexStreamInfo @@ -53,7 +83,9 @@ struct VertexStreamInfo /// Strong reference to the buffer object RefCntAutoPtr<BufferImplType> pBuffer; - Uint32 Offset = 0; ///< Offset in bytes + + /// Offset in bytes + Uint32 Offset = 0; }; /// Base implementation of the device context. @@ -62,7 +94,7 @@ struct VertexStreamInfo /// \tparam ImplementationTraits - implementation traits that define specific implementation details /// (texture implemenation type, buffer implementation type, etc.) /// \remarks Device context keeps strong references to all objects currently bound to -/// the pipeline: buffers, states, samplers, shaders, etc. +/// the pipeline: buffers, tetxures, states, SRBs, etc. /// The context also keeps strong references to the device and /// the swap chain. template <typename BaseInterface, typename ImplementationTraits> @@ -78,10 +110,12 @@ public: using QueryImplType = typename ImplementationTraits::QueryType; using FramebufferImplType = typename ImplementationTraits::FramebufferType; using RenderPassImplType = typename ImplementationTraits::RenderPassType; + using BottomLevelASType = typename ImplementationTraits::BottomLevelASType; + using TopLevelASType = typename ImplementationTraits::TopLevelASType; - /// \param pRefCounters - reference counters object that controls the lifetime of this device context. + /// \param pRefCounters - reference counters object that controls the lifetime of this device context. /// \param pRenderDevice - render device. - /// \param bIsDeferred - flag indicating if this instance is a deferred context + /// \param bIsDeferred - flag indicating if this instance is a deferred context DeviceContextBase(IReferenceCounters* pRefCounters, DeviceImplType* pRenderDevice, bool bIsDeferred) : // clang-format off TObjectBase {pRefCounters }, @@ -114,7 +148,9 @@ public: int); /// Base implementation of IDeviceContext::SetIndexBuffer(); caches the strong reference to the index buffer - inline virtual void DILIGENT_CALL_TYPE SetIndexBuffer(IBuffer* pIndexBuffer, Uint32 ByteOffset, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override = 0; + inline virtual void DILIGENT_CALL_TYPE SetIndexBuffer(IBuffer* pIndexBuffer, + Uint32 ByteOffset, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override = 0; /// Caches the viewports inline void SetViewports(Uint32 NumViewports, const Viewport* pViewports, Uint32& RTWidth, Uint32& RTHeight); @@ -251,20 +287,22 @@ protected: #ifdef DILIGENT_DEVELOPMENT // clang-format off - bool DvpVerifyDrawArguments (const DrawAttribs& Attribs)const; - bool DvpVerifyDrawIndexedArguments (const DrawIndexedAttribs& Attribs)const; - bool DvpVerifyDrawMeshArguments (const DrawMeshAttribs& Attribs)const; - bool DvpVerifyDrawIndirectArguments (const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; - bool DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; - bool DvpVerifyDrawMeshIndirectArguments (const DrawMeshIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; - - bool DvpVerifyDispatchArguments (const DispatchComputeAttribs& Attribs)const; - bool DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const; - - void DvpVerifyRenderTargets()const; - void DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier)const; - bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName)const; - bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName)const; + bool DvpVerifyDrawArguments (const DrawAttribs& Attribs) const; + bool DvpVerifyDrawIndexedArguments (const DrawIndexedAttribs& Attribs) const; + bool DvpVerifyDrawMeshArguments (const DrawMeshAttribs& Attribs) const; + bool DvpVerifyDrawIndirectArguments (const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const; + bool DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const; + bool DvpVerifyDrawMeshIndirectArguments (const DrawMeshIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const; + + bool DvpVerifyDispatchArguments (const DispatchComputeAttribs& Attribs) const; + bool DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const; + + bool DvpVerifyRenderTargets() const; + bool DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier) const; + bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName) const; + bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName) const; + bool DvpVerifyBLASState (const BottomLevelASType& BLAS, RESOURCE_STATE RequiredState, const char* OperationName) const; + bool DvpVerifyTLASState (const TopLevelASType& TLAS, RESOURCE_STATE RequiredState, const char* OperationName) const; #else bool DvpVerifyDrawArguments (const DrawAttribs& Attribs)const {return true;} bool DvpVerifyDrawIndexedArguments (const DrawIndexedAttribs& Attribs)const {return true;} @@ -276,13 +314,23 @@ protected: bool DvpVerifyDispatchArguments (const DispatchComputeAttribs& Attribs)const {return true;} bool DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer)const {return true;} - void DvpVerifyRenderTargets()const {} - void DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier)const {} - bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} - bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} + bool DvpVerifyRenderTargets()const {return true;} + bool DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier)const {return true;} + bool DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} + bool DvpVerifyBufferState (const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} + bool DvpVerifyBLASState (const BottomLevelASType& BLAS, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} + bool DvpVerifyTLASState (const TopLevelASType& TLAS, RESOURCE_STATE RequiredState, const char* OperationName)const {return true;} // clang-format on #endif + bool BuildBLAS(const BuildBLASAttribs& Attribs, int) const; + bool BuildTLAS(const BuildTLASAttribs& Attribs, int) const; + bool CopyBLAS(const CopyBLASAttribs& Attribs, int) const; + bool CopyTLAS(const CopyTLASAttribs& Attribs, int) const; + bool WriteBLASCompactedSize(const WriteBLASCompactedSizeAttribs& Attribs, int) const; + bool WriteTLASCompactedSize(const WriteTLASCompactedSizeAttribs& Attribs, int) const; + bool TraceRays(const TraceRaysAttribs& Attribs, int) const; + /// Strong reference to the device. RefCntAutoPtr<DeviceImplType> m_pDevice; @@ -368,13 +416,13 @@ protected: template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - SetVertexBuffers(Uint32 StartSlot, - Uint32 NumBuffersSet, - IBuffer** ppBuffers, - Uint32* pOffsets, - RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, - SET_VERTEX_BUFFERS_FLAGS Flags) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::SetVertexBuffers( + Uint32 StartSlot, + Uint32 NumBuffersSet, + IBuffer** ppBuffers, + Uint32* pOffsets, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, + SET_VERTEX_BUFFERS_FLAGS Flags) { #ifdef DILIGENT_DEVELOPMENT if (StartSlot >= MAX_BUFFER_SLOTS) @@ -429,15 +477,18 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - SetPipelineState(PipelineStateImplType* pPipelineState, int /*Dummy*/) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::SetPipelineState( + PipelineStateImplType* pPipelineState, + int /*Dummy*/) { m_pPipelineState = pPipelineState; } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, int) +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::CommitShaderResources( + IShaderResourceBinding* pShaderResourceBinding, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, + int) { #ifdef DILIGENT_DEVELOPMENT VERIFY(!(m_pActiveRenderPass != nullptr && StateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION), @@ -459,6 +510,7 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: } } #endif + return true; } @@ -472,8 +524,10 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>::InvalidateSt } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - SetIndexBuffer(IBuffer* pIndexBuffer, Uint32 ByteOffset, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::SetIndexBuffer( + IBuffer* pIndexBuffer, + Uint32 ByteOffset, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) { m_pIndexBuffer = ValidatedCast<BufferImplType>(pIndexBuffer); m_IndexDataStartOffset = ByteOffset; @@ -538,8 +592,11 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::SetStencilRe } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - SetViewports(Uint32 NumViewports, const Viewport* pViewports, Uint32& RTWidth, Uint32& RTHeight) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::SetViewports( + Uint32 NumViewports, + const Viewport* pViewports, + Uint32& RTWidth, + Uint32& RTHeight) { if (RTWidth == 0 || RTHeight == 0) { @@ -578,8 +635,11 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>::GetViewports } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - SetScissorRects(Uint32 NumRects, const Rect* pRects, Uint32& RTWidth, Uint32& RTHeight) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::SetScissorRects( + Uint32 NumRects, + const Rect* pRects, + Uint32& RTWidth, + Uint32& RTHeight) { if (RTWidth == 0 || RTHeight == 0) { @@ -599,8 +659,10 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - SetRenderTargets(Uint32 NumRenderTargets, ITextureView* ppRenderTargets[], ITextureView* pDepthStencil) +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::SetRenderTargets( + Uint32 NumRenderTargets, + ITextureView* ppRenderTargets[], + ITextureView* pDepthStencil) { if (NumRenderTargets == 0 && pDepthStencil == nullptr) { @@ -776,8 +838,10 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::SetSubpassRe template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - GetRenderTargets(Uint32& NumRenderTargets, ITextureView** ppRTVs, ITextureView** ppDSV) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::GetRenderTargets( + Uint32& NumRenderTargets, + ITextureView** ppRTVs, + ITextureView** ppDSV) { NumRenderTargets = m_NumBoundRenderTargets; @@ -956,33 +1020,8 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>::BeginRenderP { VERIFY(m_pActiveRenderPass == nullptr, "Attempting to begin render pass while another render pass ('", m_pActiveRenderPass->GetDesc().Name, "') is active."); VERIFY(m_pBoundFramebuffer == nullptr, "Attempting to begin render pass while another framebuffer ('", m_pBoundFramebuffer->GetDesc().Name, "') is bound."); - VERIFY(Attribs.pRenderPass != nullptr, "Render pass must not be null"); - VERIFY(Attribs.pFramebuffer != nullptr, "Framebuffer must not be null"); -#ifdef DILIGENT_DEBUG - { - const auto& RPDesc = Attribs.pRenderPass->GetDesc(); - Uint32 NumRequiredClearValues = 0; - for (Uint32 i = 0; i < RPDesc.AttachmentCount; ++i) - { - const auto& Attchmnt = RPDesc.pAttachments[i]; - if (Attchmnt.LoadOp == ATTACHMENT_LOAD_OP_CLEAR) - NumRequiredClearValues = i + 1; - - const auto& FmtAttribs = GetTextureFormatAttribs(Attchmnt.Format); - if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL) - { - if (Attchmnt.StencilLoadOp == ATTACHMENT_LOAD_OP_CLEAR) - NumRequiredClearValues = i + 1; - } - } - VERIFY(Attribs.ClearValueCount >= NumRequiredClearValues, - "Begin render pass operation requiers at least ", NumRequiredClearValues, - " clear values, but only ", Attribs.ClearValueCount, " are given."); - VERIFY(Attribs.ClearValueCount == 0 || Attribs.pClearValues != nullptr, - "pClearValues must not be null when ClearValueCount is not zero"); - } -#endif + VerifyBeginRenderPassAttribs(Attribs); // Reset current render targets (in Vulkan backend, this may end current render pass). ResetRenderTargets(); @@ -1023,6 +1062,7 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>::BeginRenderP m_pBoundFramebuffer = pNewFramebuffer; m_SubpassIndex = 0; m_RenderPassAttachmentsTransitionMode = Attribs.StateTransitionMode; + UpdateAttachmentStates(m_SubpassIndex); SetSubpassRenderTargets(); } @@ -1197,6 +1237,7 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::BeginQuery(I return false; } +#ifdef DILIGENT_DEVELOPMENT if (m_bIsDeferred) { LOG_ERROR_MESSAGE("IDeviceContext::BeginQuery: Deferred contexts do not support queries"); @@ -1208,6 +1249,7 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::BeginQuery(I LOG_ERROR_MESSAGE("BeginQuery() is disabled for timestamp queries. Call EndQuery() to set the timestamp."); return false; } +#endif if (!ValidatedCast<QueryImplType>(pQuery)->OnBeginQuery(this)) return false; @@ -1224,11 +1266,13 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::EndQuery(IQu return false; } +#ifdef DILIGENT_DEVELOPMENT if (m_bIsDeferred) { LOG_ERROR_MESSAGE("IDeviceContext::EndQuery: Deferred contexts do not support queries"); return false; } +#endif if (!ValidatedCast<QueryImplType>(pQuery)->OnEndQuery(this)) return false; @@ -1237,8 +1281,12 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::EndQuery(IQu } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - UpdateBuffer(IBuffer* pBuffer, Uint32 Offset, Uint32 Size, const void* pData, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::UpdateBuffer( + IBuffer* pBuffer, + Uint32 Offset, + Uint32 Size, + const void* pData, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) { VERIFY(pBuffer != nullptr, "Buffer must not be null"); VERIFY(m_pActiveRenderPass == nullptr, "UpdateBuffer command must be used outside of render pass."); @@ -1253,14 +1301,14 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - CopyBuffer(IBuffer* pSrcBuffer, - Uint32 SrcOffset, - RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, - IBuffer* pDstBuffer, - Uint32 DstOffset, - Uint32 Size, - RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::CopyBuffer( + IBuffer* pSrcBuffer, + Uint32 SrcOffset, + RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, + IBuffer* pDstBuffer, + Uint32 DstOffset, + Uint32 Size, + RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) { VERIFY(pSrcBuffer != nullptr, "Source buffer must not be null"); VERIFY(pDstBuffer != nullptr, "Destination buffer must not be null"); @@ -1276,8 +1324,11 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - MapBuffer(IBuffer* pBuffer, MAP_TYPE MapType, MAP_FLAGS MapFlags, PVoid& pMappedData) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::MapBuffer( + IBuffer* pBuffer, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + PVoid& pMappedData) { VERIFY(pBuffer, "pBuffer must not be null"); @@ -1331,8 +1382,7 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) { VERIFY(pBuffer, "pBuffer must not be null"); #ifdef DILIGENT_DEBUG @@ -1347,41 +1397,50 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - UpdateTexture(ITexture* pTexture, Uint32 MipLevel, Uint32 Slice, const Box& DstBox, const TextureSubResData& SubresData, RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::UpdateTexture( + ITexture* pTexture, + Uint32 MipLevel, + Uint32 Slice, + const Box& DstBox, + const TextureSubResData& SubresData, + RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, + RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode) { VERIFY(pTexture != nullptr, "pTexture must not be null"); VERIFY(m_pActiveRenderPass == nullptr, "UpdateTexture command must be used outside of render pass."); + ValidateUpdateTextureParams(pTexture->GetDesc(), MipLevel, Slice, DstBox, SubresData); } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - CopyTexture(const CopyTextureAttribs& CopyAttribs) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::CopyTexture(const CopyTextureAttribs& CopyAttribs) { VERIFY(CopyAttribs.pSrcTexture, "Src texture must not be null"); VERIFY(CopyAttribs.pDstTexture, "Dst texture must not be null"); VERIFY(m_pActiveRenderPass == nullptr, "CopyTexture command must be used outside of render pass."); + ValidateCopyTextureParams(CopyAttribs); } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - MapTextureSubresource(ITexture* pTexture, - Uint32 MipLevel, - Uint32 ArraySlice, - MAP_TYPE MapType, - MAP_FLAGS MapFlags, - const Box* pMapRegion, - MappedTextureSubresource& MappedData) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::MapTextureSubresource( + ITexture* pTexture, + Uint32 MipLevel, + Uint32 ArraySlice, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + const Box* pMapRegion, + MappedTextureSubresource& MappedData) { VERIFY(pTexture, "pTexture must not be null"); ValidateMapTextureParams(pTexture->GetDesc(), MipLevel, ArraySlice, MapType, MapFlags, pMapRegion); } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::UnmapTextureSubresource( + ITexture* pTexture, + Uint32 MipLevel, + Uint32 ArraySlice) { VERIFY(pTexture, "pTexture must not be null"); DEV_CHECK_ERR(MipLevel < pTexture->GetDesc().MipLevels, "Mip level is out of range"); @@ -1389,8 +1448,7 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - GenerateMips(ITextureView* pTexView) +inline void DeviceContextBase<BaseInterface, ImplementationTraits>::GenerateMips(ITextureView* pTexView) { VERIFY(pTexView != nullptr, "pTexView must not be null"); VERIFY(m_pActiveRenderPass == nullptr, "GenerateMips command must be used outside of render pass."); @@ -1407,241 +1465,353 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: template <typename BaseInterface, typename ImplementationTraits> -void DeviceContextBase<BaseInterface, ImplementationTraits>:: - ResolveTextureSubresource(ITexture* pSrcTexture, - ITexture* pDstTexture, - const ResolveTextureSubresourceAttribs& ResolveAttribs) +void DeviceContextBase<BaseInterface, ImplementationTraits>::ResolveTextureSubresource( + ITexture* pSrcTexture, + ITexture* pDstTexture, + const ResolveTextureSubresourceAttribs& ResolveAttribs) { #ifdef DILIGENT_DEVELOPMENT + VERIFY(m_pActiveRenderPass == nullptr, "ResolveTextureSubresource command must be used outside of render pass."); + VERIFY_EXPR(pSrcTexture != nullptr && pDstTexture != nullptr); const auto& SrcTexDesc = pSrcTexture->GetDesc(); const auto& DstTexDesc = pDstTexture->GetDesc(); - DEV_CHECK_ERR(SrcTexDesc.SampleCount > 1, - "Source texture '", SrcTexDesc.Name, "' of a resolve operation is not multi-sampled"); - DEV_CHECK_ERR(DstTexDesc.SampleCount == 1, - "Destination texture '", DstTexDesc.Name, "' of a resolve operation is multi-sampled"); - auto SrcMipLevelProps = GetMipLevelProperties(SrcTexDesc, ResolveAttribs.SrcMipLevel); - auto DstMipLevelProps = GetMipLevelProperties(DstTexDesc, ResolveAttribs.DstMipLevel); - DEV_CHECK_ERR(SrcMipLevelProps.LogicalWidth == DstMipLevelProps.LogicalWidth && SrcMipLevelProps.LogicalHeight == DstMipLevelProps.LogicalHeight, - "The size (", SrcMipLevelProps.LogicalWidth, "x", SrcMipLevelProps.LogicalHeight, - ") of the source subresource of a resolve operation (texture '", - SrcTexDesc.Name, "', mip ", ResolveAttribs.SrcMipLevel, ", slice ", ResolveAttribs.SrcSlice, - ") does not match the size (", DstMipLevelProps.LogicalWidth, "x", DstMipLevelProps.LogicalHeight, - ") of the destination subresource (texture '", DstTexDesc.Name, "', mip ", ResolveAttribs.DstMipLevel, ", slice ", - ResolveAttribs.DstSlice, ")"); - - const auto& SrcFmtAttribs = GetTextureFormatAttribs(SrcTexDesc.Format); - const auto& DstFmtAttribs = GetTextureFormatAttribs(DstTexDesc.Format); - const auto& ResolveFmtAttribs = GetTextureFormatAttribs(ResolveAttribs.Format); - if (!SrcFmtAttribs.IsTypeless && !DstFmtAttribs.IsTypeless) - { - DEV_CHECK_ERR(SrcTexDesc.Format == DstTexDesc.Format, - "Source (", SrcFmtAttribs.Name, ") and destination (", DstFmtAttribs.Name, - ") texture formats of a resolve operation must match exaclty or be compatible typeless formats"); - DEV_CHECK_ERR(ResolveAttribs.Format == TEX_FORMAT_UNKNOWN || SrcTexDesc.Format == ResolveAttribs.Format, "Invalid format of a resolve operation"); - } - if (SrcFmtAttribs.IsTypeless && DstFmtAttribs.IsTypeless) - { - DEV_CHECK_ERR(ResolveAttribs.Format != TEX_FORMAT_UNKNOWN, - "Format of a resolve operation must not be unknown when both src and dst texture formats are typeless"); - } - if (SrcFmtAttribs.IsTypeless || DstFmtAttribs.IsTypeless) - { - DEV_CHECK_ERR(!ResolveFmtAttribs.IsTypeless, - "Format of a resolve operation must not be typeless when one of the texture formats is typeless"); - } - VERIFY(m_pActiveRenderPass == nullptr, "ResolveTextureSubresource command must be used outside of render pass."); + + VerifyResolveTextureSubresourceAttribs(ResolveAttribs, SrcTexDesc, DstTexDesc); #endif } + +template <typename BaseInterface, typename ImplementationTraits> +bool DeviceContextBase<BaseInterface, ImplementationTraits>::BuildBLAS(const BuildBLASAttribs& Attribs, int) const +{ #ifdef DILIGENT_DEVELOPMENT + if (m_pDevice->GetDeviceCaps().Features.RayTracing != DEVICE_FEATURE_STATE_ENABLED) + { + LOG_ERROR_MESSAGE("IDeviceContext::BuildBLAS: ray tracing is not supported by this device"); + return false; + } + + if (m_pActiveRenderPass != nullptr) + { + LOG_ERROR_MESSAGE("IDeviceContext::BuildBLAS command must be performed outside of render pass"); + return false; + } + + if (!VerifyBuildBLASAttribs(Attribs)) + return false; +#endif + + return true; +} + template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDrawArguments(const DrawAttribs& Attribs) const +bool DeviceContextBase<BaseInterface, ImplementationTraits>::BuildTLAS(const BuildTLASAttribs& Attribs, int) const { - if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) - return true; +#ifdef DILIGENT_DEVELOPMENT + if (m_pDevice->GetDeviceCaps().Features.RayTracing != DEVICE_FEATURE_STATE_ENABLED) + { + LOG_ERROR_MESSAGE("IDeviceContext::BuildTLAS: ray tracing is not supported by this device"); + return false; + } - if (!m_pPipelineState) + if (m_pActiveRenderPass != nullptr) { - LOG_ERROR_MESSAGE("Draw command arguments are invalid: no pipeline state is bound."); + LOG_ERROR_MESSAGE("IDeviceContext::BuildTLAS command must be performed outside of render pass"); return false; } - if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_GRAPHICS) + if (!VerifyBuildTLASAttribs(Attribs)) + return false; +#endif + + return true; +} + +template <typename BaseInterface, typename ImplementationTraits> +bool DeviceContextBase<BaseInterface, ImplementationTraits>::CopyBLAS(const CopyBLASAttribs& Attribs, int) const +{ +#ifdef DILIGENT_DEVELOPMENT + if (m_pDevice->GetDeviceCaps().Features.RayTracing != DEVICE_FEATURE_STATE_ENABLED) { - LOG_ERROR_MESSAGE("Draw command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline."); + LOG_ERROR_MESSAGE("IDeviceContext::CopyBLAS: ray tracing is not supported by this device"); return false; } - if (Attribs.NumVertices == 0) + if (m_pActiveRenderPass != nullptr) { - LOG_WARNING_MESSAGE("Draw command arguments are invalid: number of vertices to draw is zero."); + LOG_ERROR_MESSAGE("IDeviceContext::CopyBLAS command must be performed outside of render pass"); + return false; } + if (!VerifyCopyBLASAttribs(m_pDevice, Attribs)) + return false; +#endif + return true; } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDrawIndexedArguments(const DrawIndexedAttribs& Attribs) const +bool DeviceContextBase<BaseInterface, ImplementationTraits>::CopyTLAS(const CopyTLASAttribs& Attribs, int) const { - if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) - return true; +#ifdef DILIGENT_DEVELOPMENT + if (m_pDevice->GetDeviceCaps().Features.RayTracing != DEVICE_FEATURE_STATE_ENABLED) + { + LOG_ERROR_MESSAGE("IDeviceContext::CopyTLAS: ray tracing is not supported by this device"); + return false; + } - if (!m_pPipelineState) + if (m_pActiveRenderPass != nullptr) { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no pipeline state is bound."); + LOG_ERROR_MESSAGE("IDeviceContext::CopyTLAS command must be performed outside of render pass"); return false; } - if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_GRAPHICS) + if (!VerifyCopyTLASAttribs(Attribs)) + return false; + + if (!ValidatedCast<TopLevelASType>(Attribs.pSrc)->ValidateContent()) { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: pipeline state '", - m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline."); + LOG_ERROR_MESSAGE("IDeviceContext::CopyTLAS: pSrc acceleration structure is not valid"); return false; } +#endif - if (Attribs.IndexType != VT_UINT16 && Attribs.IndexType != VT_UINT32) + return true; +} + +template <typename BaseInterface, typename ImplementationTraits> +bool DeviceContextBase<BaseInterface, ImplementationTraits>::WriteBLASCompactedSize(const WriteBLASCompactedSizeAttribs& Attribs, int) const +{ +#ifdef DILIGENT_DEVELOPMENT + if (m_pDevice->GetDeviceCaps().Features.RayTracing != DEVICE_FEATURE_STATE_ENABLED) { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: IndexType (", - GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); + LOG_ERROR_MESSAGE("IDeviceContext::WriteBLASCompactedSize: ray tracing is not supported by this device"); return false; } - if (!m_pIndexBuffer) + if (m_pActiveRenderPass != nullptr) { - LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no index buffer is bound."); + LOG_ERROR_MESSAGE("IDeviceContext::WriteBLASCompactedSize: command must be performed outside of render pass"); return false; } - if (Attribs.NumIndices == 0) + if (!VerifyWriteBLASCompactedSizeAttribs(m_pDevice, Attribs)) + return false; +#endif + + return true; +} + +template <typename BaseInterface, typename ImplementationTraits> +bool DeviceContextBase<BaseInterface, ImplementationTraits>::WriteTLASCompactedSize(const WriteTLASCompactedSizeAttribs& Attribs, int) const +{ +#ifdef DILIGENT_DEVELOPMENT + if (m_pDevice->GetDeviceCaps().Features.RayTracing != DEVICE_FEATURE_STATE_ENABLED) { - LOG_WARNING_MESSAGE("DrawIndexed command arguments are invalid: number of indices to draw is zero."); + LOG_ERROR_MESSAGE("IDeviceContext::WriteTLASCompactedSize: ray tracing is not supported by this device"); + return false; } + if (m_pActiveRenderPass != nullptr) + { + LOG_ERROR_MESSAGE("IDeviceContext::WriteTLASCompactedSize: command must be performed outside of render pass"); + return false; + } + + if (!VerifyWriteTLASCompactedSizeAttribs(m_pDevice, Attribs)) + return false; +#endif + return true; } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDrawMeshArguments(const DrawMeshAttribs& Attribs) const +bool DeviceContextBase<BaseInterface, ImplementationTraits>::TraceRays(const TraceRaysAttribs& Attribs, int) const { - if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) - return true; +#ifdef DILIGENT_DEVELOPMENT + if (m_pDevice->GetDeviceCaps().Features.RayTracing != DEVICE_FEATURE_STATE_ENABLED) + { + LOG_ERROR_MESSAGE("IDeviceContext::TraceRays: ray tracing is not supported by this device"); + return false; + } if (!m_pPipelineState) { - LOG_ERROR_MESSAGE("DrawMesh command arguments are invalid: no pipeline state is bound."); + LOG_ERROR_MESSAGE("IDeviceContext::TraceRays command arguments are invalid: no pipeline state is bound."); return false; } - if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_MESH) + if (!m_pPipelineState->GetDesc().IsRayTracingPipeline()) { - LOG_ERROR_MESSAGE("DrawMesh command arguments are invalid: pipeline state '", - m_pPipelineState->GetDesc().Name, "' is not a mesh pipeline."); + LOG_ERROR_MESSAGE("IDeviceContext::TraceRays command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is not a ray tracing pipeline."); + return false; + } + + if (m_pActiveRenderPass != nullptr && Attribs.SBTTransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION) + { + LOG_ERROR_MESSAGE("IDeviceContext::TraceRays command uses resource state transition and must be performed outside of render pass"); + return false; + } + + if (!VerifyTraceRaysAttribs(Attribs)) + return false; + + if (m_pPipelineState.RawPtr() != Attribs.pSBT->GetDesc().pPSO) + { + LOG_ERROR_MESSAGE("IDeviceContext::TraceRays command arguments are invalid: currently bound pipeline ", m_pPipelineState->GetDesc().Name, + "doesn't match the pipeline ", Attribs.pSBT->GetDesc().pPSO->GetDesc().Name, " that was used in ShaderBindingTable"); return false; } - if (Attribs.ThreadGroupCount == 0) + if ((Attribs.DimensionX * Attribs.DimensionY * Attribs.DimensionZ) > m_pDevice->GetProperties().MaxRayGenThreads) { - LOG_WARNING_MESSAGE("DrawMesh command arguments are invalid: number of groups to dispatch is zero."); + LOG_ERROR_MESSAGE("IDeviceContext::TraceRays command arguments are invalid: the dimension must not exceed the ", m_pDevice->GetProperties().MaxRayGenThreads, " threads"); + return false; } +#endif return true; } + + + +#ifdef DILIGENT_DEVELOPMENT + template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDrawIndirectArguments(const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDrawArguments(const DrawAttribs& Attribs) const { if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) return true; if (!m_pPipelineState) { - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: no pipeline state is bound."); + LOG_ERROR_MESSAGE("Draw command arguments are invalid: no pipeline state is bound."); return false; } if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_GRAPHICS) { + LOG_ERROR_MESSAGE("Draw command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline."); + return false; + } - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: pipeline state '", + return VerifyDrawAttribs(Attribs); +} + +template <typename BaseInterface, typename ImplementationTraits> +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDrawIndexedArguments(const DrawIndexedAttribs& Attribs) const +{ + if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) + return true; + + if (!m_pPipelineState) + { + LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no pipeline state is bound."); + return false; + } + + if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_GRAPHICS) + { + LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline."); return false; } - if (pAttribsBuffer != nullptr) + if (!m_pIndexBuffer) { - if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) - { - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: indirect draw arguments buffer '", - pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); - return false; - } + LOG_ERROR_MESSAGE("DrawIndexed command arguments are invalid: no index buffer is bound."); + return false; } - else + + return VerifyDrawIndexedAttribs(Attribs); +} + +template <typename BaseInterface, typename ImplementationTraits> +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDrawMeshArguments(const DrawMeshAttribs& Attribs) const +{ + if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) + return true; + + if (m_pDevice->GetDeviceCaps().Features.MeshShaders != DEVICE_FEATURE_STATE_ENABLED) { - LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: indirect draw arguments buffer is null."); + LOG_ERROR_MESSAGE("DrawMesh: mesh shaders are not supported by this device"); return false; } - if (m_pActiveRenderPass != nullptr && Attribs.IndirectAttribsBufferStateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION) + if (!m_pPipelineState) { - LOG_ERROR_MESSAGE("Resource state transitons are not allowed inside a render pass and may result in an undefined behavior. " - "Do not use RESOURCE_STATE_TRANSITION_MODE_TRANSITION or end the render pass first."); + LOG_ERROR_MESSAGE("DrawMesh command arguments are invalid: no pipeline state is bound."); return false; } - return true; + if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_MESH) + { + LOG_ERROR_MESSAGE("DrawMesh command arguments are invalid: pipeline state '", + m_pPipelineState->GetDesc().Name, "' is not a mesh pipeline."); + return false; + } + + return VerifyDrawMeshAttribs(m_pDevice->GetProperties().MaxDrawMeshTasksCount, Attribs); } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDrawIndexedIndirectArguments(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDrawIndirectArguments( + const DrawIndirectAttribs& Attribs, + const IBuffer* pAttribsBuffer) const { if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) return true; if (!m_pPipelineState) { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no pipeline state is bound."); + LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: no pipeline state is bound."); return false; } if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_GRAPHICS) { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: pipeline state '", + + LOG_ERROR_MESSAGE("DrawIndirect command arguments are invalid: pipeline state '", m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline."); return false; } - if (Attribs.IndexType != VT_UINT16 && Attribs.IndexType != VT_UINT32) + if (m_pActiveRenderPass != nullptr && Attribs.IndirectAttribsBufferStateTransitionMode == RESOURCE_STATE_TRANSITION_MODE_TRANSITION) { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: IndexType (", - GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); + LOG_ERROR_MESSAGE("Resource state transitons are not allowed inside a render pass and may result in an undefined behavior. " + "Do not use RESOURCE_STATE_TRANSITION_MODE_TRANSITION or end the render pass first."); return false; } - if (!m_pIndexBuffer) + return VerifyDrawIndirectAttribs(Attribs, pAttribsBuffer); +} + +template <typename BaseInterface, typename ImplementationTraits> +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDrawIndexedIndirectArguments( + const DrawIndexedIndirectAttribs& Attribs, + const IBuffer* pAttribsBuffer) const +{ + if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) + return true; + + if (!m_pPipelineState) { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no index buffer is bound."); + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no pipeline state is bound."); return false; } - if (pAttribsBuffer != nullptr) + if (m_pPipelineState->GetDesc().PipelineType != PIPELINE_TYPE_GRAPHICS) { - if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) - { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: indirect draw arguments buffer '", - pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); - return false; - } + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: pipeline state '", + m_pPipelineState->GetDesc().Name, "' is not a graphics pipeline."); + return false; } - else + + if (!m_pIndexBuffer) { - LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: indirect draw arguments buffer is null."); + LOG_ERROR_MESSAGE("DrawIndexedIndirect command arguments are invalid: no index buffer is bound."); return false; } @@ -1652,16 +1822,23 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: return false; } - return true; + return VerifyDrawIndexedIndirectAttribs(Attribs, pAttribsBuffer); } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDrawMeshIndirectArguments(const DrawMeshIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDrawMeshIndirectArguments( + const DrawMeshIndirectAttribs& Attribs, + const IBuffer* pAttribsBuffer) const { if ((Attribs.Flags & DRAW_FLAG_VERIFY_DRAW_ATTRIBS) == 0) return true; + if (m_pDevice->GetDeviceCaps().Features.MeshShaders != DEVICE_FEATURE_STATE_ENABLED) + { + LOG_ERROR_MESSAGE("DrawMeshIndirect: mesh shaders are not supported by this device"); + return false; + } + if (!m_pPipelineState) { LOG_ERROR_MESSAGE("DrawMeshIndirect command arguments are invalid: no pipeline state is bound."); @@ -1675,39 +1852,23 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: return false; } - if (pAttribsBuffer != nullptr) - { - if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) - { - LOG_ERROR_MESSAGE("DrawMeshIndirect command arguments are invalid: indirect draw arguments buffer '", - pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); - return false; - } - } - else - { - LOG_ERROR_MESSAGE("DrawMeshIndirect command arguments are invalid: indirect draw arguments buffer is null."); - return false; - } - - return true; + return VerifyDrawMeshIndirectAttribs(Attribs, pAttribsBuffer); } template <typename BaseInterface, typename ImplementationTraits> -inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyRenderTargets() const +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyRenderTargets() const { if (!m_pPipelineState) { LOG_ERROR_MESSAGE("No pipeline state is bound"); - return; + return false; } const auto& PSODesc = m_pPipelineState->GetDesc(); if (!PSODesc.IsAnyGraphicsPipeline()) { LOG_ERROR_MESSAGE("Pipeline state '", PSODesc.Name, "' is not a graphics pipeline"); - return; + return false; } TEXTURE_FORMAT BoundRTVFormats[8] = {TEX_FORMAT_UNKNOWN}; @@ -1749,13 +1910,14 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: "' (", GetTextureFormatAttribs(PSOFmt).Name, ")."); } } + + return true; } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDispatchArguments(const DispatchComputeAttribs& Attribs) const +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDispatchArguments(const DispatchComputeAttribs& Attribs) const { if (!m_pPipelineState) { @@ -1776,21 +1938,13 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: return false; } - if (Attribs.ThreadGroupCountX == 0) - LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountX is zero."); - - if (Attribs.ThreadGroupCountY == 0) - LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountY is zero."); - - if (Attribs.ThreadGroupCountZ == 0) - LOG_WARNING_MESSAGE("DispatchCompute command arguments are invalid: ThreadGroupCountZ is zero."); - - return true; + return VerifyDispatchComputeAttribs(Attribs); } template <typename BaseInterface, typename ImplementationTraits> -inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyDispatchIndirectArguments(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) const +inline bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyDispatchIndirectArguments( + const DispatchComputeIndirectAttribs& Attribs, + const IBuffer* pAttribsBuffer) const { if (!m_pPipelineState) { @@ -1811,91 +1965,21 @@ inline bool DeviceContextBase<BaseInterface, ImplementationTraits>:: return false; } - if (pAttribsBuffer != nullptr) - { - if ((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) == 0) - { - LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: indirect dispatch arguments buffer '", - pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); - return false; - } - } - else - { - LOG_ERROR_MESSAGE("DispatchComputeIndirect command arguments are invalid: indirect dispatch arguments buffer is null."); - return false; - } - - return true; + return VerifyDispatchComputeIndirectAttribs(Attribs, pAttribsBuffer); } template <typename BaseInterface, typename ImplementationTraits> -void DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier) const +bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyStateTransitionDesc(const StateTransitionDesc& Barrier) const { - DEV_CHECK_ERR((Barrier.pTexture != nullptr) ^ (Barrier.pBuffer != nullptr), "Exactly one of pTexture or pBuffer members of StateTransitionDesc must not be null"); - DEV_CHECK_ERR(Barrier.NewState != RESOURCE_STATE_UNKNOWN, "New resource state can't be unknown"); - RESOURCE_STATE OldState = RESOURCE_STATE_UNKNOWN; - if (Barrier.pTexture) - { - const auto& TexDesc = Barrier.pTexture->GetDesc(); - - DEV_CHECK_ERR(VerifyResourceStates(Barrier.NewState, true), "Invlaid new state specified for texture '", TexDesc.Name, "'"); - OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : Barrier.pTexture->GetState(); - DEV_CHECK_ERR(OldState != RESOURCE_STATE_UNKNOWN, - "The state of texture '", TexDesc.Name, - "' is unknown to the engine and is not explicitly specified in the barrier"); - DEV_CHECK_ERR(VerifyResourceStates(OldState, true), "Invlaid old state specified for texture '", TexDesc.Name, "'"); - - DEV_CHECK_ERR(Barrier.FirstMipLevel < TexDesc.MipLevels, "First mip level (", Barrier.FirstMipLevel, - ") specified by the barrier is out of range. Texture '", - TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); - DEV_CHECK_ERR(Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.FirstMipLevel + Barrier.MipLevelsCount <= TexDesc.MipLevels, - "Mip level range ", Barrier.FirstMipLevel, "..", Barrier.FirstMipLevel + Barrier.MipLevelsCount - 1, - " specified by the barrier is out of range. Texture '", - TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); - - DEV_CHECK_ERR(Barrier.FirstArraySlice < TexDesc.ArraySize, "First array slice (", Barrier.FirstArraySlice, - ") specified by the barrier is out of range. Array size of texture '", - TexDesc.Name, "' is ", TexDesc.ArraySize); - DEV_CHECK_ERR(Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.FirstArraySlice + Barrier.ArraySliceCount <= TexDesc.ArraySize, - "Array slice range ", Barrier.FirstArraySlice, "..", Barrier.FirstArraySlice + Barrier.ArraySliceCount - 1, - " specified by the barrier is out of range. Array size of texture '", - TexDesc.Name, "' is ", TexDesc.ArraySize); - - auto DevType = m_pDevice->GetDeviceCaps().DevType; - if (DevType != RENDER_DEVICE_TYPE_D3D12 && DevType != RENDER_DEVICE_TYPE_VULKAN) - { - DEV_CHECK_ERR(Barrier.FirstMipLevel == 0 && (Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.MipLevelsCount == TexDesc.MipLevels), - "Failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); - DEV_CHECK_ERR(Barrier.FirstArraySlice == 0 && (Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.ArraySliceCount == TexDesc.ArraySize), - "Failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); - } - } - else - { - const auto& BuffDesc = Barrier.pBuffer->GetDesc(); - DEV_CHECK_ERR(VerifyResourceStates(Barrier.NewState, false), "Invlaid new state specified for buffer '", BuffDesc.Name, "'"); - OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : Barrier.pBuffer->GetState(); - DEV_CHECK_ERR(OldState != RESOURCE_STATE_UNKNOWN, "The state of buffer '", BuffDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier"); - DEV_CHECK_ERR(VerifyResourceStates(OldState, false), "Invlaid old state specified for buffer '", BuffDesc.Name, "'"); - } - - if (OldState == RESOURCE_STATE_UNORDERED_ACCESS && Barrier.NewState == RESOURCE_STATE_UNORDERED_ACCESS) - { - DEV_CHECK_ERR(Barrier.TransitionType == STATE_TRANSITION_TYPE_IMMEDIATE, "For UAV barriers, transition type must be STATE_TRANSITION_TYPE_IMMEDIATE"); - } - - if (Barrier.TransitionType == STATE_TRANSITION_TYPE_BEGIN) - { - DEV_CHECK_ERR(!Barrier.UpdateResourceState, "Resource state can't be updated in begin-split barrier"); - } + return VerifyStateTransitionDesc(m_pDevice, Barrier); } template <typename BaseInterface, typename ImplementationTraits> -bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyTextureState(const TextureImplType& Texture, RESOURCE_STATE RequiredState, const char* OperationName) const +bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyTextureState( + const TextureImplType& Texture, + RESOURCE_STATE RequiredState, + const char* OperationName) const { if (Texture.IsInKnownState() && !Texture.CheckState(RequiredState)) { @@ -1909,8 +1993,10 @@ bool DeviceContextBase<BaseInterface, ImplementationTraits>:: } template <typename BaseInterface, typename ImplementationTraits> -bool DeviceContextBase<BaseInterface, ImplementationTraits>:: - DvpVerifyBufferState(const BufferImplType& Buffer, RESOURCE_STATE RequiredState, const char* OperationName) const +bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyBufferState( + const BufferImplType& Buffer, + RESOURCE_STATE RequiredState, + const char* OperationName) const { if (Buffer.IsInKnownState() && !Buffer.CheckState(RequiredState)) { @@ -1923,6 +2009,40 @@ bool DeviceContextBase<BaseInterface, ImplementationTraits>:: return true; } +template <typename BaseInterface, typename ImplementationTraits> +bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyBLASState( + const BottomLevelASType& BLAS, + RESOURCE_STATE RequiredState, + const char* OperationName) const +{ + if (BLAS.IsInKnownState() && !BLAS.CheckState(RequiredState)) + { + LOG_ERROR_MESSAGE(OperationName, " requires BLAS '", BLAS.GetDesc().Name, "' to be transitioned to ", GetResourceStateString(RequiredState), + " state. Actual BLAS state: ", GetResourceStateString(BLAS.GetState()), + ". Use appropriate state transiton flags or explicitly transition the BLAS using IDeviceContext::TransitionResourceStates() method."); + return false; + } + + return true; +} + +template <typename BaseInterface, typename ImplementationTraits> +bool DeviceContextBase<BaseInterface, ImplementationTraits>::DvpVerifyTLASState( + const TopLevelASType& TLAS, + RESOURCE_STATE RequiredState, + const char* OperationName) const +{ + if (TLAS.IsInKnownState() && !TLAS.CheckState(RequiredState)) + { + LOG_ERROR_MESSAGE(OperationName, " requires TLAS '", TLAS.GetDesc().Name, "' to be transitioned to ", GetResourceStateString(RequiredState), + " state. Actual TLAS state: ", GetResourceStateString(TLAS.GetState()), + ". Use appropriate state transiton flags or explicitly transition the TLAS using IDeviceContext::TransitionResourceStates() method."); + return false; + } + + return true; +} + #endif // DILIGENT_DEVELOPMENT } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/FramebufferBase.hpp b/Graphics/GraphicsEngine/include/FramebufferBase.hpp index fc008d73..0cd484dc 100644 --- a/Graphics/GraphicsEngine/include/FramebufferBase.hpp +++ b/Graphics/GraphicsEngine/include/FramebufferBase.hpp @@ -39,7 +39,7 @@ namespace Diligent { -void ValidateFramebufferDesc(const FramebufferDesc& Desc); +void ValidateFramebufferDesc(const FramebufferDesc& Desc) noexcept(false); /// Template class implementing base functionality for the framebuffer object. @@ -66,7 +66,7 @@ public: TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal}, m_pRenderPass{Desc.pRenderPass} { - ValidateFramebufferDesc(Desc); + ValidateFramebufferDesc(this->m_Desc); if (this->m_Desc.AttachmentCount > 0) { diff --git a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp index a86fae07..54b9ba0c 100644 --- a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp +++ b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp @@ -32,19 +32,27 @@ #include <array> #include <vector> +#include <unordered_map> +#include <unordered_set> #include "PipelineState.h" #include "DeviceObjectBase.hpp" #include "STDAllocator.hpp" #include "EngineMemory.h" #include "GraphicsAccessories.hpp" -#include "LinearAllocator.hpp" +#include "FixedLinearAllocator.hpp" +#include "HashUtils.hpp" namespace Diligent { void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(false); void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(false); +void ValidateRayTracingPipelineCreateInfo(IRenderDevice* pDevice, Uint32 MaxRecursion, const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(false); + +void CopyRayTracingShaderGroups(std::unordered_map<HashMapStringKey, Uint32, HashMapStringKey::Hasher>& NameToGroupIndex, + const RayTracingPipelineStateCreateInfo& CreateInfo, + FixedLinearAllocator& MemPool) noexcept(false); void CorrectGraphicsPipelineDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept; @@ -109,6 +117,20 @@ public: ValidateComputePipelineCreateInfo(ComputePipelineCI); } + /// \param pRefCounters - Reference counters object that controls the lifetime of this PSO + /// \param pDevice - Pointer to the device. + /// \param RayTracingPipelineCI - Ray tracing pipeline create information. + /// \param bIsDeviceInternal - Flag indicating if the pipeline state is an internal device object and + /// must not keep a strong reference to the device. + PipelineStateBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const RayTracingPipelineStateCreateInfo& RayTracingPipelineCI, + bool bIsDeviceInternal = false) : + PipelineStateBase{pRefCounters, pDevice, RayTracingPipelineCI.PSODesc, bIsDeviceInternal} + { + ValidateRayTracingPipelineCreateInfo(pDevice, pDevice->GetProperties().MaxRayTracingRecursionDepth, RayTracingPipelineCI); + } + ~PipelineStateBase() { @@ -129,6 +151,26 @@ public: RasterizerStateRegistry.ReportDeletedObject(); DSSRegistry.ReportDeletedObject(); */ + VERIFY(m_IsDestructed, "This object must be explicitly destructed with Destruct()"); + } + + void Destruct() + { + VERIFY(!m_IsDestructed, "This object has already been destructed"); + + if (this->m_Desc.IsAnyGraphicsPipeline() && m_pGraphicsPipelineDesc != nullptr) + { + m_pGraphicsPipelineDesc->~GraphicsPipelineDesc(); + m_pGraphicsPipelineDesc = nullptr; + } + else if (this->m_Desc.IsRayTracingPipeline() && m_pRayTracingPipelineData != nullptr) + { + m_pRayTracingPipelineData->~RayTracingPipelineData(); + m_pRayTracingPipelineData = nullptr; + } +#if DILIGENT_DEBUG + m_IsDestructed = true; +#endif } IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_PipelineState, TDeviceObjectBase) @@ -160,7 +202,41 @@ public: return *m_pGraphicsPipelineDesc; } + virtual const RayTracingPipelineDesc& DILIGENT_CALL_TYPE GetRayTracingPipelineDesc() const override final + { + VERIFY_EXPR(this->m_Desc.IsRayTracingPipeline()); + VERIFY_EXPR(m_pRayTracingPipelineData != nullptr); + return m_pRayTracingPipelineData->Desc; + } + + inline void CopyShaderHandle(const char* Name, void* pData, size_t DataSize) const + { + VERIFY_EXPR(this->m_Desc.IsRayTracingPipeline()); + VERIFY_EXPR(m_pRayTracingPipelineData != nullptr); + + const auto ShaderHandleSize = m_pRayTracingPipelineData->ShaderHandleSize; + VERIFY_EXPR(ShaderHandleSize <= DataSize); + + if (Name == nullptr || Name[0] == '\0') + { + // set shader binding to zero to skip shader execution + std::memset(pData, 0, ShaderHandleSize); + return; + } + + auto iter = m_pRayTracingPipelineData->NameToGroupIndex.find(Name); + if (iter != m_pRayTracingPipelineData->NameToGroupIndex.end()) + { + VERIFY_EXPR(ShaderHandleSize * (iter->second + 1) <= m_pRayTracingPipelineData->ShaderDataSize); + std::memcpy(pData, &m_pRayTracingPipelineData->Shaders[ShaderHandleSize * iter->second], ShaderHandleSize); + return; + } + UNEXPECTED("Can't find shader group with specified name"); + } + protected: + using TNameToGroupIndexMap = std::unordered_map<HashMapStringKey, Uint32, HashMapStringKey::Hasher>; + Int8 GetStaticVariableCountHelper(SHADER_TYPE ShaderType, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& ResourceLayoutIndex) const { if (!IsConsistentShaderType(ShaderType, this->m_Desc.PipelineType)) @@ -223,7 +299,7 @@ protected: void ReserveSpaceForPipelineDesc(const GraphicsPipelineStateCreateInfo& CreateInfo, - LinearAllocator& MemPool) noexcept + FixedLinearAllocator& MemPool) noexcept { MemPool.AddSpace<GraphicsPipelineDesc>(); ReserveResourceLayout(CreateInfo.PSODesc.ResourceLayout, MemPool); @@ -238,20 +314,50 @@ protected: } MemPool.AddSpace<Uint32>(m_BufferSlotsUsed); + + static_assert(std::is_trivially_destructible<decltype(*InputLayout.LayoutElements)>::value, "Add destructor for this object to Destruct()"); } void ReserveSpaceForPipelineDesc(const ComputePipelineStateCreateInfo& CreateInfo, - LinearAllocator& MemPool) const noexcept + FixedLinearAllocator& MemPool) const noexcept { ReserveResourceLayout(CreateInfo.PSODesc.ResourceLayout, MemPool); } + void ReserveSpaceForPipelineDesc(const RayTracingPipelineStateCreateInfo& CreateInfo, + FixedLinearAllocator& MemPool) const noexcept + { + for (Uint32 i = 0; i < CreateInfo.GeneralShaderCount; ++i) + { + MemPool.AddSpaceForString(CreateInfo.pGeneralShaders[i].Name); + } + for (Uint32 i = 0; i < CreateInfo.TriangleHitShaderCount; ++i) + { + MemPool.AddSpaceForString(CreateInfo.pTriangleHitShaders[i].Name); + } + for (Uint32 i = 0; i < CreateInfo.ProceduralHitShaderCount; ++i) + { + MemPool.AddSpaceForString(CreateInfo.pProceduralHitShaders[i].Name); + } + + ReserveResourceLayout(CreateInfo.PSODesc.ResourceLayout, MemPool); + + size_t RTDataSize = sizeof(RayTracingPipelineData); + // reserve size for shader handles + const auto ShaderHandleSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + RTDataSize += ShaderHandleSize * (CreateInfo.GeneralShaderCount + CreateInfo.TriangleHitShaderCount + CreateInfo.ProceduralHitShaderCount); + // Extra bytes are reserved to avoid compiler errors on zero-sized arrays + RTDataSize -= sizeof(RayTracingPipelineData::Shaders); + MemPool.AddSpace(RTDataSize, alignof(RayTracingPipelineData)); + } + template <typename ShaderImplType, typename TShaderStages> void ExtractShaders(const GraphicsPipelineStateCreateInfo& CreateInfo, TShaderStages& ShaderStages) { VERIFY(m_NumShaderStages == 0, "The number of shader stages is not zero! ExtractShaders must only be called once."); + VERIFY_EXPR(this->m_Desc.IsAnyGraphicsPipeline()); ShaderStages.clear(); auto AddShaderStage = [&](IShader* pShader) { @@ -298,6 +404,7 @@ protected: TShaderStages& ShaderStages) { VERIFY(m_NumShaderStages == 0, "The number of shader stages is not zero! ExtractShaders must only be called once."); + VERIFY_EXPR(this->m_Desc.IsComputePipeline()); ShaderStages.clear(); @@ -311,9 +418,71 @@ protected: VERIFY_EXPR(!ShaderStages.empty() && ShaderStages.size() == m_NumShaderStages); } + template <typename ShaderImplType, typename TShaderStages> + void ExtractShaders(const RayTracingPipelineStateCreateInfo& CreateInfo, + TShaderStages& ShaderStages) + { + VERIFY(m_NumShaderStages == 0, "The number of shader stages is not zero! ExtractShaders must only be called once."); + VERIFY_EXPR(this->m_Desc.IsRayTracingPipeline()); + + std::unordered_set<IShader*> UniqueShaders; + + auto AddShaderStage = [&ShaderStages, &UniqueShaders](IShader* pShader) { + if (pShader != nullptr && UniqueShaders.insert(pShader).second) + { + auto ShaderType = pShader->GetDesc().ShaderType; + auto StageInd = GetShaderTypePipelineIndex(ShaderType, PIPELINE_TYPE_RAY_TRACING); + auto& Stage = ShaderStages[StageInd]; + Stage.Append(ValidatedCast<ShaderImplType>(pShader)); + VERIFY_EXPR(Stage.Type == SHADER_TYPE_UNKNOWN || Stage.Type == ShaderType); + Stage.Type = ShaderType; + } + }; + + ShaderStages.clear(); + ShaderStages.resize(6); + + for (Uint32 i = 0; i < CreateInfo.GeneralShaderCount; ++i) + { + AddShaderStage(CreateInfo.pGeneralShaders[i].pShader); + } + for (Uint32 i = 0; i < CreateInfo.TriangleHitShaderCount; ++i) + { + AddShaderStage(CreateInfo.pTriangleHitShaders[i].pClosestHitShader); + AddShaderStage(CreateInfo.pTriangleHitShaders[i].pAnyHitShader); + } + for (Uint32 i = 0; i < CreateInfo.ProceduralHitShaderCount; ++i) + { + AddShaderStage(CreateInfo.pProceduralHitShaders[i].pIntersectionShader); + AddShaderStage(CreateInfo.pProceduralHitShaders[i].pClosestHitShader); + AddShaderStage(CreateInfo.pProceduralHitShaders[i].pAnyHitShader); + } + + if (ShaderStages[GetShaderTypePipelineIndex(SHADER_TYPE_RAY_GEN, PIPELINE_TYPE_RAY_TRACING)].Count() == 0) + LOG_ERROR_AND_THROW("At least one shader with type SHADER_TYPE_RAY_GEN must be provided"); + + if (ShaderStages[GetShaderTypePipelineIndex(SHADER_TYPE_RAY_MISS, PIPELINE_TYPE_RAY_TRACING)].Count() == 0) + LOG_ERROR_AND_THROW("At least one shader with type SHADER_TYPE_RAY_MISS must be provided"); + + // remove empty stages + for (auto iter = ShaderStages.begin(); iter != ShaderStages.end();) + { + if (iter->Count() == 0) + { + iter = ShaderStages.erase(iter); + continue; + } + + m_ShaderStageTypes[m_NumShaderStages++] = iter->Type; + ++iter; + } + + VERIFY_EXPR(!ShaderStages.empty() && ShaderStages.size() == m_NumShaderStages); + } + void InitializePipelineDesc(const GraphicsPipelineStateCreateInfo& CreateInfo, - LinearAllocator& MemPool) + FixedLinearAllocator& MemPool) { this->m_pGraphicsPipelineDesc = MemPool.Copy(CreateInfo.GraphicsPipeline); @@ -352,7 +521,7 @@ protected: } const auto& InputLayout = GraphicsPipeline.InputLayout; - LayoutElement* pLayoutElements = MemPool.Allocate<LayoutElement>(InputLayout.NumElements); + LayoutElement* pLayoutElements = MemPool.ConstructArray<LayoutElement>(InputLayout.NumElements); for (size_t Elem = 0; Elem < InputLayout.NumElements; ++Elem) { const auto& SrcElem = InputLayout.LayoutElements[Elem]; @@ -432,7 +601,7 @@ protected: LayoutElem.Stride = Strides[BuffSlot]; } - m_pStrides = MemPool.Allocate<Uint32>(m_BufferSlotsUsed); + m_pStrides = MemPool.ConstructArray<Uint32>(m_BufferSlotsUsed); // Set strides for all unused slots to 0 for (Uint32 i = 0; i < m_BufferSlotsUsed; ++i) @@ -443,13 +612,37 @@ protected: } void InitializePipelineDesc(const ComputePipelineStateCreateInfo& CreateInfo, - LinearAllocator& MemPool) + FixedLinearAllocator& MemPool) { CopyResourceLayout(CreateInfo.PSODesc.ResourceLayout, this->m_Desc.ResourceLayout, MemPool); } + void InitializePipelineDesc(const RayTracingPipelineStateCreateInfo& CreateInfo, + FixedLinearAllocator& MemPool) noexcept + { + TNameToGroupIndexMap NameToGroupIndex; + CopyRayTracingShaderGroups(NameToGroupIndex, CreateInfo, MemPool); + + CopyResourceLayout(CreateInfo.PSODesc.ResourceLayout, this->m_Desc.ResourceLayout, MemPool); + + size_t RTDataSize = sizeof(RayTracingPipelineData); + // reserve size for shader handles + const auto ShaderHandleSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + const auto ShaderDataSize = ShaderHandleSize * (CreateInfo.GeneralShaderCount + CreateInfo.TriangleHitShaderCount + CreateInfo.ProceduralHitShaderCount); + RTDataSize += ShaderDataSize; + // Extra bytes are reserved to avoid compiler errors on zero-sized arrays + RTDataSize -= sizeof(RayTracingPipelineData::Shaders); + + this->m_pRayTracingPipelineData = static_cast<RayTracingPipelineData*>(MemPool.Allocate(RTDataSize, alignof(RayTracingPipelineData))); + new (this->m_pRayTracingPipelineData) RayTracingPipelineData{}; + this->m_pRayTracingPipelineData->ShaderHandleSize = ShaderHandleSize; + this->m_pRayTracingPipelineData->Desc = CreateInfo.RayTracingPipeline; + this->m_pRayTracingPipelineData->ShaderDataSize = ShaderDataSize; + this->m_pRayTracingPipelineData->NameToGroupIndex = std::move(NameToGroupIndex); + } + private: - static void ReserveResourceLayout(const PipelineResourceLayoutDesc& SrcLayout, LinearAllocator& MemPool) noexcept + static void ReserveResourceLayout(const PipelineResourceLayoutDesc& SrcLayout, FixedLinearAllocator& MemPool) noexcept { if (SrcLayout.Variables != nullptr) { @@ -470,13 +663,16 @@ private: MemPool.AddSpaceForString(SrcLayout.ImmutableSamplers[i].SamplerOrTextureName); } } + + static_assert(std::is_trivially_destructible<decltype(*SrcLayout.Variables)>::value, "Add destructor for this object to Destruct()"); + static_assert(std::is_trivially_destructible<decltype(*SrcLayout.ImmutableSamplers)>::value, "Add destructor for this object to Destruct()"); } - static void CopyResourceLayout(const PipelineResourceLayoutDesc& SrcLayout, PipelineResourceLayoutDesc& DstLayout, LinearAllocator& MemPool) + static void CopyResourceLayout(const PipelineResourceLayoutDesc& SrcLayout, PipelineResourceLayoutDesc& DstLayout, FixedLinearAllocator& MemPool) { if (SrcLayout.Variables != nullptr) { - auto* Variables = MemPool.Allocate<ShaderResourceVariableDesc>(SrcLayout.NumVariables); + auto* Variables = MemPool.ConstructArray<ShaderResourceVariableDesc>(SrcLayout.NumVariables); DstLayout.Variables = Variables; for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) { @@ -488,7 +684,7 @@ private: if (SrcLayout.ImmutableSamplers != nullptr) { - auto* ImmutableSamplers = MemPool.Allocate<ImmutableSamplerDesc>(SrcLayout.NumImmutableSamplers); + auto* ImmutableSamplers = MemPool.ConstructArray<ImmutableSamplerDesc>(SrcLayout.NumImmutableSamplers); DstLayout.ImmutableSamplers = ImmutableSamplers; for (Uint32 i = 0; i < SrcLayout.NumImmutableSamplers; ++i) { @@ -526,7 +722,27 @@ protected: RefCntAutoPtr<IRenderPass> m_pRenderPass; ///< Strong reference to the render pass object - GraphicsPipelineDesc* m_pGraphicsPipelineDesc = nullptr; + struct RayTracingPipelineData + { + RayTracingPipelineDesc Desc; + TNameToGroupIndexMap NameToGroupIndex; + + Uint32 ShaderHandleSize = 0; + Uint32 ShaderDataSize = 0; + + Uint8 Shaders[sizeof(void*)] = {}; // The actual array size will be ShaderDataSize + }; + static_assert(offsetof(RayTracingPipelineData, Shaders) % sizeof(void*) == 0, "Shaders member is expected to be sizeof(void*)-aligned"); + + union + { + GraphicsPipelineDesc* m_pGraphicsPipelineDesc = nullptr; + RayTracingPipelineData* m_pRayTracingPipelineData; + }; + +#ifdef DILIGENT_DEBUG + bool m_IsDestructed = false; +#endif }; } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/RenderDeviceBase.hpp b/Graphics/GraphicsEngine/include/RenderDeviceBase.hpp index f406bf4e..cff25a92 100644 --- a/Graphics/GraphicsEngine/include/RenderDeviceBase.hpp +++ b/Graphics/GraphicsEngine/include/RenderDeviceBase.hpp @@ -226,6 +226,15 @@ public: /// Size of the framebuffer object (FramebufferD3D12Impl, FramebufferVkImpl, etc.), in bytes const size_t FramebufferObjSize; + + /// Size of the BLAS object (BottomLevelASD3D12Impl, BottomLevelASVkImpl, etc.), in bytes + const size_t BLASObjSize; + + /// Size of the TLAS object (TopLevelASD3D12Impl, TopLevelASVkImpl, etc.), in bytes + const size_t TLASObjSize; + + /// Size of the SBT object (ShaderBindingTableD3D12Impl, ShaderBindingtableVkImpl, etc.), in bytes + const size_t SBTObjSize; }; /// \param pRefCounters - reference counters object that controls the lifetime of this render device @@ -261,7 +270,11 @@ public: m_FenceAllocator {RawMemAllocator, ObjectSizes.FenceSize, 16 }, m_QueryAllocator {RawMemAllocator, ObjectSizes.QuerySize, 16 }, m_RenderPassAllocator {RawMemAllocator, ObjectSizes.RenderPassObjSize, 16 }, - m_FramebufferAllocator {RawMemAllocator, ObjectSizes.FramebufferObjSize, 16 } + m_FramebufferAllocator {RawMemAllocator, ObjectSizes.FramebufferObjSize, 16 }, + m_BLASAllocator {RawMemAllocator, ObjectSizes.BLASObjSize, 16 }, + m_TLASAllocator {RawMemAllocator, ObjectSizes.TLASObjSize, 16 }, + m_SBTAllocator {RawMemAllocator, ObjectSizes.SBTObjSize, 16 }, + m_DeviceProperties {} // clang-format on { // Initialize texture format info @@ -335,6 +348,12 @@ public: return m_DeviceCaps; } + /// Implementation of IRenderDevice::GetDeviceProperties(). + virtual const DeviceProperties& DILIGENT_CALL_TYPE GetDeviceProperties() const override final + { + return m_DeviceProperties; + } + /// Implementation of IRenderDevice::GetTextureFormatInfo(). virtual const TextureFormatInfo& DILIGENT_CALL_TYPE GetTextureFormatInfo(TEXTURE_FORMAT TexFormat) override final { @@ -406,7 +425,8 @@ protected: RefCntAutoPtr<IEngineFactory> m_pEngineFactory; - DeviceCaps m_DeviceCaps; + DeviceCaps m_DeviceCaps; + DeviceProperties m_DeviceProperties; // All state object registries hold raw pointers. // This is safe because every object unregisters itself @@ -436,6 +456,9 @@ protected: FixedBlockMemoryAllocator m_QueryAllocator; ///< Allocator for query objects FixedBlockMemoryAllocator m_RenderPassAllocator; ///< Allocator for render pass objects FixedBlockMemoryAllocator m_FramebufferAllocator; ///< Allocator for framebuffer objects + FixedBlockMemoryAllocator m_BLASAllocator; ///< Allocator for bottom-level acceleration structure objects + FixedBlockMemoryAllocator m_TLASAllocator; ///< Allocator for top-level acceleration structure objects + FixedBlockMemoryAllocator m_SBTAllocator; ///< Allocator for shader binding table objects }; diff --git a/Graphics/GraphicsEngine/include/RenderPassBase.hpp b/Graphics/GraphicsEngine/include/RenderPassBase.hpp index fbc65dd7..627c2094 100644 --- a/Graphics/GraphicsEngine/include/RenderPassBase.hpp +++ b/Graphics/GraphicsEngine/include/RenderPassBase.hpp @@ -40,7 +40,7 @@ namespace Diligent { -void ValidateRenderPassDesc(const RenderPassDesc& Desc); +void ValidateRenderPassDesc(const RenderPassDesc& Desc) noexcept(false); template <typename RenderDeviceImplType> void _CorrectAttachmentState(RESOURCE_STATE& State) {} @@ -81,7 +81,7 @@ public: bool bIsDeviceInternal = false) : TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} { - ValidateRenderPassDesc(Desc); + ValidateRenderPassDesc(this->m_Desc); if (Desc.AttachmentCount != 0) { diff --git a/Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp b/Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp index 144ecdd5..ad70ae9d 100644 --- a/Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp +++ b/Graphics/GraphicsEngine/include/ResourceMappingImpl.hpp @@ -30,68 +30,17 @@ /// \file /// Declaration of the Diligent::ResourceMappingImpl class +#include <unordered_map> + #include "ResourceMapping.h" #include "ObjectBase.hpp" -#include <unordered_map> #include "HashUtils.hpp" #include "STDAllocator.hpp" +#include "RefCntAutoPtr.hpp" namespace Diligent { -struct ResMappingHashKey -{ - ResMappingHashKey(const Char* Str, bool bMakeCopy, Uint32 ArrInd) : - StrKey{Str, bMakeCopy}, - ArrayIndex{ArrInd} - { - } - - ResMappingHashKey(ResMappingHashKey&& rhs) : - StrKey{std::move(rhs.StrKey)}, - ArrayIndex{rhs.ArrayIndex} - {} - - bool operator==(const ResMappingHashKey& RHS) const - { - return StrKey == RHS.StrKey && ArrayIndex == RHS.ArrayIndex; - } - - size_t GetHash() const - { - if (Hash == 0) - { - Hash = ComputeHash(StrKey.GetHash(), ArrayIndex); - } - - return Hash; - } - - // clang-format off - ResMappingHashKey ( const ResMappingHashKey& ) = delete; - ResMappingHashKey& operator = ( const ResMappingHashKey& ) = delete; - ResMappingHashKey& operator = ( ResMappingHashKey&& ) = delete; - // clang-format on - - HashMapStringKey StrKey; - Uint32 ArrayIndex; - mutable size_t Hash = 0; -}; -} // namespace Diligent - -namespace std -{ -template <> -struct hash<Diligent::ResMappingHashKey> -{ - size_t operator()(const Diligent::ResMappingHashKey& Key) const - { - return Key.GetHash(); - } -}; -} // namespace std -namespace Diligent -{ class FixedBlockMemoryAllocator; /// Implementation of the resource mapping @@ -103,13 +52,13 @@ public: /// \param pRefCounters - reference counters object that controls the lifetime of this resource mapping /// \param RawMemAllocator - raw memory allocator that is used by the m_HashTable member ResourceMappingImpl(IReferenceCounters* pRefCounters, IMemoryAllocator& RawMemAllocator) : - TObjectBase(pRefCounters), - m_HashTable(STD_ALLOCATOR_RAW_MEM(HashTableElem, RawMemAllocator, "Allocator for unordered_map< ResMappingHashKey, RefCntAutoPtr<IDeviceObject> >")) + TObjectBase{pRefCounters}, + m_HashTable{STD_ALLOCATOR_RAW_MEM(HashTableElem, RawMemAllocator, "Allocator for unordered_map<ResMappingHashKey, RefCntAutoPtr<IDeviceObject>>")} {} ~ResourceMappingImpl(); - virtual void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final; + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_ResourceMapping, TObjectBase) /// Implementation of IResourceMapping::AddResource() virtual void DILIGENT_CALL_TYPE AddResource(const Char* Name, @@ -135,10 +84,47 @@ public: virtual size_t DILIGENT_CALL_TYPE GetSize() override final; private: + struct ResMappingHashKey : public HashMapStringKey + { + using TBase = HashMapStringKey; + + ResMappingHashKey(const Char* Str, bool bMakeCopy, Uint32 ArrInd) : + HashMapStringKey{Str, bMakeCopy}, + ArrayIndex{ArrInd} + { + Ownership_Hash = (ComputeHash(GetHash(), ArrInd) & HashMask) | (Ownership_Hash & StrOwnershipMask); + } + + ResMappingHashKey(ResMappingHashKey&& rhs) : + HashMapStringKey{std::move(rhs)}, + ArrayIndex{rhs.ArrayIndex} + {} + + // clang-format off + ResMappingHashKey ( const ResMappingHashKey& ) = delete; + ResMappingHashKey& operator = ( const ResMappingHashKey& ) = delete; + ResMappingHashKey& operator = ( ResMappingHashKey&& ) = delete; + // clang-format on + + bool operator==(const ResMappingHashKey& RHS) const + { + return static_cast<const TBase&>(*this) == static_cast<const TBase&>(RHS) && ArrayIndex == RHS.ArrayIndex; + } + + const Uint32 ArrayIndex; + }; + ThreadingTools::LockHelper Lock(); - ThreadingTools::LockFlag m_LockFlag; - typedef std::pair<const ResMappingHashKey, RefCntAutoPtr<IDeviceObject>> HashTableElem; - std::unordered_map<ResMappingHashKey, RefCntAutoPtr<IDeviceObject>, std::hash<ResMappingHashKey>, std::equal_to<ResMappingHashKey>, STDAllocatorRawMem<HashTableElem>> m_HashTable; + ThreadingTools::LockFlag m_LockFlag; + + using HashTableElem = std::pair<const ResMappingHashKey, RefCntAutoPtr<IDeviceObject>>; + std::unordered_map<ResMappingHashKey, + RefCntAutoPtr<IDeviceObject>, + ResMappingHashKey::Hasher, + std::equal_to<ResMappingHashKey>, + STDAllocatorRawMem<HashTableElem>> + m_HashTable; }; + } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderBase.hpp b/Graphics/GraphicsEngine/include/ShaderBase.hpp index 24ad92ee..8b6e9efa 100644 --- a/Graphics/GraphicsEngine/include/ShaderBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderBase.hpp @@ -79,6 +79,9 @@ public: if ((ShdrDesc.ShaderType == SHADER_TYPE_AMPLIFICATION || ShdrDesc.ShaderType == SHADER_TYPE_MESH) && !deviceFeatures.MeshShaders) LOG_ERROR_AND_THROW("Mesh shaders are not supported by this device"); + + if ((ShdrDesc.ShaderType >= SHADER_TYPE_RAY_GEN && ShdrDesc.ShaderType <= SHADER_TYPE_CALLABLE) && !deviceFeatures.RayTracing) + LOG_ERROR_AND_THROW("Ray tracing shaders are not supported by this device"); } IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_Shader, TDeviceObjectBase) diff --git a/Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp b/Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp new file mode 100644 index 00000000..340d8821 --- /dev/null +++ b/Graphics/GraphicsEngine/include/ShaderBindingTableBase.hpp @@ -0,0 +1,548 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::ShaderBindingTableBase template class + +#include <unordered_map> + +#include "ShaderBindingTable.h" +#include "TopLevelASBase.hpp" +#include "DeviceObjectBase.hpp" +#include "RenderDeviceBase.hpp" +#include "StringPool.hpp" +#include "HashUtils.hpp" + +namespace Diligent +{ + +/// Validates SBT description and throws an exception in case of an error. +void ValidateShaderBindingTableDesc(const ShaderBindingTableDesc& Desc, Uint32 ShaderGroupHandleSize, Uint32 MaxShaderRecordStride) noexcept(false); + +/// Template class implementing base functionality for a shader binding table object. + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::IShaderBindingTableD3D12 or Diligent::IShaderBindingTableVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D12Impl or Diligent::RenderDeviceVkImpl) +template <class BaseInterface, class PipelineStateImplType, class TopLevelASImplType, class RenderDeviceImplType> +class ShaderBindingTableBase : public DeviceObjectBase<BaseInterface, RenderDeviceImplType, ShaderBindingTableDesc> +{ +public: + using TDeviceObjectBase = DeviceObjectBase<BaseInterface, RenderDeviceImplType, ShaderBindingTableDesc>; + + /// \param pRefCounters - reference counters object that controls the lifetime of this SBT. + /// \param pDevice - pointer to the device. + /// \param Desc - SBT description. + /// \param bIsDeviceInternal - flag indicating if the BLAS is an internal device object and + /// must not keep a strong reference to the device. + ShaderBindingTableBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const ShaderBindingTableDesc& Desc, + bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} + { + const auto& DeviceProps = this->m_pDevice->GetProperties(); + ValidateShaderBindingTableDesc(this->m_Desc, DeviceProps.ShaderGroupHandleSize, DeviceProps.MaxShaderRecordStride); + + 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; + } + + ~ShaderBindingTableBase() + { + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_ShaderBindingTable, TDeviceObjectBase) + + + 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; + + this->m_Desc.pPSO = pPSO; + + const auto& DeviceProps = this->m_pDevice->GetProperties(); + try + { + ValidateShaderBindingTableDesc(this->m_Desc, DeviceProps.ShaderGroupHandleSize, DeviceProps.MaxShaderRecordStride); + } + catch (const std::runtime_error&) + { + return; + } + + 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 ResetHitGroups() override final + { +#ifdef DILIGENT_DEVELOPMENT + this->m_DbgHitGroupBindings.clear(); +#endif + this->m_HitGroupsRecord.clear(); + this->m_Changed = true; + } + + + 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(pShaderGroupName, this->m_RayGenShaderRecord.data(), this->m_ShaderRecordStride); + + const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + std::memcpy(this->m_RayGenShaderRecord.data() + GroupSize, pData, DataSize); + this->m_Changed = true; + } + + + void DILIGENT_CALL_TYPE BindMissShader(const char* pShaderGroupName, Uint32 MissIndex, const void* pData, Uint32 DataSize) override final + { + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); + + const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + 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(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 BindHitGroupByIndex(Uint32 BindingIndex, + const char* pShaderGroupName, + const void* pData, + Uint32 DataSize) override final + { + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); + + const size_t Stride = this->m_ShaderRecordStride; + const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + const size_t Offset = BindingIndex * Stride; + + this->m_HitGroupsRecord.resize(std::max(this->m_HitGroupsRecord.size(), Offset + Stride), Uint8{EmptyElem}); + + 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(nullptr, BindingIndex); +#endif + } + + + void DILIGENT_CALL_TYPE BindHitGroup(ITopLevelAS* pTLAS, + const char* pInstanceName, + const char* pGeometryName, + 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* const pTLASImpl = ValidatedCast<TopLevelASImplType>(pTLAS); + const auto Info = pTLASImpl->GetBuildInfo(); + const auto Desc = pTLASImpl->GetInstanceDesc(pInstanceName); + + VERIFY_EXPR(Info.BindingMode == HIT_GROUP_BINDING_MODE_PER_GEOMETRY); + VERIFY_EXPR(RayOffsetInHitGroupIndex < Info.HitGroupStride); + VERIFY_EXPR(Desc.ContributionToHitGroupIndex != ~0u); + VERIFY_EXPR(Desc.pBLAS != nullptr); + + const Uint32 InstanceOffset = Desc.ContributionToHitGroupIndex; + const Uint32 GeometryIndex = Desc.pBLAS->GetGeometryIndex(pGeometryName); + VERIFY_EXPR(GeometryIndex != INVALID_INDEX); + + const Uint32 Index = InstanceOffset + GeometryIndex * Info.HitGroupStride + 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(), Offset + Stride), Uint8{EmptyElem}); + + 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 + VERIFY_EXPR(Index >= Info.FirstContributionToHitGroupIndex && Index <= Info.LastContributionToHitGroupIndex); + OnBindHitGroup(pTLASImpl, Index); +#endif + } + + + void DILIGENT_CALL_TYPE BindHitGroups(ITopLevelAS* pTLAS, + const char* pInstanceName, + 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* const pTLASImpl = ValidatedCast<TopLevelASImplType>(pTLAS); + const auto Info = pTLASImpl->GetBuildInfo(); + const auto Desc = pTLASImpl->GetInstanceDesc(pInstanceName); + + VERIFY_EXPR(Info.BindingMode == HIT_GROUP_BINDING_MODE_PER_GEOMETRY || + Info.BindingMode == HIT_GROUP_BINDING_MODE_PER_INSTANCE); + VERIFY_EXPR(RayOffsetInHitGroupIndex < Info.HitGroupStride); + VERIFY_EXPR(Desc.ContributionToHitGroupIndex != INVALID_INDEX); + VERIFY_EXPR(Desc.pBLAS != nullptr); + + const Uint32 InstanceOffset = Desc.ContributionToHitGroupIndex; + Uint32 GeometryCount = 0; + + switch (Info.BindingMode) + { + // clang-format off + case HIT_GROUP_BINDING_MODE_PER_GEOMETRY: GeometryCount = Desc.pBLAS->GetActualGeometryCount(); break; + case HIT_GROUP_BINDING_MODE_PER_INSTANCE: GeometryCount = 1; break; + default: UNEXPECTED("unknown binding mode"); + // clang-format on + } + + const Uint32 BeginIndex = InstanceOffset; + const size_t EndIndex = InstanceOffset + GeometryCount * Info.HitGroupStride; + 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(), EndIndex * Stride), Uint8{EmptyElem}); + this->m_Changed = true; + + for (Uint32 i = 0; i < GeometryCount; ++i) + { + Uint32 Index = BeginIndex + i * Info.HitGroupStride + RayOffsetInHitGroupIndex; + 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 + VERIFY_EXPR(Index >= Info.FirstContributionToHitGroupIndex && Index <= Info.LastContributionToHitGroupIndex); + OnBindHitGroup(pTLASImpl, BeginIndex + i); +#endif + } + } + + + 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); + const auto Info = pTLASImpl->GetBuildInfo(); + VERIFY_EXPR(Info.BindingMode == HIT_GROUP_BINDING_MODE_PER_GEOMETRY || + Info.BindingMode == HIT_GROUP_BINDING_MODE_PER_INSTANCE || + Info.BindingMode == HIT_GROUP_BINDING_MODE_PER_ACCEL_STRUCT); + VERIFY_EXPR(RayOffsetInHitGroupIndex < Info.HitGroupStride); + + 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(), (Info.LastContributionToHitGroupIndex + 1) * Stride), Uint8{EmptyElem}); + this->m_Changed = true; + + for (Uint32 Index = RayOffsetInHitGroupIndex + Info.FirstContributionToHitGroupIndex; + Index <= Info.LastContributionToHitGroupIndex; + Index += Info.HitGroupStride) + { + 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* pData, + Uint32 DataSize) override final + { + VERIFY_EXPR((pData == nullptr) == (DataSize == 0)); + VERIFY_EXPR((pData == nullptr) || (DataSize == this->m_ShaderRecordSize)); + + const Uint32 GroupSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + 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(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(SHADER_BINDING_VALIDATION_FLAGS Flags) const override final + { +#ifdef DILIGENT_DEVELOPMENT + static_assert(EmptyElem != 0, "must not be zero"); + + const auto Stride = this->m_ShaderRecordStride; + const auto ShSize = this->m_pDevice->GetProperties().ShaderGroupHandleSize; + const auto FindPattern = [&](const std::vector<Uint8>& Data, const char* GroupName) -> bool // + { + for (size_t i = 0; i < Data.size(); i += Stride) + { + if (Flags & SHADER_BINDING_VALIDATION_SHADER_ONLY) + { + 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; + } + } + + 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_INFO_MESSAGE("Shader binding table '", this->m_Desc.Name, "' is not valid: ray generation shader is not bound"); + return false; + } + + 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 (!Binding.IsBound) + { + LOG_INFO_MESSAGE("Shader binding table '", this->m_Desc.Name, "' is not valid: hit group at index (", i, ") is not bound"); + return false; + } + 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; + } + } + } + + 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; +#else + return true; + +#endif // DILIGENT_DEVELOPMENT + } + + + struct BindingTable + { + const void* pData = nullptr; + Uint32 Size = 0; + Uint32 Offset = 0; + Uint32 Stride = 0; + }; + void GetData(IBuffer*& pSBTBuffer, + BindingTable& RaygenShaderBindingTable, + BindingTable& MissShaderBindingTable, + BindingTable& HitShaderBindingTable, + BindingTable& CallableShaderBindingTable) + { + const auto ShaderGroupBaseAlignment = this->m_pDevice->GetProperties().ShaderGroupBaseAlignment; + + const auto AlignToLarger = [ShaderGroupBaseAlignment](size_t offset) -> Uint32 { + return Align(static_cast<Uint32>(offset), ShaderGroupBaseAlignment); + }; + + const Uint32 RayGenOffset = 0; + const Uint32 MissShaderOffset = AlignToLarger(m_RayGenShaderRecord.size()); + const Uint32 HitGroupOffset = AlignToLarger(MissShaderOffset + m_MissShadersRecord.size()); + const Uint32 CallableShadersOffset = AlignToLarger(HitGroupOffset + m_HitGroupsRecord.size()); + const Uint32 BufSize = AlignToLarger(CallableShadersOffset + m_CallableShadersRecord.size()); + + // recreate buffer + if (this->m_pBuffer == nullptr || this->m_pBuffer->GetDesc().uiSizeInBytes < BufSize) + { + this->m_pBuffer = nullptr; + + String BuffName = String{this->m_Desc.Name} + " - internal buffer"; + BufferDesc BuffDesc; + BuffDesc.Name = BuffName.c_str(); + BuffDesc.Usage = USAGE_DEFAULT; + BuffDesc.BindFlags = BIND_RAY_TRACING; + BuffDesc.uiSizeInBytes = BufSize; + + this->m_pDevice->CreateBuffer(BuffDesc, nullptr, &this->m_pBuffer); + VERIFY_EXPR(this->m_pBuffer != nullptr); + } + + if (this->m_pBuffer == nullptr) + return; // Something went wrong + + pSBTBuffer = this->m_pBuffer; + + if (m_RayGenShaderRecord.size()) + { + RaygenShaderBindingTable.pData = this->m_Changed ? m_RayGenShaderRecord.data() : nullptr; + RaygenShaderBindingTable.Offset = RayGenOffset; + RaygenShaderBindingTable.Size = static_cast<Uint32>(m_RayGenShaderRecord.size()); + RaygenShaderBindingTable.Stride = this->m_ShaderRecordStride; + } + + if (m_MissShadersRecord.size()) + { + MissShaderBindingTable.pData = this->m_Changed ? m_MissShadersRecord.data() : nullptr; + MissShaderBindingTable.Offset = MissShaderOffset; + MissShaderBindingTable.Size = static_cast<Uint32>(m_MissShadersRecord.size()); + MissShaderBindingTable.Stride = this->m_ShaderRecordStride; + } + + if (m_HitGroupsRecord.size()) + { + HitShaderBindingTable.pData = this->m_Changed ? m_HitGroupsRecord.data() : nullptr; + HitShaderBindingTable.Offset = HitGroupOffset; + HitShaderBindingTable.Size = static_cast<Uint32>(m_HitGroupsRecord.size()); + HitShaderBindingTable.Stride = this->m_ShaderRecordStride; + } + + if (m_CallableShadersRecord.size()) + { + CallableShaderBindingTable.pData = this->m_Changed ? m_CallableShadersRecord.data() : nullptr; + CallableShaderBindingTable.Offset = CallableShadersOffset; + CallableShaderBindingTable.Size = static_cast<Uint32>(m_CallableShadersRecord.size()); + CallableShaderBindingTable.Stride = this->m_ShaderRecordStride; + } + + if (!this->m_Changed) + return; + + this->m_Changed = false; + } + + +protected: + std::vector<Uint8> m_RayGenShaderRecord; + std::vector<Uint8> m_MissShadersRecord; + std::vector<Uint8> m_CallableShadersRecord; + std::vector<Uint8> m_HitGroupsRecord; + + RefCntAutoPtr<PipelineStateImplType> m_pPSO; + RefCntAutoPtr<IBuffer> m_pBuffer; + + Uint32 m_ShaderRecordSize = 0; + Uint32 m_ShaderRecordStride = 0; + bool m_Changed = true; + +#ifdef DILIGENT_DEVELOPMENT + static constexpr Uint8 EmptyElem = 0xA7; +#else + // In release mode clear uninitialized data by zeros. + // This makes shader inactive, wich hides errors but prevents crashes. + static constexpr Uint8 EmptyElem = 0; +#endif + +private: +#ifdef DILIGENT_DEVELOPMENT + struct HitGroupBinding + { + RefCntWeakPtr<TopLevelASImplType> pTLAS; + Uint32 Version = ~0u; + bool IsBound = false; + }; + mutable std::vector<HitGroupBinding> m_DbgHitGroupBindings; + + void OnBindHitGroup(TopLevelASImplType* pTLAS, size_t Index) + { + this->m_DbgHitGroupBindings.resize(std::max(this->m_DbgHitGroupBindings.size(), Index + 1)); + + auto& Binding = this->m_DbgHitGroupBindings[Index]; + Binding.pTLAS = pTLAS; + Binding.Version = pTLAS ? pTLAS->GetVersion() : ~0u; + Binding.IsBound = true; + } +#endif +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp index f9adfe40..a8725fd0 100644 --- a/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderResourceBindingBase.hpp @@ -128,7 +128,7 @@ protected: return ResLayoutInd; } - Int8 GetVariableByIndexHelper(SHADER_TYPE ShaderType, Uint32 Index, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& ResourceLayoutIndex) + Int8 GetVariableByIndexHelper(SHADER_TYPE ShaderType, Uint32 Index, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& ResourceLayoutIndex) const { const auto PipelineType = m_pPSO->GetDesc().PipelineType; if (!IsConsistentShaderType(ShaderType, PipelineType)) diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp index 1130fbff..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) @@ -381,6 +433,28 @@ inline void VerifyAndCorrectSetArrayArguments(const char* Name, Uint32 ArraySize } } +template <typename ShaderVectorType> +std::string GetShaderGroupName(const ShaderVectorType& Shaders) +{ + std::string Name; + if (Shaders.size() == 1) + { + Name = Shaders[0]->GetDesc().Name; + } + else + { + Name = "{"; + for (size_t s = 0; s < Shaders.size(); ++s) + { + if (s > 0) + Name += ", "; + Name += Shaders[s]->GetDesc().Name; + } + Name += "}"; + } + return Name; +} + struct DefaultShaderVariableIDComparator { bool operator()(const INTERFACE_ID& IID) const diff --git a/Graphics/GraphicsEngine/include/TextureBase.hpp b/Graphics/GraphicsEngine/include/TextureBase.hpp index 559df1ea..ddd1a378 100644 --- a/Graphics/GraphicsEngine/include/TextureBase.hpp +++ b/Graphics/GraphicsEngine/include/TextureBase.hpp @@ -44,9 +44,19 @@ namespace Diligent struct CopyTextureAttribs; -void ValidateTextureDesc(const TextureDesc& TexDesc); +/// Validates texture description and throws an exception in case of an error. +void ValidateTextureDesc(const TextureDesc& TexDesc) noexcept(false); + +/// Validates and corrects texture view description; throws an exception in case of an error. +void ValidatedAndCorrectTextureViewDesc(const TextureDesc& TexDesc, TextureViewDesc& ViewDesc) noexcept(false); + +/// Validates update texture command paramters. void ValidateUpdateTextureParams(const TextureDesc& TexDesc, Uint32 MipLevel, Uint32 Slice, const Box& DstBox, const TextureSubResData& SubresData); + +/// Validates copy texture command paramters. void ValidateCopyTextureParams(const CopyTextureAttribs& CopyAttribs); + +/// Validates map texture command paramters. void ValidateMapTextureParams(const TextureDesc& TexDesc, Uint32 MipLevel, Uint32 ArraySlice, @@ -88,10 +98,10 @@ public: #ifdef DILIGENT_DEBUG m_dbgTexViewObjAllocator(TexViewObjAllocator), #endif - m_pDefaultSRV(nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)), - m_pDefaultRTV(nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)), - m_pDefaultDSV(nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)), - m_pDefaultUAV(nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)) + m_pDefaultSRV{nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)}, + m_pDefaultRTV{nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)}, + m_pDefaultDSV{nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)}, + m_pDefaultUAV{nullptr, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>(TexViewObjAllocator)} { if (this->m_Desc.MipLevels == 0) { @@ -161,7 +171,78 @@ public: /// - Creates default unordered access view addressing the entire texture if Diligent::BIND_UNORDERED_ACCESS flag is set. /// /// The function calls CreateViewInternal(). - void CreateDefaultViews(); + void CreateDefaultViews() + { + const auto& TexFmtAttribs = GetTextureFormatAttribs(this->m_Desc.Format); + if (TexFmtAttribs.ComponentType == COMPONENT_TYPE_UNDEFINED) + { + // Cannot create default view for TYPELESS formats + return; + } + + auto CreateDefaultView = [&](TEXTURE_VIEW_TYPE ViewType) // + { + TextureViewDesc ViewDesc; + ViewDesc.ViewType = ViewType; + + std::string ViewName; + switch (ViewType) + { + case TEXTURE_VIEW_SHADER_RESOURCE: + if ((this->m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0) + ViewDesc.Flags |= TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION; + ViewName = "Default SRV of texture '"; + break; + + case TEXTURE_VIEW_RENDER_TARGET: + ViewName = "Default RTV of texture '"; + break; + + case TEXTURE_VIEW_DEPTH_STENCIL: + ViewName = "Default DSV of texture '"; + break; + + case TEXTURE_VIEW_UNORDERED_ACCESS: + ViewDesc.AccessFlags = UAV_ACCESS_FLAG_READ_WRITE; + + ViewName = "Default UAV of texture '"; + break; + + default: + UNEXPECTED("Unexpected texture type"); + } + ViewName += this->m_Desc.Name; + ViewName += '\''; + ViewDesc.Name = ViewName.c_str(); + + ITextureView* pView = nullptr; + CreateViewInternal(ViewDesc, &pView, true); + VERIFY(pView != nullptr, "Failed to create default view for texture '", this->m_Desc.Name, "'"); + VERIFY(pView->GetDesc().ViewType == ViewType, "Unexpected view type"); + + return static_cast<TTextureViewImpl*>(pView); + }; + + if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE) + { + m_pDefaultSRV.reset(CreateDefaultView(TEXTURE_VIEW_SHADER_RESOURCE)); + } + + if (this->m_Desc.BindFlags & BIND_RENDER_TARGET) + { + m_pDefaultRTV.reset(CreateDefaultView(TEXTURE_VIEW_RENDER_TARGET)); + } + + if (this->m_Desc.BindFlags & BIND_DEPTH_STENCIL) + { + m_pDefaultDSV.reset(CreateDefaultView(TEXTURE_VIEW_DEPTH_STENCIL)); + } + + if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) + { + m_pDefaultUAV.reset(CreateDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS)); + } + } virtual void DILIGENT_CALL_TYPE SetState(RESOURCE_STATE State) override final { @@ -223,303 +304,7 @@ protected: /// Default UAV addressing the entire texture std::unique_ptr<TTextureViewImpl, STDDeleter<TTextureViewImpl, TTexViewObjAllocator>> m_pDefaultUAV; - void CorrectTextureViewDesc(struct TextureViewDesc& ViewDesc); - RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; }; - -template <class BaseInterface, class TRenderDeviceImpl, class TTextureViewImpl, class TTexViewObjAllocator> -void TextureBase<BaseInterface, TRenderDeviceImpl, TTextureViewImpl, TTexViewObjAllocator>::CorrectTextureViewDesc(struct TextureViewDesc& ViewDesc) -{ -#define TEX_VIEW_VALIDATION_ERROR(...) LOG_ERROR_AND_THROW("\n Failed to create texture view '", (ViewDesc.Name ? ViewDesc.Name : ""), "' for texture '", this->m_Desc.Name, "': ", ##__VA_ARGS__) - - if (!(ViewDesc.ViewType > TEXTURE_VIEW_UNDEFINED && ViewDesc.ViewType < TEXTURE_VIEW_NUM_VIEWS)) - TEX_VIEW_VALIDATION_ERROR("Texture view type is not specified"); - - if (ViewDesc.MostDetailedMip >= this->m_Desc.MipLevels) - TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") is out of range. The texture has only ", this->m_Desc.MipLevels, " mip ", (this->m_Desc.MipLevels > 1 ? "levels." : "level.")); - - if (ViewDesc.NumMipLevels != REMAINING_MIP_LEVELS && ViewDesc.MostDetailedMip + ViewDesc.NumMipLevels > this->m_Desc.MipLevels) - TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") and number of mip levels in the view (", ViewDesc.NumMipLevels, ") is out of range. The texture has only ", this->m_Desc.MipLevels, " mip ", (this->m_Desc.MipLevels > 1 ? "levels." : "level.")); - - if (ViewDesc.Format == TEX_FORMAT_UNKNOWN) - ViewDesc.Format = GetDefaultTextureViewFormat(this->m_Desc.Format, ViewDesc.ViewType, this->m_Desc.BindFlags); - - if (ViewDesc.TextureDim == RESOURCE_DIM_UNDEFINED) - { - if (this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE || this->m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) - { - switch (ViewDesc.ViewType) - { - case TEXTURE_VIEW_SHADER_RESOURCE: - ViewDesc.TextureDim = this->m_Desc.Type; - break; - - case TEXTURE_VIEW_RENDER_TARGET: - case TEXTURE_VIEW_DEPTH_STENCIL: - case TEXTURE_VIEW_UNORDERED_ACCESS: - ViewDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; - break; - - default: UNEXPECTED("Unexpected view type"); - } - } - else - { - ViewDesc.TextureDim = this->m_Desc.Type; - } - } - - switch (this->m_Desc.Type) - { - case RESOURCE_DIM_TEX_1D: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 1D view: only Texture 1D is allowed"); - } - break; - - case RESOURCE_DIM_TEX_1D_ARRAY: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect view type for Texture 1D Array: only Texture 1D or Texture 1D Array are allowed"); - } - break; - - case RESOURCE_DIM_TEX_2D: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D view: only Texture 2D or Texture 2D Array are allowed"); - } - break; - - case RESOURCE_DIM_TEX_2D_ARRAY: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D Array view: only Texture 2D or Texture 2D Array are allowed"); - } - break; - - case RESOURCE_DIM_TEX_3D: - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_3D) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 3D view: only Texture 3D is allowed"); - } - break; - - case RESOURCE_DIM_TEX_CUBE: - if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube SRV: Texture 2D, Texture 2D array or Texture Cube is allowed"); - } - } - else - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube non-shader resource view: Texture 2D or Texture 2D array is allowed"); - } - } - break; - - case RESOURCE_DIM_TEX_CUBE_ARRAY: - if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE_ARRAY) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array SRV: Texture 2D, Texture 2D array, Texture Cube or Texture Cube Array is allowed"); - } - } - else - { - if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && - ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) - { - TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array non-shader resource view: Texture 2D or Texture 2D array is allowed"); - } - } - break; - - default: - UNEXPECTED("Unexpected texture type"); - break; - } - - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE) - { - if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) - TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); - if (ViewDesc.NumArraySlices != 6 && ViewDesc.NumArraySlices != 0 && ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES) - TEX_VIEW_VALIDATION_ERROR("Texture cube SRV is expected to have 6 array slices, while ", ViewDesc.NumArraySlices, " is provided"); - if (ViewDesc.FirstArraySlice != 0) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture cube SRV"); - } - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) - { - if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) - TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); - if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && (ViewDesc.NumArraySlices % 6) != 0) - TEX_VIEW_VALIDATION_ERROR("Number of slices in texture cube array SRV is expected to be multiple of 6. ", ViewDesc.NumArraySlices, " slices is provided."); - } - - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D) - { - if (ViewDesc.FirstArraySlice != 0) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture 1D/2D views"); - - if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.NumArraySlices > 1) - TEX_VIEW_VALIDATION_ERROR("Number of slices in the view (", ViewDesc.NumArraySlices, ") must be 1 (or 0) for non-array texture 1D/2D views"); - } - else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) - { - if (ViewDesc.FirstArraySlice >= this->m_Desc.ArraySize) - TEX_VIEW_VALIDATION_ERROR("First array slice (", ViewDesc.FirstArraySlice, ") exceeds the number of slices in the texture array (", this->m_Desc.ArraySize, ")"); - - if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.FirstArraySlice + ViewDesc.NumArraySlices > this->m_Desc.ArraySize) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") and number of slices in the view (", ViewDesc.NumArraySlices, ") specify more slices than target texture has (", this->m_Desc.ArraySize, ")"); - } - else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) - { - auto MipDepth = this->m_Desc.Depth >> ViewDesc.MostDetailedMip; - if (ViewDesc.FirstDepthSlice + ViewDesc.NumDepthSlices > MipDepth) - TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstDepthSlice, ") and number of slices in the view (", ViewDesc.NumDepthSlices, ") specify more slices than target 3D texture mip level has (", MipDepth, ")"); - } - else - { - UNEXPECTED("Unexpected texture dimension"); - } - - if (GetTextureFormatAttribs(ViewDesc.Format).IsTypeless) - { - TEX_VIEW_VALIDATION_ERROR("Texture view format (", GetTextureFormatAttribs(ViewDesc.Format).Name, ") cannot be typeless"); - } - - if ((ViewDesc.Flags & TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION) != 0) - { - if ((this->m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) == 0) - TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only set if the texture was created with MISC_TEXTURE_FLAG_GENERATE_MIPS flag"); - - if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) - TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only be used with TEXTURE_VIEW_SHADER_RESOURCE view type"); - } - -#undef TEX_VIEW_VALIDATION_ERROR - - if (ViewDesc.NumMipLevels == 0 || ViewDesc.NumMipLevels == REMAINING_MIP_LEVELS) - { - if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) - ViewDesc.NumMipLevels = this->m_Desc.MipLevels - ViewDesc.MostDetailedMip; - else - ViewDesc.NumMipLevels = 1; - } - - if (ViewDesc.NumArraySlices == 0 || ViewDesc.NumArraySlices == REMAINING_ARRAY_SLICES) - { - if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || - ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) - ViewDesc.NumArraySlices = this->m_Desc.ArraySize - ViewDesc.FirstArraySlice; - else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) - { - auto MipDepth = this->m_Desc.Depth >> ViewDesc.MostDetailedMip; - ViewDesc.NumDepthSlices = MipDepth - ViewDesc.FirstDepthSlice; - } - else - ViewDesc.NumArraySlices = 1; - } - - if ((ViewDesc.ViewType == TEXTURE_VIEW_RENDER_TARGET) && - (ViewDesc.Format == TEX_FORMAT_R8_SNORM || ViewDesc.Format == TEX_FORMAT_RG8_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA8_SNORM || - ViewDesc.Format == TEX_FORMAT_R16_SNORM || ViewDesc.Format == TEX_FORMAT_RG16_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA16_SNORM)) - { - const auto* FmtName = GetTextureFormatAttribs(ViewDesc.Format).Name; - LOG_WARNING_MESSAGE(FmtName, " render target view is created.\n" - "There might be an issue in OpenGL driver on NVidia hardware: when rendering to SNORM textures, all negative values are clamped to zero.\n" - "Use UNORM format instead."); - } -} - -template <class BaseInterface, class TRenderDeviceImpl, class TTextureViewImpl, class TTexViewObjAllocator> -void TextureBase<BaseInterface, TRenderDeviceImpl, TTextureViewImpl, TTexViewObjAllocator>::CreateDefaultViews() -{ - const auto& TexFmtAttribs = GetTextureFormatAttribs(this->m_Desc.Format); - if (TexFmtAttribs.ComponentType == COMPONENT_TYPE_UNDEFINED) - { - // Cannot create default view for TYPELESS formats - return; - } - - if (this->m_Desc.BindFlags & BIND_SHADER_RESOURCE) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; - auto ViewName = FormatString("Default SRV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ITextureView* pSRV = nullptr; - if ((this->m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) != 0) - ViewDesc.Flags |= TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION; - CreateViewInternal(ViewDesc, &pSRV, true); - m_pDefaultSRV.reset(static_cast<TTextureViewImpl*>(pSRV)); - VERIFY(m_pDefaultSRV->GetDesc().ViewType == TEXTURE_VIEW_SHADER_RESOURCE, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_RENDER_TARGET) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_RENDER_TARGET; - auto ViewName = FormatString("Default RTV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ITextureView* pRTV = nullptr; - CreateViewInternal(ViewDesc, &pRTV, true); - m_pDefaultRTV.reset(static_cast<TTextureViewImpl*>(pRTV)); - VERIFY(m_pDefaultRTV->GetDesc().ViewType == TEXTURE_VIEW_RENDER_TARGET, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_DEPTH_STENCIL) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_DEPTH_STENCIL; - auto ViewName = FormatString("Default DSV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ITextureView* pDSV = nullptr; - CreateViewInternal(ViewDesc, &pDSV, true); - m_pDefaultDSV.reset(static_cast<TTextureViewImpl*>(pDSV)); - VERIFY(m_pDefaultDSV->GetDesc().ViewType == TEXTURE_VIEW_DEPTH_STENCIL, "Unexpected view type"); - } - - if (this->m_Desc.BindFlags & BIND_UNORDERED_ACCESS) - { - TextureViewDesc ViewDesc; - ViewDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; - auto ViewName = FormatString("Default UAV of texture '", this->m_Desc.Name, "'"); - ViewDesc.Name = ViewName.c_str(); - - ViewDesc.AccessFlags = UAV_ACCESS_FLAG_READ_WRITE; - ITextureView* pUAV = nullptr; - CreateViewInternal(ViewDesc, &pUAV, true); - m_pDefaultUAV.reset(static_cast<TTextureViewImpl*>(pUAV)); - VERIFY(m_pDefaultUAV->GetDesc().ViewType == TEXTURE_VIEW_UNORDERED_ACCESS, "Unexpected view type"); - } -} - } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/TopLevelASBase.hpp b/Graphics/GraphicsEngine/include/TopLevelASBase.hpp new file mode 100644 index 00000000..c1b73ebc --- /dev/null +++ b/Graphics/GraphicsEngine/include/TopLevelASBase.hpp @@ -0,0 +1,385 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// 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" +#include "HashUtils.hpp" + +namespace Diligent +{ + +/// Validates top-level AS description and throws an exception in case of an error. +void ValidateTopLevelASDesc(const TopLevelASDesc& Desc) noexcept(false); + +/// Template class implementing base functionality for a top-level acceleration structure object. + +/// \tparam BaseInterface - base interface that this class will inheret +/// (Diligent::ITopLevelASD3D12 or Diligent::ITopLevelASVk). +/// \tparam RenderDeviceImplType - type of the render device implementation +/// (Diligent::RenderDeviceD3D12Impl or Diligent::RenderDeviceVkImpl) +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>; + + /// \param pRefCounters - reference counters object that controls the lifetime of this BLAS. + /// \param pDevice - pointer to the device. + /// \param Desc - TLAS description. + /// \param bIsDeviceInternal - flag indicating if the BLAS is an internal device object and + /// must not keep a strong reference to the device. + TopLevelASBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const TopLevelASDesc& Desc, + bool bIsDeviceInternal = false) : + TDeviceObjectBase{pRefCounters, pDevice, Desc, bIsDeviceInternal} + { + ValidateTopLevelASDesc(this->m_Desc); + } + + ~TopLevelASBase() + { + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_TopLevelAS, TDeviceObjectBase) + + bool SetInstanceData(const TLASBuildInstanceData* pInstances, + const Uint32 InstanceCount, + const Uint32 BaseContributionToHitGroupIndex, + const Uint32 HitGroupStride, + const HIT_GROUP_BINDING_MODE BindingMode) noexcept + { + try + { + ClearInstanceData(); + + size_t StringPoolSize = 0; + for (Uint32 i = 0; i < InstanceCount; ++i) + { + VERIFY_EXPR(pInstances[i].InstanceName != nullptr); + StringPoolSize += StringPool::GetRequiredReserveSize(pInstances[i].InstanceName); + } + + this->m_StringPool.Reserve(StringPoolSize, GetRawAllocator()); + + Uint32 InstanceOffset = BaseContributionToHitGroupIndex; + + for (Uint32 i = 0; i < InstanceCount; ++i) + { + const auto& Inst = pInstances[i]; + const char* NameCopy = this->m_StringPool.CopyString(Inst.InstanceName); + InstanceDesc Desc = {}; + + Desc.pBLAS = ValidatedCast<BottomLevelASType>(Inst.pBLAS); + Desc.ContributionToHitGroupIndex = Inst.ContributionToHitGroupIndex; + Desc.InstanceIndex = i; + CalculateHitGroupIndex(Desc, InstanceOffset, HitGroupStride, BindingMode); + +#ifdef DILIGENT_DEVELOPMENT + Desc.Version = Desc.pBLAS->GetVersion(); +#endif + 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); + + InstanceOffset = InstanceOffset + (BindingMode == HIT_GROUP_BINDING_MODE_PER_ACCEL_STRUCT ? HitGroupStride : 0) - 1; + + this->m_BuildInfo.HitGroupStride = HitGroupStride; + this->m_BuildInfo.FirstContributionToHitGroupIndex = BaseContributionToHitGroupIndex; + this->m_BuildInfo.LastContributionToHitGroupIndex = InstanceOffset; + this->m_BuildInfo.BindingMode = BindingMode; + this->m_BuildInfo.InstanceCount = InstanceCount; + +#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 HitGroupStride, + const HIT_GROUP_BINDING_MODE BindingMode) noexcept + { + VERIFY_EXPR(this->m_BuildInfo.InstanceCount == InstanceCount); +#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 the previous build"); + return false; + } + + auto& Desc = Iter->second; + const auto PrevIndex = Desc.ContributionToHitGroupIndex; + const auto pPrevBLAS = Desc.pBLAS; + + Desc.pBLAS = ValidatedCast<BottomLevelASType>(Inst.pBLAS); + Desc.ContributionToHitGroupIndex = Inst.ContributionToHitGroupIndex; + //Desc.InstanceIndex = i; // keep Desc.InstanceIndex unmodified + CalculateHitGroupIndex(Desc, InstanceOffset, HitGroupStride, BindingMode); + +#ifdef DILIGENT_DEVELOPMENT + Changed = Changed || (pPrevBLAS != Desc.pBLAS); + Changed = Changed || (PrevIndex != Desc.ContributionToHitGroupIndex); + Desc.Version = Desc.pBLAS->GetVersion(); +#endif + } + + InstanceOffset = InstanceOffset + (BindingMode == HIT_GROUP_BINDING_MODE_PER_ACCEL_STRUCT ? HitGroupStride : 0) - 1; + +#ifdef DILIGENT_DEVELOPMENT + Changed = Changed || (this->m_BuildInfo.HitGroupStride != HitGroupStride); + Changed = Changed || (this->m_BuildInfo.FirstContributionToHitGroupIndex != BaseContributionToHitGroupIndex); + Changed = Changed || (this->m_BuildInfo.LastContributionToHitGroupIndex != InstanceOffset); + Changed = Changed || (this->m_BuildInfo.BindingMode != BindingMode); + if (Changed) + this->m_DbgVersion.fetch_add(1); +#endif + this->m_BuildInfo.HitGroupStride = HitGroupStride; + this->m_BuildInfo.FirstContributionToHitGroupIndex = BaseContributionToHitGroupIndex; + this->m_BuildInfo.LastContributionToHitGroupIndex = InstanceOffset; + this->m_BuildInfo.BindingMode = BindingMode; + + return true; + } + + void CopyInstancceData(const TopLevelASBase& Src) noexcept + { + ClearInstanceData(); + + this->m_StringPool.Reserve(Src.m_StringPool.GetReservedSize(), GetRawAllocator()); + this->m_BuildInfo = Src.m_BuildInfo; + + for (auto& SrcInst : Src.m_Instances) + { + const char* NameCopy = this->m_StringPool.CopyString(SrcInst.first.GetStr()); + this->m_Instances.emplace(NameCopy, SrcInst.second); + } + + VERIFY_EXPR(this->m_StringPool.GetRemainingSize() == 0); + +#ifdef DILIGENT_DEVELOPMENT + this->m_DbgVersion.fetch_add(1); +#endif + } + + virtual TLASInstanceDesc DILIGENT_CALL_TYPE GetInstanceDesc(const char* Name) const override final + { + VERIFY_EXPR(Name != nullptr && Name[0] != '\0'); + + TLASInstanceDesc Result = {}; + + auto Iter = this->m_Instances.find(Name); + if (Iter != this->m_Instances.end()) + { + const auto& Inst = Iter->second; + Result.ContributionToHitGroupIndex = Inst.ContributionToHitGroupIndex; + Result.InstanceIndex = Inst.InstanceIndex; + Result.pBLAS = Inst.pBLAS.template RawPtr<IBottomLevelAS>(); + } + else + { + Result.ContributionToHitGroupIndex = INVALID_INDEX; + Result.InstanceIndex = INVALID_INDEX; + LOG_ERROR_MESSAGE("Can't find instance with the specified name ('", Name, "')"); + } + + return Result; + } + + virtual TLASBuildInfo DILIGENT_CALL_TYPE GetBuildInfo() const override final + { + return m_BuildInfo; + } + + virtual void DILIGENT_CALL_TYPE SetState(RESOURCE_STATE State) override final + { + 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; + } + + virtual RESOURCE_STATE DILIGENT_CALL_TYPE GetState() const override final + { + return this->m_State; + } + + /// Implementation of ITopLevelAS::GetScratchBufferSizes(). + virtual ScratchBufferSizes DILIGENT_CALL_TYPE GetScratchBufferSizes() const override final + { + return this->m_ScratchSize; + } + + bool IsInKnownState() const + { + return this->m_State != RESOURCE_STATE_UNKNOWN; + } + + bool CheckState(RESOURCE_STATE State) const + { + VERIFY((State & (State - 1)) == 0, "Single state is expected"); + VERIFY(IsInKnownState(), "TLAS state is unknown"); + return (this->m_State & State) == State; + } + +#ifdef DILIGENT_DEVELOPMENT + bool ValidateContent() const + { + bool result = true; + + 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 : this->m_Instances) + { + const InstanceDesc& Inst = NameAndInst.second; + + if (Inst.Version != Inst.pBLAS->GetVersion()) + { + 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->IsInKnownState() && Inst.pBLAS->GetState() != RESOURCE_STATE_BUILD_AS_READ) + { + 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; + } + } + return result; + } + + Uint32 GetVersion() const + { + return this->m_DbgVersion.load(); + } +#endif // DILIGENT_DEVELOPMENT + +private: + void ClearInstanceData() + { + this->m_Instances.clear(); + this->m_StringPool.Clear(); + + this->m_BuildInfo.BindingMode = HIT_GROUP_BINDING_MODE_LAST; + this->m_BuildInfo.HitGroupStride = 0; + this->m_BuildInfo.FirstContributionToHitGroupIndex = INVALID_INDEX; + this->m_BuildInfo.LastContributionToHitGroupIndex = INVALID_INDEX; + } + + static void CalculateHitGroupIndex(InstanceDesc& Desc, Uint32& InstanceOffset, const Uint32 HitGroupStride, const HIT_GROUP_BINDING_MODE BindingMode) + { + static_assert(HIT_GROUP_BINDING_MODE_LAST == HIT_GROUP_BINDING_MODE_USER_DEFINED, "Please update the switch below to handle the new shader binding mode"); + + if (Desc.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO) + { + Desc.ContributionToHitGroupIndex = InstanceOffset; + switch (BindingMode) + { + // clang-format off + case HIT_GROUP_BINDING_MODE_PER_GEOMETRY: InstanceOffset += Desc.pBLAS->GetActualGeometryCount() * HitGroupStride; break; + case HIT_GROUP_BINDING_MODE_PER_INSTANCE: InstanceOffset += HitGroupStride; break; + case HIT_GROUP_BINDING_MODE_PER_ACCEL_STRUCT: /* InstanceOffset is a constant */ break; + case HIT_GROUP_BINDING_MODE_USER_DEFINED: UNEXPECTED("TLAS_INSTANCE_OFFSET_AUTO is not compatible with HIT_GROUP_BINDING_MODE_USER_DEFINED"); break; + default: UNEXPECTED("Unknown ray tracing shader binding mode"); + // clang-format on + } + } + else + { + VERIFY(BindingMode == HIT_GROUP_BINDING_MODE_USER_DEFINED, "BindingMode must be HIT_GROUP_BINDING_MODE_USER_DEFINED"); + } + + constexpr Uint32 MaxIndex = (1u << 24); + VERIFY(Desc.ContributionToHitGroupIndex < MaxIndex, "ContributionToHitGroupIndex must be less than ", MaxIndex); + } + +protected: + RESOURCE_STATE m_State = RESOURCE_STATE_UNKNOWN; + TLASBuildInfo m_BuildInfo; + ScratchBufferSizes m_ScratchSize; + + std::unordered_map<HashMapStringKey, InstanceDesc, HashMapStringKey::Hasher> m_Instances; + StringPool m_StringPool; + +#ifdef DILIGENT_DEVELOPMENT + std::atomic<Uint32> m_DbgVersion{0}; +#endif +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/interface/APIInfo.h b/Graphics/GraphicsEngine/interface/APIInfo.h index 608c6fe3..3ade31f3 100644 --- a/Graphics/GraphicsEngine/interface/APIInfo.h +++ b/Graphics/GraphicsEngine/interface/APIInfo.h @@ -39,65 +39,67 @@ DILIGENT_BEGIN_NAMESPACE(Diligent) /// Diligent API Info. This tructure can be used to verify API compatibility. struct APIInfo { - size_t StructSize DEFAULT_INITIALIZER(0); - int APIVersion DEFAULT_INITIALIZER(0); - size_t RenderTargetBlendDescSize DEFAULT_INITIALIZER(0); - size_t BlendStateDescSize DEFAULT_INITIALIZER(0); - size_t BufferDescSize DEFAULT_INITIALIZER(0); - size_t BufferDataSize DEFAULT_INITIALIZER(0); - size_t BufferFormatSize DEFAULT_INITIALIZER(0); - size_t BufferViewDescSize DEFAULT_INITIALIZER(0); - size_t StencilOpDescSize DEFAULT_INITIALIZER(0); - size_t DepthStencilStateDescSize DEFAULT_INITIALIZER(0); - size_t SamplerCapsSize DEFAULT_INITIALIZER(0); - size_t TextureCapsSize DEFAULT_INITIALIZER(0); - size_t DeviceCapsSize DEFAULT_INITIALIZER(0); - size_t DrawAttribsSize DEFAULT_INITIALIZER(0); - size_t DispatchComputeAttribsSize DEFAULT_INITIALIZER(0); - size_t ViewportSize DEFAULT_INITIALIZER(0); - size_t RectSize DEFAULT_INITIALIZER(0); - size_t CopyTextureAttribsSize DEFAULT_INITIALIZER(0); - size_t DeviceObjectAttribsSize DEFAULT_INITIALIZER(0); - size_t GraphicsAdapterInfoSize DEFAULT_INITIALIZER(0); - size_t DisplayModeAttribsSize DEFAULT_INITIALIZER(0); - size_t SwapChainDescSize DEFAULT_INITIALIZER(0); - size_t FullScreenModeDescSize DEFAULT_INITIALIZER(0); - size_t EngineCreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineGLCreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineD3D11CreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineD3D12CreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineVkCreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineMtlCreateInfoSize DEFAULT_INITIALIZER(0); - size_t BoxSize DEFAULT_INITIALIZER(0); - size_t TextureFormatAttribsSize DEFAULT_INITIALIZER(0); - size_t TextureFormatInfoSize DEFAULT_INITIALIZER(0); - size_t TextureFormatInfoExtSize DEFAULT_INITIALIZER(0); - size_t StateTransitionDescSize DEFAULT_INITIALIZER(0); - size_t LayoutElementSize DEFAULT_INITIALIZER(0); - size_t InputLayoutDescSize DEFAULT_INITIALIZER(0); - size_t SampleDescSize DEFAULT_INITIALIZER(0); - size_t ShaderResourceVariableDescSize DEFAULT_INITIALIZER(0); - size_t ImmutableSamplerDescSize DEFAULT_INITIALIZER(0); - size_t PipelineResourceLayoutDescSize DEFAULT_INITIALIZER(0); - size_t GraphicsPipelineDescSize DEFAULT_INITIALIZER(0); - size_t GraphicsPipelineStateCreateInfoSize DEFAULT_INITIALIZER(0); - size_t ComputePipelineStateCreateInfoSize DEFAULT_INITIALIZER(0); - size_t PipelineStateDescSize DEFAULT_INITIALIZER(0); - size_t RasterizerStateDescSize DEFAULT_INITIALIZER(0); - size_t ResourceMappingEntrySize DEFAULT_INITIALIZER(0); - size_t ResourceMappingDescSize DEFAULT_INITIALIZER(0); - size_t SamplerDescSize DEFAULT_INITIALIZER(0); - size_t ShaderDescSize DEFAULT_INITIALIZER(0); - size_t ShaderMacroSize DEFAULT_INITIALIZER(0); - size_t ShaderCreateInfoSize DEFAULT_INITIALIZER(0); - size_t ShaderResourceDescSize DEFAULT_INITIALIZER(0); - size_t DepthStencilClearValueSize DEFAULT_INITIALIZER(0); - size_t OptimizedClearValueSize DEFAULT_INITIALIZER(0); - size_t TextureDescSize DEFAULT_INITIALIZER(0); - size_t TextureSubResDataSize DEFAULT_INITIALIZER(0); - size_t TextureDataSize DEFAULT_INITIALIZER(0); - size_t MappedTextureSubresourceSize DEFAULT_INITIALIZER(0); - size_t TextureViewDescSize DEFAULT_INITIALIZER(0); + size_t StructSize DEFAULT_INITIALIZER(0); + int APIVersion DEFAULT_INITIALIZER(0); + size_t RenderTargetBlendDescSize DEFAULT_INITIALIZER(0); + size_t BlendStateDescSize DEFAULT_INITIALIZER(0); + size_t BufferDescSize DEFAULT_INITIALIZER(0); + size_t BufferDataSize DEFAULT_INITIALIZER(0); + size_t BufferFormatSize DEFAULT_INITIALIZER(0); + size_t BufferViewDescSize DEFAULT_INITIALIZER(0); + size_t StencilOpDescSize DEFAULT_INITIALIZER(0); + size_t DepthStencilStateDescSize DEFAULT_INITIALIZER(0); + size_t SamplerCapsSize DEFAULT_INITIALIZER(0); + size_t TextureCapsSize DEFAULT_INITIALIZER(0); + size_t DeviceCapsSize DEFAULT_INITIALIZER(0); + size_t DrawAttribsSize DEFAULT_INITIALIZER(0); + size_t DispatchComputeAttribsSize DEFAULT_INITIALIZER(0); + size_t ViewportSize DEFAULT_INITIALIZER(0); + size_t RectSize DEFAULT_INITIALIZER(0); + size_t CopyTextureAttribsSize DEFAULT_INITIALIZER(0); + size_t DeviceObjectAttribsSize DEFAULT_INITIALIZER(0); + size_t GraphicsAdapterInfoSize DEFAULT_INITIALIZER(0); + size_t DisplayModeAttribsSize DEFAULT_INITIALIZER(0); + size_t SwapChainDescSize DEFAULT_INITIALIZER(0); + size_t FullScreenModeDescSize DEFAULT_INITIALIZER(0); + size_t EngineCreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineGLCreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineD3D11CreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineD3D12CreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineVkCreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineMtlCreateInfoSize DEFAULT_INITIALIZER(0); + size_t BoxSize DEFAULT_INITIALIZER(0); + size_t TextureFormatAttribsSize DEFAULT_INITIALIZER(0); + size_t TextureFormatInfoSize DEFAULT_INITIALIZER(0); + size_t TextureFormatInfoExtSize DEFAULT_INITIALIZER(0); + size_t StateTransitionDescSize DEFAULT_INITIALIZER(0); + size_t LayoutElementSize DEFAULT_INITIALIZER(0); + size_t InputLayoutDescSize DEFAULT_INITIALIZER(0); + size_t SampleDescSize DEFAULT_INITIALIZER(0); + size_t ShaderResourceVariableDescSize DEFAULT_INITIALIZER(0); + size_t ImmutableSamplerDescSize DEFAULT_INITIALIZER(0); + size_t PipelineResourceLayoutDescSize DEFAULT_INITIALIZER(0); + size_t PipelineStateDescSize DEFAULT_INITIALIZER(0); + size_t GraphicsPipelineDescSize DEFAULT_INITIALIZER(0); + size_t GraphicsPipelineStateCreateInfoSize DEFAULT_INITIALIZER(0); + size_t ComputePipelineStateCreateInfoSize DEFAULT_INITIALIZER(0); + size_t RayTracingPipelineDescSize DEFAULT_INITIALIZER(0); + size_t RayTracingPipelineStateCreateInfoSize DEFAULT_INITIALIZER(0); + size_t RasterizerStateDescSize DEFAULT_INITIALIZER(0); + size_t ResourceMappingEntrySize DEFAULT_INITIALIZER(0); + size_t ResourceMappingDescSize DEFAULT_INITIALIZER(0); + size_t SamplerDescSize DEFAULT_INITIALIZER(0); + size_t ShaderDescSize DEFAULT_INITIALIZER(0); + size_t ShaderMacroSize DEFAULT_INITIALIZER(0); + size_t ShaderCreateInfoSize DEFAULT_INITIALIZER(0); + size_t ShaderResourceDescSize DEFAULT_INITIALIZER(0); + size_t DepthStencilClearValueSize DEFAULT_INITIALIZER(0); + size_t OptimizedClearValueSize DEFAULT_INITIALIZER(0); + size_t TextureDescSize DEFAULT_INITIALIZER(0); + size_t TextureSubResDataSize DEFAULT_INITIALIZER(0); + size_t TextureDataSize DEFAULT_INITIALIZER(0); + size_t MappedTextureSubresourceSize DEFAULT_INITIALIZER(0); + size_t TextureViewDescSize DEFAULT_INITIALIZER(0); }; typedef struct APIInfo APIInfo; diff --git a/Graphics/GraphicsEngine/interface/BottomLevelAS.h b/Graphics/GraphicsEngine/interface/BottomLevelAS.h new file mode 100644 index 00000000..f7190c93 --- /dev/null +++ b/Graphics/GraphicsEngine/interface/BottomLevelAS.h @@ -0,0 +1,290 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::IBottomLevelAS interface and related data structures + +#include "../../../Primitives/interface/Object.h" +#include "../../../Primitives/interface/FlagEnum.h" +#include "GraphicsTypes.h" +#include "Constants.h" +#include "Buffer.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {E56F5755-FE5E-496C-BFA7-BCD535360FF7} +static const INTERFACE_ID IID_BottomLevelAS = + {0xe56f5755, 0xfe5e, 0x496c, {0xbf, 0xa7, 0xbc, 0xd5, 0x35, 0x36, 0xf, 0xf7}}; + +// clang-format off + +static const Uint32 INVALID_INDEX = ~0u; + + +/// Defines bottom level acceleration structure triangles description. + +/// Triangle geometry description. +struct BLASTriangleDesc +{ + /// Geometry name. + /// The name is used to map triangle data (BLASBuildTriangleData) to this geometry. + const char* GeometryName DEFAULT_INITIALIZER(nullptr); + + /// The maximum vertex count in this geometry. + /// Current number of vertices is defined in BLASBuildTriangleData::VertexCount. + Uint32 MaxVertexCount DEFAULT_INITIALIZER(0); + + /// The type of vertices in this geometry, see Diligent::VALUE_TYPE. + /// + /// \remarks Only the following values are allowed: VT_FLOAT32, VT_FLOAT16, VT_INT16. + /// VT_INT16 defines 16-bit signed normalized vertex components. + VALUE_TYPE VertexValueType DEFAULT_INITIALIZER(VT_UNDEFINED); + + /// The number of components in the vertex. + /// + /// \remarks Only 2 or 3 are allowed values. For 2-component formats, the third component is assumed 0. + Uint8 VertexComponentCount DEFAULT_INITIALIZER(0); + + /// The maximum primitive count in this geometry. + /// The current number of primitives is defined in BLASBuildTriangleData::PrimitiveCount. + Uint32 MaxPrimitiveCount DEFAULT_INITIALIZER(0); + + /// Index type of this geometry, see Diligent::VALUE_TYPE. + /// Must be VT_UINT16, VT_UINT32 or VT_UNDEFINED. + /// If not defined then vertex array is used instead of indexed vertices. + VALUE_TYPE IndexType DEFAULT_INITIALIZER(VT_UNDEFINED); + + /// Vulkan only, allows to use transformations in BLASBuildTriangleData. + Bool AllowsTransforms DEFAULT_INITIALIZER(False); + +#if DILIGENT_CPP_INTERFACE + BLASTriangleDesc() noexcept {} +#endif +}; +typedef struct BLASTriangleDesc BLASTriangleDesc; + + +/// Defines bottom level acceleration structure axis-aligned bounding boxes description. + +/// AABB geometry description. +struct BLASBoundingBoxDesc +{ + /// Geometry name. + /// The name is used to map AABB data (BLASBuildBoundingBoxData) to this geometry. + const char* GeometryName DEFAULT_INITIALIZER(nullptr); + + /// The maximum AABB count. + /// Current number of AABBs is defined in BLASBuildBoundingBoxData::BoxCount. + Uint32 MaxBoxCount DEFAULT_INITIALIZER(0); + +#if DILIGENT_CPP_INTERFACE + BLASBoundingBoxDesc() noexcept {} +#endif +}; +typedef struct BLASBoundingBoxDesc BLASBoundingBoxDesc; + + +/// Defines acceleration structures build flags. +DILIGENT_TYPED_ENUM(RAYTRACING_BUILD_AS_FLAGS, Uint8) +{ + RAYTRACING_BUILD_AS_NONE = 0, + + /// Indicates that the specified acceleration structure can be updated + /// via IDeviceContext::BuildBLAS() or IDeviceContext::BuildTLAS(). + /// With this flag, the acceleration structure may allocate more memory and take more time to 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. + RAYTRACING_BUILD_AS_PREFER_FAST_TRACE = 0x04, + + /// Indicates that the given acceleration structure build should prioritize build time over trace performance. + RAYTRACING_BUILD_AS_PREFER_FAST_BUILD = 0x08, + + /// Indicates that this acceleration structure should minimize the size of the scratch memory and the final + /// result build, potentially at the expense of build time or trace performance. + RAYTRACING_BUILD_AS_LOW_MEMORY = 0x10, + + RAYTRACING_BUILD_AS_FLAGS_LAST = RAYTRACING_BUILD_AS_LOW_MEMORY +}; +DEFINE_FLAG_ENUM_OPERATORS(RAYTRACING_BUILD_AS_FLAGS) + + +/// Bottom-level AS description. +struct BottomLevelASDesc DILIGENT_DERIVE(DeviceObjectAttribs) + + /// Array of triangle geometry descriptions. + const BLASTriangleDesc* pTriangles DEFAULT_INITIALIZER(nullptr); + + /// The number of triangle geometries in pTriangles array. + Uint32 TriangleCount DEFAULT_INITIALIZER(0); + + /// Array of AABB geometry descriptions. + const BLASBoundingBoxDesc* pBoxes DEFAULT_INITIALIZER(nullptr); + + /// The number of AABB geometries in pBoxes array. + Uint32 BoxCount DEFAULT_INITIALIZER(0); + + /// Ray tracing build flags, see Diligent::RAYTRACING_BUILD_AS_FLAGS. + RAYTRACING_BUILD_AS_FLAGS Flags DEFAULT_INITIALIZER(RAYTRACING_BUILD_AS_NONE); + + /// Size from the result of IDeviceContext::WriteBLASCompactedSize() if this acceleration structure + /// is going to be the target of a compacting copy (IDeviceContext::CopyBLAS() with COPY_AS_MODE_COMPACT). + Uint32 CompactedSize DEFAULT_INITIALIZER(0); + + /// Defines which command queues this BLAS can be used with + Uint64 CommandQueueMask DEFAULT_INITIALIZER(1); + +#if DILIGENT_CPP_INTERFACE + BottomLevelASDesc() noexcept {} +#endif +}; +typedef struct BottomLevelASDesc BottomLevelASDesc; + + +/// Defines the scratch buffer info for acceleration structure. +struct ScratchBufferSizes +{ + /// Scratch buffer size for acceleration structure building, + /// see IDeviceContext::BuildBLAS(), IDeviceContext::BuildTLAS(). + /// May be zero if the acceleration structure was created with non-zero CompactedSize. + Uint32 Build DEFAULT_INITIALIZER(0); + + /// Scratch buffer size for acceleration structure updating, + /// see IDeviceContext::BuildBLAS(), IDeviceContext::BuildTLAS(). + /// May be zero if acceleration structure was created without RAYTRACING_BUILD_AS_ALLOW_UPDATE flag. + /// May be zero if acceleration structure was created with non-zero CompactedSize. + Uint32 Update DEFAULT_INITIALIZER(0); + +#if DILIGENT_CPP_INTERFACE + ScratchBufferSizes() noexcept {} +#endif +}; +typedef struct ScratchBufferSizes ScratchBufferSizes; + + +#define DILIGENT_INTERFACE_NAME IBottomLevelAS +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IBottomLevelASInclusiveMethods \ + IDeviceObjectInclusiveMethods; \ + IBottomLevelASMethods BottomLevelAS + +/// Bottom-level AS interface + +/// Defines the methods to manipulate a BLAS object +DILIGENT_BEGIN_INTERFACE(IBottomLevelAS, IDeviceObject) +{ +#if DILIGENT_CPP_INTERFACE + /// Returns the bottom level AS description used to create the object + 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 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 + /// 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 + /// resets the internal acceleration structure state to the given value. + /// This method should be used after the application finished + /// manually managing the acceleration structure state and wants to hand over + /// state management back to the engine. + VIRTUAL void METHOD(SetState)(THIS_ + RESOURCE_STATE State) PURE; + + + /// Returns the internal acceleration structure state + VIRTUAL RESOURCE_STATE METHOD(GetState)(THIS) CONST PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +# 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 + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngine/interface/Buffer.h b/Graphics/GraphicsEngine/interface/Buffer.h index 417b8ac3..dd7f0c84 100644 --- a/Graphics/GraphicsEngine/interface/Buffer.h +++ b/Graphics/GraphicsEngine/interface/Buffer.h @@ -81,7 +81,7 @@ struct BufferDesc DILIGENT_DERIVE(DeviceObjectAttribs) /// The following bind flags are allowed: /// Diligent::BIND_VERTEX_BUFFER, Diligent::BIND_INDEX_BUFFER, Diligent::BIND_UNIFORM_BUFFER, /// Diligent::BIND_SHADER_RESOURCE, Diligent::BIND_STREAM_OUTPUT, Diligent::BIND_UNORDERED_ACCESS, - /// Diligent::BIND_INDIRECT_DRAW_ARGS + /// Diligent::BIND_INDIRECT_DRAW_ARGS, Diligent::BIND_RAY_TRACING BIND_FLAGS BindFlags DEFAULT_INITIALIZER(BIND_NONE); /// Buffer usage, see Diligent::USAGE for details diff --git a/Graphics/GraphicsEngine/interface/Constants.h b/Graphics/GraphicsEngine/interface/Constants.h index 9cd45c4d..b00f2de2 100644 --- a/Graphics/GraphicsEngine/interface/Constants.h +++ b/Graphics/GraphicsEngine/interface/Constants.h @@ -51,8 +51,8 @@ static const Uint32 MAX_RENDER_TARGETS = DILIGENT_MAX_RENDER_TARGETS; static const Uint32 MAX_VIEWPORTS = DILIGENT_MAX_VIEWPORTS; /// Maximum number of shader stages in a pipeline. -/// (Vertex, Hull, Domain, Geometry, Pixel) or (Amplification, Mesh, Pixel), or (Compute) -static const Uint32 MAX_SHADERS_IN_PIPELINE = 5; +/// (Vertex, Hull, Domain, Geometry, Pixel) or (Amplification, Mesh, Pixel), or (Compute) or (RayGen, Miss, ClosestHit, AnyHit, Intersection, Callable) +static const Uint32 MAX_SHADERS_IN_PIPELINE = 6; // clang-format on diff --git a/Graphics/GraphicsEngine/interface/DeviceContext.h b/Graphics/GraphicsEngine/interface/DeviceContext.h index 122661f0..73e2640c 100644 --- a/Graphics/GraphicsEngine/interface/DeviceContext.h +++ b/Graphics/GraphicsEngine/interface/DeviceContext.h @@ -53,6 +53,9 @@ #include "Framebuffer.h" #include "CommandList.h" #include "SwapChain.h" +#include "BottomLevelAS.h" +#include "TopLevelAS.h" +#include "ShaderBindingTable.h" DILIGENT_BEGIN_NAMESPACE(Diligent) @@ -716,6 +719,645 @@ struct BeginRenderPassAttribs }; typedef struct BeginRenderPassAttribs BeginRenderPassAttribs; + +/// TLAS instance flags that are used in IDeviceContext::BuildTLAS(). +DILIGENT_TYPED_ENUM(RAYTRACING_INSTANCE_FLAGS, Uint8) +{ + RAYTRACING_INSTANCE_NONE = 0, + + /// Disables face culling for this instance. + RAYTRACING_INSTANCE_TRIANGLE_FACING_CULL_DISABLE = 0x01, + + /// Indicates that the front face of the triangle for culling purposes is the face that is counter + /// clockwise in object space relative to the ray origin. Because the facing is determined in object + /// space, an instance transform matrix does not change the winding, but a geometry transform does. + RAYTRACING_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE = 0x02, + + /// Causes this instance to act as though RAYTRACING_GEOMETRY_FLAGS_OPAQUE were specified on all + /// geometries referenced by this instance. This behavior can be overridden in the shader with ray flags. + RAYTRACING_INSTANCE_FORCE_OPAQUE = 0x04, + + /// Causes this instance to act as though RAYTRACING_GEOMETRY_FLAGS_OPAQUE were not specified on all + /// geometries referenced by this instance. This behavior can be overridden in the shader with ray flags. + RAYTRACING_INSTANCE_FORCE_NO_OPAQUE = 0x08, + + RAYTRACING_INSTANCE_FLAGS_LAST = RAYTRACING_INSTANCE_FORCE_NO_OPAQUE +}; +DEFINE_FLAG_ENUM_OPERATORS(RAYTRACING_INSTANCE_FLAGS) + + +/// Defines acceleration structure copy mode. + +/// These the flags used by IDeviceContext::CopyBLAS() and IDeviceContext::CopyTLAS(). +DILIGENT_TYPED_ENUM(COPY_AS_MODE, Uint8) +{ + /// Creates a direct copy of the acceleration structure specified in pSrc into the one specified by pDst. + /// The pDst acceleration structure must have been created with the same parameters as pSrc. + COPY_AS_MODE_CLONE = 0, + + /// Creates a more compact version of an acceleration structure pSrc into pDst. + /// The acceleration structure pDst must have been created with a CompactedSize corresponding + /// to the one returned by IDeviceContext::WriteBLASCompactedSize() or IDeviceContext::WriteTLASCompactedSize() + /// after the build of the acceleration structure specified by pSrc. + COPY_AS_MODE_COMPACT, + + COPY_AS_MODE_LAST = COPY_AS_MODE_COMPACT, +}; + + +/// Defines geometry flags for ray tracing. +DILIGENT_TYPED_ENUM(RAYTRACING_GEOMETRY_FLAGS, Uint8) +{ + RAYTRACING_GEOMETRY_FLAG_NONE = 0, + + /// Indicates that this geometry does not invoke the any-hit shaders even if present in a hit group. + RAYTRACING_GEOMETRY_FLAG_OPAQUE = 0x01, + + /// Indicates that the implementation must only call the any-hit shader a single time for each primitive in this geometry. + /// If this bit is absent an implementation may invoke the any-hit shader more than once for this geometry. + RAYTRACING_GEOMETRY_FLAG_NO_DUPLICATE_ANY_HIT_INVOCATION = 0x02, + + RAYTRACING_GEOMETRY_FLAGS_LAST = RAYTRACING_GEOMETRY_FLAG_NO_DUPLICATE_ANY_HIT_INVOCATION +}; +DEFINE_FLAG_ENUM_OPERATORS(RAYTRACING_GEOMETRY_FLAGS) + + +/// Triangle geometry data description. +struct BLASBuildTriangleData +{ + /// Geometry name used to map a geometry to a hit group in the shader binding table. + /// Add geometry data to the geometry that is allocated by BLASTriangleDesc with the same name. + const char* GeometryName DEFAULT_INITIALIZER(nullptr); + + /// Triangle vertices data source. + /// Triangles are considered "inactive" if the x component of each vertex is NaN. + /// The buffer must be created with BIND_RAY_TRACING flag. + IBuffer* pVertexBuffer DEFAULT_INITIALIZER(nullptr); + + /// Data offset in bytes in pVertexBuffer. + Uint32 VertexOffset DEFAULT_INITIALIZER(0); + + /// Stride in bytes between vertices. + Uint32 VertexStride DEFAULT_INITIALIZER(0); + + /// The number of triangle vertices. + /// Must be less than or equal to BLASTriangleDesc::MaxVertexCount. + Uint32 VertexCount DEFAULT_INITIALIZER(0); + + /// The type of the vertex components. + /// 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. + /// This is an optional value. Must be undefined or same as in BLASTriangleDesc. + Uint8 VertexComponentCount DEFAULT_INITIALIZER(0); + + /// The number of triangles. + /// Must equal to VertexCount / 3 if pIndexBuffer is null or must be equal to index count / 3. + Uint32 PrimitiveCount DEFAULT_INITIALIZER(0); + + /// Triangle indices data source. + /// Must be null if BLASTriangleDesc::IndexType is undefined. + /// The buffer must be created with BIND_RAY_TRACING flag. + IBuffer* pIndexBuffer DEFAULT_INITIALIZER(nullptr); + + /// Data offset in bytes in pIndexBuffer. + Uint32 IndexOffset DEFAULT_INITIALIZER(0); + + /// The type of triangle indices, see Diligent::VALUE_TYPE. + /// This is an optional value. Must be undefined or same as in BLASTriangleDesc. + VALUE_TYPE IndexType DEFAULT_INITIALIZER(VT_UNDEFINED); + + /// Geometry transformation data source. + /// The buffer must be created with BIND_RAY_TRACING flag. + IBuffer* pTransformBuffer DEFAULT_INITIALIZER(nullptr); + + /// Data offset in bytes in pTransformBuffer. + Uint32 TransformBufferOffset DEFAULT_INITIALIZER(0); + + /// Geometry flags, se Diligent::RAYTRACING_GEOMETRY_FLAGS. + RAYTRACING_GEOMETRY_FLAGS Flags DEFAULT_INITIALIZER(RAYTRACING_GEOMETRY_FLAG_NONE); + +#if DILIGENT_CPP_INTERFACE + BLASBuildTriangleData() noexcept {} +#endif +}; +typedef struct BLASBuildTriangleData BLASBuildTriangleData; + + +/// AABB geometry data description. +struct BLASBuildBoundingBoxData +{ + /// Geometry name used to map geometry to hit group in shader binding table. + /// Put geometry data to geometry that allocated by BLASBoundingBoxDesc with the same name. + const char* GeometryName DEFAULT_INITIALIZER(nullptr); + + /// AABB data source. + /// Each AABB defined as { float3 Min; float3 Max } structure. + /// AABB are considered inactive if AABB.Min.x is NaN. + /// Buffer must be created with BIND_RAY_TRACING flag. + IBuffer* pBoxBuffer DEFAULT_INITIALIZER(nullptr); + + /// Data offset in bytes in pBoxBuffer. + Uint32 BoxOffset DEFAULT_INITIALIZER(0); + + /// Stride in bytes between each AABB. + Uint32 BoxStride DEFAULT_INITIALIZER(0); + + /// Number of AABBs. + /// Must be less than or equal to BLASBoundingBoxDesc::MaxBoxCount. + Uint32 BoxCount DEFAULT_INITIALIZER(0); + + /// Geometry flags, see Diligent::RAYTRACING_GEOMETRY_FLAGS. + RAYTRACING_GEOMETRY_FLAGS Flags DEFAULT_INITIALIZER(RAYTRACING_GEOMETRY_FLAG_NONE); + +#if DILIGENT_CPP_INTERFACE + BLASBuildBoundingBoxData() noexcept {} +#endif +}; +typedef struct BLASBuildBoundingBoxData BLASBuildBoundingBoxData; + + +/// This structure is used by IDeviceContext::BuildBLAS(). +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). + RESOURCE_STATE_TRANSITION_MODE BLASTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + + /// Geometry data source buffers state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + 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 the same as what was 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 the count must be the same as the one 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 the count must be the same as the one used to build BLAS. + Uint32 BoxDataCount DEFAULT_INITIALIZER(0); + + /// The buffer that is used for acceleration structure building. + /// Must be created with BIND_RAY_TRACING. + /// Call IBottomLevelAS::GetScratchBufferSizes().Build to get the minimal size for the scratch buffer. + IBuffer* pScratchBuffer DEFAULT_INITIALIZER(nullptr); + + /// Offset from the beginning of the buffer. + Uint32 ScratchBufferOffset DEFAULT_INITIALIZER(0); + + /// 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 +}; +typedef struct BuildBLASAttribs BuildBLASAttribs; + + +/// 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; +/// if (BindingMode == HIT_GROUP_BINDING_MODE_PER_GEOMETRY) InstanceOffset += Instance.pBLAS->GeometryCount() * HitGroupStride; +/// if (BindingMode == HIT_GROUP_BINDING_MODE_PER_INSTANCE) InstanceOffset += HitGroupStride; +static const Uint32 TLAS_INSTANCE_OFFSET_AUTO = ~0u; + + +/// Row-major matrix +struct InstanceMatrix +{ + /// rotation translation + /// ([0,0] [0,1] [0,2]) ([0,3]) + /// ([1,0] [1,1] [1,2]) ([1,3]) + /// ([2,0] [2,1] [2,2]) ([2,3]) + float data [3][4]; + +#if DILIGENT_CPP_INTERFACE + /// Construct identity matrix. + InstanceMatrix() noexcept : + data{{1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}} + {} + + InstanceMatrix(const InstanceMatrix&) noexcept = default; + + /// Sets the translation part. + InstanceMatrix& SetTranslation(float x, float y, float z) noexcept + { + data[0][3] = x; + data[1][3] = y; + data[2][3] = z; + return *this; + } + + /// Sets the rotation part. + InstanceMatrix& SetRotation(const float* pMatrix3x3) noexcept + { + data[0][0] = pMatrix3x3[0]; data[1][0] = pMatrix3x3[1]; data[2][0] = pMatrix3x3[2]; + data[0][1] = pMatrix3x3[3]; data[1][1] = pMatrix3x3[4]; data[2][1] = pMatrix3x3[5]; + data[0][2] = pMatrix3x3[6]; data[1][2] = pMatrix3x3[7]; data[2][2] = pMatrix3x3[8]; + return *this; + } +#endif +}; +typedef struct InstanceMatrix InstanceMatrix; + + +/// This structure is used by BuildTLASAttribs. +struct TLASBuildInstanceData +{ + /// Instance name that is used to map an instance to a hit group in shader binding table. + 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. + InstanceMatrix Transform; + + /// User-defined value that can be accessed in the shader via InstanceID() in HLSL and gl_InstanceCustomIndex in GLSL. + /// Only the lower 24 bits are used. + Uint32 CustomId DEFAULT_INITIALIZER(0); + + /// Instance flags, see Diligent::RAYTRACING_INSTANCE_FLAGS. + 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 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 BuildTLASAttribs::BindingMode is not SHADER_BINDING_USER_DEFINED. + /// Only the lower 24 bits are used. + Uint32 ContributionToHitGroupIndex DEFAULT_INITIALIZER(TLAS_INSTANCE_OFFSET_AUTO); + +#if DILIGENT_CPP_INTERFACE + TLASBuildInstanceData() noexcept {} +#endif +}; +typedef struct TLASBuildInstanceData TLASBuildInstanceData; + + +/// 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; + + +/// 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). + RESOURCE_STATE_TRANSITION_MODE TLASTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + + /// Bottom-level AS (in TLASBuildInstanceData::pBLAS) state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + 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 an instance set TLASBuildInstanceData::Mask to zero or set empty TLASBuildInstanceData::BLAS to pBLAS. + 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. + /// The buffer size must be at least TLAS_INSTANCE_DATA_SIZE * InstanceCount. + /// The buffer must be created with BIND_RAY_TRACING flag. + IBuffer* pInstanceBuffer DEFAULT_INITIALIZER(nullptr); + + /// Offset from the beginning of the buffer to the location of instance data. + Uint32 InstanceBufferOffset DEFAULT_INITIALIZER(0); + + /// Instance buffer state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE InstanceBufferTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + + /// The number of hit shaders that can be bound for a single geometry or an instance (depends on BindingMode). + /// - Used to calculate TLASBuildInstanceData::ContributionToHitGroupIndex. + /// - Ignored if BindingMode is SHADER_BINDING_USER_DEFINED. + /// You should use the same value in a shader: + /// 'MultiplierForGeometryContributionToHitGroupIndex' argument in TraceRay() in HLSL, 'sbtRecordStride' argument in traceRay() in GLSL. + Uint32 HitGroupStride 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. + HIT_GROUP_BINDING_MODE BindingMode DEFAULT_INITIALIZER(HIT_GROUP_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. + Uint32 ScratchBufferOffset DEFAULT_INITIALIZER(0); + + /// 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 +}; +typedef struct BuildTLASAttribs BuildTLASAttribs; + + +/// This structure is used by IDeviceContext::CopyBLAS(). +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. + COPY_AS_MODE Mode DEFAULT_INITIALIZER(COPY_AS_MODE_CLONE); + + /// Source bottom-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE SrcTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + + /// Destination bottom-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE DstTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + +#if DILIGENT_CPP_INTERFACE + CopyBLASAttribs() noexcept {} +#endif +}; +typedef struct CopyBLASAttribs CopyBLASAttribs; + + +/// This structure is used by IDeviceContext::CopyTLAS(). +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. + COPY_AS_MODE Mode DEFAULT_INITIALIZER(COPY_AS_MODE_CLONE); + + /// Source top-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE SrcTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + + /// Destination top-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE DstTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + +#if DILIGENT_CPP_INTERFACE + CopyTLASAttribs() noexcept {} +#endif +}; +typedef struct CopyTLASAttribs CopyTLASAttribs; + + +/// This structure is used by IDeviceContext::WriteBLASCompactedSize(). +struct WriteBLASCompactedSizeAttribs +{ + /// Bottom-level AS. + IBottomLevelAS* pBLAS DEFAULT_INITIALIZER(nullptr); + + /// The destination buffer into which a 64-bit value representing the acceleration structure compacted size will be written to. + IBuffer* pDestBuffer DEFAULT_INITIALIZER(nullptr); + + /// Offset from the beginning of the buffer to the location of the AS compacted size. + Uint32 DestBufferOffset DEFAULT_INITIALIZER(0); + + /// Bottom-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE BLASTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + + /// Destination buffer state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE BufferTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + +#if DILIGENT_CPP_INTERFACE + WriteBLASCompactedSizeAttribs() noexcept {} +#endif +}; +typedef struct WriteBLASCompactedSizeAttribs WriteBLASCompactedSizeAttribs; + + +/// This structure is used by IDeviceContext::WriteTLASCompactedSize(). +struct WriteTLASCompactedSizeAttribs +{ + /// Top-level AS. + ITopLevelAS* pTLAS DEFAULT_INITIALIZER(nullptr); + + /// The destination buffer into which a 64-bit value representing the acceleration structure compacted size will be written to. + IBuffer* pDestBuffer DEFAULT_INITIALIZER(nullptr); + + /// Offset from the beginning of the buffer to the location of the AS compacted size. + Uint32 DestBufferOffset DEFAULT_INITIALIZER(0); + + /// Top-level AS state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE TLASTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + + /// Destination buffer state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE BufferTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + +#if DILIGENT_CPP_INTERFACE + WriteTLASCompactedSizeAttribs() noexcept {} +#endif +}; +typedef struct WriteTLASCompactedSizeAttribs WriteTLASCompactedSizeAttribs; + + +/// This structure is used by IDeviceContext::TraceRays(). +struct TraceRaysAttribs +{ + /// Shader binding table. + IShaderBindingTable* pSBT DEFAULT_INITIALIZER(nullptr); + + Uint32 DimensionX DEFAULT_INITIALIZER(1); ///< The number of rays dispatched in X direction. + Uint32 DimensionY DEFAULT_INITIALIZER(1); ///< The number of rays dispatched in Y direction. + Uint32 DimensionZ DEFAULT_INITIALIZER(1); ///< The number of rays dispatched in Z direction. + + /// Shader binding table buffer state transition mode (see Diligent::RESOURCE_STATE_TRANSITION_MODE). + RESOURCE_STATE_TRANSITION_MODE SBTTransitionMode DEFAULT_INITIALIZER(RESOURCE_STATE_TRANSITION_MODE_NONE); + +#if DILIGENT_CPP_INTERFACE + TraceRaysAttribs() noexcept {} +#endif +}; +typedef struct TraceRaysAttribs TraceRaysAttribs; + + +static const Uint32 REMAINING_MIP_LEVELS = ~0u; +static const Uint32 REMAINING_ARRAY_SLICES = ~0u; + +/// Resource state transition barrier description +struct StateTransitionDesc +{ + /// Resource to transition. + /// Can be ITexture, IBuffer, IBottomLevelAS, ITopLevelAS. + struct IDeviceObject* pResource DEFAULT_INITIALIZER(nullptr); + + /// When transitioning a texture, first mip level of the subresource range to transition. + Uint32 FirstMipLevel DEFAULT_INITIALIZER(0); + + /// When transitioning a texture, number of mip levels of the subresource range to transition. + Uint32 MipLevelsCount DEFAULT_INITIALIZER(REMAINING_MIP_LEVELS); + + /// When transitioning a texture, first array slice of the subresource range to transition. + Uint32 FirstArraySlice DEFAULT_INITIALIZER(0); + + /// When transitioning a texture, number of array slices of the subresource range to transition. + Uint32 ArraySliceCount DEFAULT_INITIALIZER(REMAINING_ARRAY_SLICES); + + /// Resource state before transition. If this value is RESOURCE_STATE_UNKNOWN, + /// internal resource state will be used, which must be defined in this case. + RESOURCE_STATE OldState DEFAULT_INITIALIZER(RESOURCE_STATE_UNKNOWN); + + /// Resource state after transition. + RESOURCE_STATE NewState DEFAULT_INITIALIZER(RESOURCE_STATE_UNKNOWN); + + /// State transition type, see Diligent::STATE_TRANSITION_TYPE. + + /// \note When issuing UAV barrier (i.e. OldState and NewState equal RESOURCE_STATE_UNORDERED_ACCESS), + /// TransitionType must be STATE_TRANSITION_TYPE_IMMEDIATE. + STATE_TRANSITION_TYPE TransitionType DEFAULT_INITIALIZER(STATE_TRANSITION_TYPE_IMMEDIATE); + + /// If set to true, the internal resource state will be set to NewState and the engine + /// will be able to take over the resource state management. In this case it is the + /// responsibility of the application to make sure that all subresources are indeed in + /// designated state. + /// If set to false, internal resource state will be unchanged. + /// \note When TransitionType is STATE_TRANSITION_TYPE_BEGIN, this member must be false. + bool UpdateResourceState DEFAULT_INITIALIZER(false); + +#if DILIGENT_CPP_INTERFACE + StateTransitionDesc()noexcept{} + + StateTransitionDesc(ITexture* _pTexture, + RESOURCE_STATE _OldState, + RESOURCE_STATE _NewState, + Uint32 _FirstMipLevel = 0, + Uint32 _MipLevelsCount = REMAINING_MIP_LEVELS, + Uint32 _FirstArraySlice = 0, + Uint32 _ArraySliceCount = REMAINING_ARRAY_SLICES, + STATE_TRANSITION_TYPE _TransitionType = STATE_TRANSITION_TYPE_IMMEDIATE, + bool _UpdateState = false)noexcept : + pResource {static_cast<IDeviceObject*>(_pTexture)}, + FirstMipLevel {_FirstMipLevel }, + MipLevelsCount {_MipLevelsCount }, + FirstArraySlice {_FirstArraySlice}, + ArraySliceCount {_ArraySliceCount}, + OldState {_OldState }, + NewState {_NewState }, + TransitionType {_TransitionType }, + UpdateResourceState {_UpdateState } + {} + + StateTransitionDesc(ITexture* _pTexture, + RESOURCE_STATE _OldState, + RESOURCE_STATE _NewState, + bool _UpdateState)noexcept : + StateTransitionDesc + { + _pTexture, + _OldState, + _NewState, + 0, + REMAINING_MIP_LEVELS, + 0, + REMAINING_ARRAY_SLICES, + STATE_TRANSITION_TYPE_IMMEDIATE, + _UpdateState + } + {} + + StateTransitionDesc(IBuffer* _pBuffer, + RESOURCE_STATE _OldState, + RESOURCE_STATE _NewState, + bool _UpdateState)noexcept : + pResource {_pBuffer }, + OldState {_OldState }, + NewState {_NewState }, + UpdateResourceState {_UpdateState} + {} + + StateTransitionDesc(IBottomLevelAS* _pBLAS, + RESOURCE_STATE _OldState, + RESOURCE_STATE _NewState, + bool _UpdateState)noexcept : + pResource {_pBLAS }, + OldState {_OldState }, + NewState {_NewState }, + UpdateResourceState {_UpdateState} + {} + + StateTransitionDesc(ITopLevelAS* _pTLAS, + RESOURCE_STATE _OldState, + RESOURCE_STATE _NewState, + bool _UpdateState)noexcept : + pResource {_pTLAS }, + OldState {_OldState }, + NewState {_NewState }, + UpdateResourceState {_UpdateState} + {} +#endif +}; +typedef struct StateTransitionDesc StateTransitionDesc; + + #define DILIGENT_INTERFACE_NAME IDeviceContext #include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" @@ -1497,6 +2139,67 @@ DILIGENT_BEGIN_INTERFACE(IDeviceContext, IObject) ITexture* pSrcTexture, ITexture* pDstTexture, const ResolveTextureSubresourceAttribs REF ResolveAttribs) PURE; + + + /// Builds a bottom-level acceleration structure with the specified geometries. + + /// \param [in] Attribs - Structure describing build BLAS command attributes, see Diligent::BuildBLASAttribs for details. + /// + /// \note Don't call build or copy operation on the same BLAS in a different contexts, because BLAS has CPU-side data + /// that will not match with GPU-side, so shader binding were incorrect. + VIRTUAL void METHOD(BuildBLAS)(THIS_ + const BuildBLASAttribs REF Attribs) PURE; + + + /// Builds a top-level acceleration structure with the specified instances. + + /// \param [in] Attribs - Structure describing build TLAS command attributes, see Diligent::BuildTLASAttribs for details. + /// + /// \note Don't call build or copy operation on the same TLAS in a different contexts, because TLAS has CPU-side data + /// that will not match with GPU-side, so shader binding were incorrect. + VIRTUAL void METHOD(BuildTLAS)(THIS_ + const BuildTLASAttribs REF Attribs) PURE; + + + /// Copies data from one acceleration structure to another. + + /// \param [in] Attribs - Structure describing copy BLAS command attributes, see Diligent::CopyBLASAttribs for details. + /// + /// \note Don't call build or copy operation on the same BLAS in a different contexts, because BLAS has CPU-side data + /// that will not match with GPU-side, so shader binding were incorrect. + VIRTUAL void METHOD(CopyBLAS)(THIS_ + const CopyBLASAttribs REF Attribs) PURE; + + + /// Copies data from one acceleration structure to another. + + /// \param [in] Attribs - Structure describing copy TLAS command attributes, see Diligent::CopyTLASAttribs for details. + /// + /// \note Don't call build or copy operation on the same TLAS in a different contexts, because TLAS has CPU-side data + /// that will not match with GPU-side, so shader binding were incorrect. + VIRTUAL void METHOD(CopyTLAS)(THIS_ + const CopyTLASAttribs REF Attribs) PURE; + + + /// Writes a bottom-level acceleration structure memory size required for compacting operation to a buffer. + + /// \param [in] Attribs - Structure describing write BLAS compacted size command attributes, see Diligent::WriteBLASCompactedSizeAttribs for details. + VIRTUAL void METHOD(WriteBLASCompactedSize)(THIS_ + const WriteBLASCompactedSizeAttribs REF Attribs) PURE; + + + /// Writes a top-level acceleration structure memory size required for compacting operation to a buffer. + + /// \param [in] Attribs - Structure describing write TLAS compacted size command attributes, see Diligent::WriteTLASCompactedSizeAttribs for details. + VIRTUAL void METHOD(WriteTLASCompactedSize)(THIS_ + const WriteTLASCompactedSizeAttribs REF Attribs) PURE; + + + /// Executes a trace rays command. + + /// \param [in] Attribs - Trace rays command attributes, see Diligent::TraceRaysAttribs for details. + VIRTUAL void METHOD(TraceRays)(THIS_ + const TraceRaysAttribs REF Attribs) PURE; }; DILIGENT_END_INTERFACE @@ -1521,6 +2224,8 @@ DILIGENT_END_INTERFACE # define IDeviceContext_DrawIndexed(This, ...) CALL_IFACE_METHOD(DeviceContext, DrawIndexed, This, __VA_ARGS__) # define IDeviceContext_DrawIndirect(This, ...) CALL_IFACE_METHOD(DeviceContext, DrawIndirect, This, __VA_ARGS__) # define IDeviceContext_DrawIndexedIndirect(This, ...) CALL_IFACE_METHOD(DeviceContext, DrawIndexedIndirect, This, __VA_ARGS__) +# define IDeviceContext_DrawMesh(This, ...) CALL_IFACE_METHOD(DeviceContext, DrawMesh, This, __VA_ARGS__) +# define IDeviceContext_DrawMeshIndirect(This, ...) CALL_IFACE_METHOD(DeviceContext, DrawMeshIndirect, This, __VA_ARGS__) # define IDeviceContext_DispatchCompute(This, ...) CALL_IFACE_METHOD(DeviceContext, DispatchCompute, This, __VA_ARGS__) # define IDeviceContext_DispatchComputeIndirect(This, ...) CALL_IFACE_METHOD(DeviceContext, DispatchComputeIndirect, This, __VA_ARGS__) # define IDeviceContext_ClearDepthStencil(This, ...) CALL_IFACE_METHOD(DeviceContext, ClearDepthStencil, This, __VA_ARGS__) @@ -1546,6 +2251,13 @@ DILIGENT_END_INTERFACE # define IDeviceContext_GetFrameNumber(This) CALL_IFACE_METHOD(DeviceContext, GetFrameNumber, This) # define IDeviceContext_TransitionResourceStates(This, ...) CALL_IFACE_METHOD(DeviceContext, TransitionResourceStates, This, __VA_ARGS__) # define IDeviceContext_ResolveTextureSubresource(This, ...) CALL_IFACE_METHOD(DeviceContext, ResolveTextureSubresource, This, __VA_ARGS__) +# define IDeviceContext_BuildBLAS(This, ...) CALL_IFACE_METHOD(DeviceContext, BuildBLAS, This, __VA_ARGS__) +# define IDeviceContext_BuildTLAS(This, ...) CALL_IFACE_METHOD(DeviceContext, BuildTLAS, This, __VA_ARGS__) +# define IDeviceContext_CopyBLAS(This, ...) CALL_IFACE_METHOD(DeviceContext, CopyBLAS, This, __VA_ARGS__) +# define IDeviceContext_CopyTLAS(This, ...) CALL_IFACE_METHOD(DeviceContext, CopyTLAS, This, __VA_ARGS__) +# define IDeviceContext_WriteBLASCompactedSize(This, ...) CALL_IFACE_METHOD(DeviceContext, WriteBLASCompactedSize, This, __VA_ARGS__) +# define IDeviceContext_WriteTLASCompactedSize(This, ...) CALL_IFACE_METHOD(DeviceContext, WriteTLASCompactedSize, This, __VA_ARGS__) +# define IDeviceContext_TraceRays(This, ...) CALL_IFACE_METHOD(DeviceContext, TraceRays, This, __VA_ARGS__) // clang-format on diff --git a/Graphics/GraphicsEngine/interface/GraphicsTypes.h b/Graphics/GraphicsEngine/interface/GraphicsTypes.h index 816d8194..701d4f14 100644 --- a/Graphics/GraphicsEngine/interface/GraphicsTypes.h +++ b/Graphics/GraphicsEngine/interface/GraphicsTypes.h @@ -41,9 +41,6 @@ /// Graphics engine namespace DILIGENT_BEGIN_NAMESPACE(Diligent) -struct ITexture; -struct IBuffer; - /// Value type /// This enumeration describes value type. It is used by @@ -73,19 +70,22 @@ DILIGENT_TYPED_ENUM(VALUE_TYPE, Uint8) /// - TextureDesc to describe bind flags for a texture DILIGENT_TYPED_ENUM(BIND_FLAGS, Uint32) { - BIND_NONE = 0x0L, ///< Undefined binding - BIND_VERTEX_BUFFER = 0x1L, ///< A buffer can be bound as a vertex buffer - BIND_INDEX_BUFFER = 0x2L, ///< A buffer can be bound as an index buffer - BIND_UNIFORM_BUFFER = 0x4L, ///< A buffer can be bound as a uniform buffer - /// \warning This flag may not be combined with any other bind flag - BIND_SHADER_RESOURCE = 0x8L, ///< A buffer or a texture can be bound as a shader resource - /// \warning This flag cannot be used with MAP_WRITE_NO_OVERWRITE flag - BIND_STREAM_OUTPUT = 0x10L,///< A buffer can be bound as a target for stream output stage - BIND_RENDER_TARGET = 0x20L,///< A texture can be bound as a render target - BIND_DEPTH_STENCIL = 0x40L,///< A texture can be bound as a depth-stencil target - BIND_UNORDERED_ACCESS = 0x80L,///< A buffer or a texture can be bound as an unordered access view - BIND_INDIRECT_DRAW_ARGS = 0x100L,///< A buffer can be bound as the source buffer for indirect draw commands - BIND_INPUT_ATTACHMENT = 0x200L ///< A texture can be used as render pass input attachment + BIND_NONE = 0x0L, ///< Undefined binding + BIND_VERTEX_BUFFER = 0x1L, ///< A buffer can be bound as a vertex buffer + BIND_INDEX_BUFFER = 0x2L, ///< A buffer can be bound as an index buffer + BIND_UNIFORM_BUFFER = 0x4L, ///< A buffer can be bound as a uniform buffer + /// \warning This flag may not be combined with any other bind flag + BIND_SHADER_RESOURCE = 0x8L, ///< A buffer or a texture can be bound as a shader resource + /// \warning This flag cannot be used with MAP_WRITE_NO_OVERWRITE flag + BIND_STREAM_OUTPUT = 0x10L, ///< A buffer can be bound as a target for stream output stage + BIND_RENDER_TARGET = 0x20L, ///< A texture can be bound as a render target + BIND_DEPTH_STENCIL = 0x40L, ///< A texture can be bound as a depth-stencil target + BIND_UNORDERED_ACCESS = 0x80L, ///< A buffer or a texture can be bound as an unordered access view + BIND_INDIRECT_DRAW_ARGS = 0x100L, ///< A buffer can be bound as the source buffer for indirect draw commands + BIND_INPUT_ATTACHMENT = 0x200L, ///< A texture can be used as render pass input attachment + BIND_RAY_TRACING = 0x400L, ///< A buffer can be used as a scratch buffer or as the source of primitive data + /// for acceleration structure building + BIND_FLAGS_LAST = 0x400L }; DEFINE_FLAG_ENUM_OPERATORS(BIND_FLAGS) @@ -1251,16 +1251,18 @@ typedef struct DisplayModeAttribs DisplayModeAttribs; DILIGENT_TYPED_ENUM(SWAP_CHAIN_USAGE_FLAGS, Uint32) { /// No allowed usage - SWAP_CHAIN_USAGE_NONE = 0x00L, + SWAP_CHAIN_USAGE_NONE = 0x00L, /// Swap chain can be used as render target ouput - SWAP_CHAIN_USAGE_RENDER_TARGET = 0x01L, + SWAP_CHAIN_USAGE_RENDER_TARGET = 0x01L, /// Swap chain images can be used as shader inputs - SWAP_CHAIN_USAGE_SHADER_INPUT = 0x02L, + SWAP_CHAIN_USAGE_SHADER_INPUT = 0x02L, /// Swap chain images can be used as source of copy operation - SWAP_CHAIN_USAGE_COPY_SOURCE = 0x04L + SWAP_CHAIN_USAGE_COPY_SOURCE = 0x04L, + + SWAP_CHAIN_USAGE_LAST = SWAP_CHAIN_USAGE_COPY_SOURCE, }; DEFINE_FLAG_ENUM_OPERATORS(SWAP_CHAIN_USAGE_FLAGS) @@ -1553,6 +1555,9 @@ struct DeviceFeatures /// Indicates if device supports mesh and amplification shaders DEVICE_FEATURE_STATE MeshShaders DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); + + /// Indicates if device supports ray tracing shaders + DEVICE_FEATURE_STATE RayTracing DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); /// Indicates if device supports bindless resources DEVICE_FEATURE_STATE BindlessResources DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); @@ -1642,6 +1647,7 @@ struct DeviceFeatures GeometryShaders {State}, Tessellation {State}, MeshShaders {State}, + RayTracing {State}, BindlessResources {State}, OcclusionQueries {State}, BinaryOcclusionQueries {State}, @@ -1666,7 +1672,7 @@ struct DeviceFeatures UniformBuffer8BitAccess {State} { # if defined(_MSC_VER) && defined(_WIN64) - static_assert(sizeof(*this) == 31, "Did you add a new feature to DeviceFeatures? Please handle its status above."); + static_assert(sizeof(*this) == 32, "Did you add a new feature to DeviceFeatures? Please handle its status above."); # endif } #endif @@ -1849,6 +1855,15 @@ struct DeviceCaps typedef struct DeviceCaps DeviceCaps; +/// Device properties +struct DeviceProperties +{ + /// Maximum supported value for RayTracingPipelineDesc::MaxRecursionDepth. + Uint32 MaxRayTracingRecursionDepth; +}; +typedef struct DeviceProperties DeviceProperties; + + /// Engine creation attibutes struct EngineCreateInfo { @@ -1885,8 +1900,8 @@ typedef struct EngineCreateInfo EngineCreateInfo; /// Attributes of the OpenGL-based engine implementation struct EngineGLCreateInfo DILIGENT_DERIVE(EngineCreateInfo) - /// Native window wrapper - NativeWindow Window; + /// Native window wrapper + NativeWindow Window; /// Create debug OpenGL context and enable debug output. @@ -2118,6 +2133,7 @@ struct VulkanDescriptorPoolSize Uint32 NumUniformTexelBufferDescriptors DEFAULT_INITIALIZER(0); Uint32 NumStorageTexelBufferDescriptors DEFAULT_INITIALIZER(0); Uint32 NumInputAttachmentDescriptors DEFAULT_INITIALIZER(0); + Uint32 NumAccelStructDescriptors DEFAULT_INITIALIZER(0); #if DILIGENT_CPP_INTERFACE VulkanDescriptorPoolSize()noexcept {} @@ -2131,7 +2147,8 @@ struct VulkanDescriptorPoolSize Uint32 _NumStorageBufferDescriptors, Uint32 _NumUniformTexelBufferDescriptors, Uint32 _NumStorageTexelBufferDescriptors, - Uint32 _NumInputAttachmentDescriptors)noexcept : + Uint32 _NumInputAttachmentDescriptors, + Uint32 _NumAccelStructDescriptors)noexcept : MaxDescriptorSets {_MaxDescriptorSets }, NumSeparateSamplerDescriptors {_NumSeparateSamplerDescriptors }, NumCombinedSamplerDescriptors {_NumCombinedSamplerDescriptors }, @@ -2141,7 +2158,8 @@ struct VulkanDescriptorPoolSize NumStorageBufferDescriptors {_NumStorageBufferDescriptors }, NumUniformTexelBufferDescriptors{_NumUniformTexelBufferDescriptors}, NumStorageTexelBufferDescriptors{_NumStorageTexelBufferDescriptors}, - NumInputAttachmentDescriptors {_NumInputAttachmentDescriptors } + NumInputAttachmentDescriptors {_NumInputAttachmentDescriptors }, + NumAccelStructDescriptors {_NumAccelStructDescriptors } { // On clang aggregate initialization fails to compile if // structure members have default initializers @@ -2179,8 +2197,8 @@ struct EngineVkCreateInfo DILIGENT_DERIVE(EngineCreateInfo) /// the engine creates another one. VulkanDescriptorPoolSize MainDescriptorPoolSize #if DILIGENT_CPP_INTERFACE - //Max SepSm CmbSm SmpImg StrImg UB SB UTxB StTxB InptAtt - {8192, 1024, 8192, 8192, 1024, 4096, 4096, 1024, 1024, 256} + //Max SepSm CmbSm SmpImg StrImg UB SB UTxB StTxB InptAtt AccelSt + {8192, 1024, 8192, 8192, 1024, 4096, 4096, 1024, 1024, 256, 256} #endif ; @@ -2191,8 +2209,8 @@ struct EngineVkCreateInfo DILIGENT_DERIVE(EngineCreateInfo) VulkanDescriptorPoolSize DynamicDescriptorPoolSize #if DILIGENT_CPP_INTERFACE - //Max SepSm CmbSm SmpImg StrImg UB SB UTxB StTxB InptAtt - {2048, 256, 2048, 2048, 256, 1024, 1024, 256, 256, 64} + //Max SepSm CmbSm SmpImg StrImg UB SB UTxB StTxB InptAtt AccelSt + {2048, 256, 2048, 2048, 256, 1024, 1024, 256, 256, 64, 64} #endif ; @@ -2697,16 +2715,16 @@ DILIGENT_TYPED_ENUM(RESOURCE_STATE, Uint32) /// The resource state is known to the engine, but is undefined. A resource is typically in an undefined state right after initialization. RESOURCE_STATE_UNDEFINED = 0x00001, - /// The resource is accessed as vertex buffer + /// The resource is accessed as a vertex buffer RESOURCE_STATE_VERTEX_BUFFER = 0x00002, - /// The resource is accessed as constant (uniform) buffer + /// The resource is accessed as a constant (uniform) buffer RESOURCE_STATE_CONSTANT_BUFFER = 0x00004, - /// The resource is accessed as index buffer + /// The resource is accessed as an index buffer RESOURCE_STATE_INDEX_BUFFER = 0x00008, - /// The resource is accessed as render target + /// The resource is accessed as a render target RESOURCE_STATE_RENDER_TARGET = 0x00010, /// The resource is used for unordered access @@ -2724,7 +2742,7 @@ DILIGENT_TYPED_ENUM(RESOURCE_STATE, Uint32) /// The resource is used as the destination for stream output RESOURCE_STATE_STREAM_OUT = 0x00200, - /// The resource is used as indirect draw/dispatch arguments buffer + /// The resource is used as an indirect draw/dispatch arguments buffer RESOURCE_STATE_INDIRECT_ARGUMENT = 0x00400, /// The resource is used as the destination in a copy operation @@ -2739,13 +2757,23 @@ DILIGENT_TYPED_ENUM(RESOURCE_STATE, Uint32) /// The resource is used as the source in a resolve operation RESOURCE_STATE_RESOLVE_SOURCE = 0x04000, - /// The resource is used as input attachment in a render pass subpass + /// The resource is used as an input attachment in a render pass subpass RESOURCE_STATE_INPUT_ATTACHMENT = 0x08000, /// The resource is used for present RESOURCE_STATE_PRESENT = 0x10000, - RESOURCE_STATE_MAX_BIT = 0x10000, + /// The resource is used as vertex/index/instance buffer in an AS building operation + /// or as an acceleration structure source in an AS copy operation. + RESOURCE_STATE_BUILD_AS_READ = 0x20000, + + /// The resource is used as the target for AS building or AS copy operations. + RESOURCE_STATE_BUILD_AS_WRITE = 0x40000, + + /// The resource is used as a top-level AS shader resource in a trace rays operation. + RESOURCE_STATE_RAY_TRACING = 0x80000, + + RESOURCE_STATE_MAX_BIT = RESOURCE_STATE_RAY_TRACING, RESOURCE_STATE_GENERIC_READ = RESOURCE_STATE_VERTEX_BUFFER | RESOURCE_STATE_CONSTANT_BUFFER | @@ -2775,105 +2803,4 @@ DILIGENT_TYPED_ENUM(STATE_TRANSITION_TYPE, Uint8) STATE_TRANSITION_TYPE_END }; -static const Uint32 REMAINING_MIP_LEVELS = 0xFFFFFFFFU; -static const Uint32 REMAINING_ARRAY_SLICES = 0xFFFFFFFFU; - -/// Resource state transition barrier description -struct StateTransitionDesc -{ - /// Texture to transition. - /// \note Exactly one of pTexture or pBuffer must be non-null. - struct ITexture* pTexture DEFAULT_INITIALIZER(nullptr); - - /// Buffer to transition. - /// \note Exactly one of pTexture or pBuffer must be non-null. - struct IBuffer* pBuffer DEFAULT_INITIALIZER(nullptr); - - /// When transitioning a texture, first mip level of the subresource range to transition. - Uint32 FirstMipLevel DEFAULT_INITIALIZER(0); - - /// When transitioning a texture, number of mip levels of the subresource range to transition. - Uint32 MipLevelsCount DEFAULT_INITIALIZER(REMAINING_MIP_LEVELS); - - /// When transitioning a texture, first array slice of the subresource range to transition. - Uint32 FirstArraySlice DEFAULT_INITIALIZER(0); - - /// When transitioning a texture, number of array slices of the subresource range to transition. - Uint32 ArraySliceCount DEFAULT_INITIALIZER(REMAINING_ARRAY_SLICES); - - /// Resource state before transition. If this value is RESOURCE_STATE_UNKNOWN, - /// internal resource state will be used, which must be defined in this case. - RESOURCE_STATE OldState DEFAULT_INITIALIZER(RESOURCE_STATE_UNKNOWN); - - /// Resource state after transition. - RESOURCE_STATE NewState DEFAULT_INITIALIZER(RESOURCE_STATE_UNKNOWN); - - /// State transition type, see Diligent::STATE_TRANSITION_TYPE. - - /// \note When issuing UAV barrier (i.e. OldState and NewState equal RESOURCE_STATE_UNORDERED_ACCESS), - /// TransitionType must be STATE_TRANSITION_TYPE_IMMEDIATE. - STATE_TRANSITION_TYPE TransitionType DEFAULT_INITIALIZER(STATE_TRANSITION_TYPE_IMMEDIATE); - - /// If set to true, the internal resource state will be set to NewState and the engine - /// will be able to take over the resource state management. In this case it is the - /// responsibility of the application to make sure that all subresources are indeed in - /// designated state. - /// If set to false, internal resource state will be unchanged. - /// \note When TransitionType is STATE_TRANSITION_TYPE_BEGIN, this member must be false. - bool UpdateResourceState DEFAULT_INITIALIZER(false); - -#if DILIGENT_CPP_INTERFACE - StateTransitionDesc()noexcept{} - - StateTransitionDesc(ITexture* _pTexture, - RESOURCE_STATE _OldState, - RESOURCE_STATE _NewState, - Uint32 _FirstMipLevel = 0, - Uint32 _MipLevelsCount = REMAINING_MIP_LEVELS, - Uint32 _FirstArraySlice = 0, - Uint32 _ArraySliceCount = REMAINING_ARRAY_SLICES, - STATE_TRANSITION_TYPE _TransitionType = STATE_TRANSITION_TYPE_IMMEDIATE, - bool _UpdateState = false)noexcept : - pTexture {_pTexture }, - FirstMipLevel {_FirstMipLevel }, - MipLevelsCount {_MipLevelsCount }, - FirstArraySlice {_FirstArraySlice}, - ArraySliceCount {_ArraySliceCount}, - OldState {_OldState }, - NewState {_NewState }, - TransitionType {_TransitionType }, - UpdateResourceState {_UpdateState } - {} - - StateTransitionDesc(ITexture* _pTexture, - RESOURCE_STATE _OldState, - RESOURCE_STATE _NewState, - bool _UpdateState)noexcept : - StateTransitionDesc - { - _pTexture, - _OldState, - _NewState, - 0, - REMAINING_MIP_LEVELS, - 0, - REMAINING_ARRAY_SLICES, - STATE_TRANSITION_TYPE_IMMEDIATE, - _UpdateState - } - {} - - StateTransitionDesc(IBuffer* _pBuffer, - RESOURCE_STATE _OldState, - RESOURCE_STATE _NewState, - bool _UpdateState)noexcept : - pBuffer {_pBuffer }, - OldState {_OldState }, - NewState {_NewState }, - UpdateResourceState {_UpdateState} - {} -#endif -}; -typedef struct StateTransitionDesc StateTransitionDesc; - DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngine/interface/PipelineState.h b/Graphics/GraphicsEngine/interface/PipelineState.h index 2d889c61..875c6a21 100644 --- a/Graphics/GraphicsEngine/interface/PipelineState.h +++ b/Graphics/GraphicsEngine/interface/PipelineState.h @@ -152,7 +152,7 @@ typedef struct PipelineResourceLayoutDesc PipelineResourceLayoutDesc; /// Graphics pipeline state description -/// This structure describes the graphics pipeline state and is part of the PipelineStateDesc structure. +/// This structure describes the graphics pipeline state and is part of the GraphicsPipelineStateCreateInfo structure. struct GraphicsPipelineDesc { /// Blend state description. @@ -215,6 +215,107 @@ struct GraphicsPipelineDesc typedef struct GraphicsPipelineDesc GraphicsPipelineDesc; +/// Ray tracing general shader group description +struct RayTracingGeneralShaderGroup +{ + /// Unique group name. + const char* Name DEFAULT_INITIALIZER(nullptr); + + /// Shader type must be SHADER_TYPE_RAY_GEN, SHADER_TYPE_RAY_MISS or SHADER_TYPE_CALLABLE. + IShader* pShader DEFAULT_INITIALIZER(nullptr); + +#if DILIGENT_CPP_INTERFACE + RayTracingGeneralShaderGroup() noexcept + {} + + RayTracingGeneralShaderGroup(const char* _Name, + IShader* _pShader) noexcept: + Name {_Name }, + pShader{_pShader} + {} +#endif +}; +typedef struct RayTracingGeneralShaderGroup RayTracingGeneralShaderGroup; + +/// Ray tracing triangle hit shader group description. +struct RayTracingTriangleHitShaderGroup +{ + /// Unique group name. + const char* Name DEFAULT_INITIALIZER(nullptr); + + /// Closest hit shader. + /// The shader type must be SHADER_TYPE_RAY_CLOSEST_HIT. + IShader* pClosestHitShader DEFAULT_INITIALIZER(nullptr); + + /// Any-hit shader. Can be null. + /// The shader type must be SHADER_TYPE_RAY_ANY_HIT. + IShader* pAnyHitShader DEFAULT_INITIALIZER(nullptr); // can be null + +#if DILIGENT_CPP_INTERFACE + RayTracingTriangleHitShaderGroup() noexcept + {} + + RayTracingTriangleHitShaderGroup(const char* _Name, + IShader* _pClosestHitShader, + IShader* _pAnyHitShader = nullptr) noexcept: + Name {_Name }, + pClosestHitShader{_pClosestHitShader}, + pAnyHitShader {_pAnyHitShader } + {} +#endif +}; +typedef struct RayTracingTriangleHitShaderGroup RayTracingTriangleHitShaderGroup; + +/// Ray tracing procedural hit shader group description. +struct RayTracingProceduralHitShaderGroup +{ + /// Unique group name. + const char* Name DEFAULT_INITIALIZER(nullptr); + + /// Intersection shader. + /// The shader type must be SHADER_TYPE_RAY_INTERSECTION. + IShader* pIntersectionShader DEFAULT_INITIALIZER(nullptr); + + /// Closest hit shader. Can be null. + /// The shader type must be SHADER_TYPE_RAY_CLOSEST_HIT. + IShader* pClosestHitShader DEFAULT_INITIALIZER(nullptr); + + /// Any-hit shader. Can be null. + /// The shader type must be SHADER_TYPE_RAY_ANY_HIT. + IShader* pAnyHitShader DEFAULT_INITIALIZER(nullptr); + +#if DILIGENT_CPP_INTERFACE + RayTracingProceduralHitShaderGroup() noexcept + {} + + RayTracingProceduralHitShaderGroup(const char* _Name, + IShader* _pIntersectionShader, + IShader* _pClosestHitShader = nullptr, + IShader* _pAnyHitShader = nullptr) noexcept: + Name {_Name }, + pIntersectionShader{_pIntersectionShader}, + pClosestHitShader {_pClosestHitShader }, + pAnyHitShader {_pAnyHitShader } + {} +#endif +}; +typedef struct RayTracingProceduralHitShaderGroup RayTracingProceduralHitShaderGroup; + +/// This structure describes the ray tracing pipeline state and is part of the RayTracingPipelineStateCreateInfo structure. +struct RayTracingPipelineDesc +{ + /// Size of the additional data passed to the shader. + /// Shader record size plus shader group size (32 bytes) must be aligned to 32 bytes. + /// Shader record size plus shader group size (32 bytes) must not exceed 4096 bytes. + Uint16 ShaderRecordSize DEFAULT_INITIALIZER(0); + + /// Number of recursive calls of TraceRay() in HLSL or traceRay() in GLSL. + /// Zero means no tracing of rays at all, only ray-gen shader will be executed. + /// See DeviceProperties::MaxRayTracingRecursionDepth. + Uint8 MaxRecursionDepth DEFAULT_INITIALIZER(0); +}; +typedef struct RayTracingPipelineDesc RayTracingPipelineDesc; + /// Pipeline type DILIGENT_TYPED_ENUM(PIPELINE_TYPE, Uint8) { @@ -227,6 +328,11 @@ DILIGENT_TYPED_ENUM(PIPELINE_TYPE, Uint8) /// Mesh pipeline, which is used by IDeviceContext::DrawMesh(), IDeviceContext::DrawMeshIndirect(). PIPELINE_TYPE_MESH, + + /// Ray tracing pipeline, which is used by IDeviceContext::TraceRays(). + PIPELINE_TYPE_RAY_TRACING, + + PIPELINE_TYPE_LAST = PIPELINE_TYPE_RAY_TRACING }; @@ -251,6 +357,7 @@ struct PipelineStateDesc DILIGENT_DERIVE(DeviceObjectAttribs) #if DILIGENT_CPP_INTERFACE bool IsAnyGraphicsPipeline() const { return PipelineType == PIPELINE_TYPE_GRAPHICS || PipelineType == PIPELINE_TYPE_MESH; } bool IsComputePipeline() const { return PipelineType == PIPELINE_TYPE_COMPUTE; } + bool IsRayTracingPipeline() const { return PipelineType == PIPELINE_TYPE_RAY_TRACING; } #endif }; typedef struct PipelineStateDesc PipelineStateDesc; @@ -339,6 +446,48 @@ struct ComputePipelineStateCreateInfo DILIGENT_DERIVE(PipelineStateCreateInfo) typedef struct ComputePipelineStateCreateInfo ComputePipelineStateCreateInfo; +/// Ray tracing pipeline state description. +struct RayTracingPipelineStateCreateInfo DILIGENT_DERIVE(PipelineStateCreateInfo) + + /// Ray tracing pipeline description. + RayTracingPipelineDesc RayTracingPipeline; + + /// A pointer to an array of GeneralShaderCount RayTracingGeneralShaderGroup structures that contain shader group description. + const RayTracingGeneralShaderGroup* pGeneralShaders DEFAULT_INITIALIZER(nullptr); + + /// The number of general shader groups. + Uint32 GeneralShaderCount DEFAULT_INITIALIZER(0); + + /// A pointer to an array of TriangleHitShaderCount RayTracingTriangleHitShaderGroup structures that contain shader group description. + /// Can be null. + const RayTracingTriangleHitShaderGroup* pTriangleHitShaders DEFAULT_INITIALIZER(nullptr); + + /// The number of triangle hit shader groups. + Uint32 TriangleHitShaderCount DEFAULT_INITIALIZER(0); + + /// A pointer to an array of ProceduralHitShaderCount RayTracingProceduralHitShaderGroup structures that contain shader group description. + /// Can be null. + const RayTracingProceduralHitShaderGroup* pProceduralHitShaders DEFAULT_INITIALIZER(nullptr); + + /// The number of procedural shader groups. + Uint32 ProceduralHitShaderCount DEFAULT_INITIALIZER(0); + + /// 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; + + // {06084AE5-6A71-4FE8-84B9-395DD489A28C} static const struct INTERFACE_ID IID_PipelineState = {0x6084ae5, 0x6a71, 0x4fe8, {0x84, 0xb9, 0x39, 0x5d, 0xd4, 0x89, 0xa2, 0x8c}}; @@ -363,6 +512,10 @@ DILIGENT_BEGIN_INTERFACE(IPipelineState, IDeviceObject) /// Returns the graphics pipeline description used to create the object. /// This method must only be called for a graphics or mesh pipeline. VIRTUAL const GraphicsPipelineDesc REF METHOD(GetGraphicsPipelineDesc)(THIS) CONST PURE; + + /// Returns the ray tracing pipeline description used to create the object. + /// This method must only be called for a ray tracing pipeline. + VIRTUAL const RayTracingPipelineDesc REF METHOD(GetRayTracingPipelineDesc)(THIS) CONST PURE; /// Binds resources for all shaders in the pipeline state @@ -453,6 +606,7 @@ DILIGENT_END_INTERFACE # define IPipelineState_GetDesc(This) (const struct PipelineStateDesc*)IDeviceObject_GetDesc(This) # define IPipelineState_GetGraphicsPipelineDesc(This) CALL_IFACE_METHOD(PipelineState, GetGraphicsPipelineDesc, This) +# define IPipelineState_GetRayTracingPipelineDesc(This) CALL_IFACE_METHOD(PipelineState, GetRayTracingPipelineDesc, This) # define IPipelineState_BindStaticResources(This, ...) CALL_IFACE_METHOD(PipelineState, BindStaticResources, This, __VA_ARGS__) # define IPipelineState_GetStaticVariableCount(This, ...) CALL_IFACE_METHOD(PipelineState, GetStaticVariableCount, This, __VA_ARGS__) # define IPipelineState_GetStaticVariableByName(This, ...) CALL_IFACE_METHOD(PipelineState, GetStaticVariableByName, This, __VA_ARGS__) diff --git a/Graphics/GraphicsEngine/interface/RenderDevice.h b/Graphics/GraphicsEngine/interface/RenderDevice.h index 47c759db..deaaab71 100644 --- a/Graphics/GraphicsEngine/interface/RenderDevice.h +++ b/Graphics/GraphicsEngine/interface/RenderDevice.h @@ -47,6 +47,9 @@ #include "Query.h" #include "RenderPass.h" #include "Framebuffer.h" +#include "BottomLevelAS.h" +#include "TopLevelAS.h" +#include "ShaderBindingTable.h" #include "DepthStencilState.h" #include "RasterizerState.h" @@ -174,6 +177,17 @@ DILIGENT_BEGIN_INTERFACE(IRenderDevice, IObject) VIRTUAL void METHOD(CreateComputePipelineState)(THIS_ const ComputePipelineStateCreateInfo REF PSOCreateInfo, IPipelineState** ppPipelineState) PURE; + + /// Creates a new ray tracing pipeline state object + + /// \param [in] PSOCreateInfo - Ray tracing pipeline state create info, see Diligent::RayTracingPipelineStateCreateInfo for details. + /// \param [out] ppPipelineState - Address of the memory location where the pointer to the + /// pipeline state interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + VIRTUAL void METHOD(CreateRayTracingPipelineState)(THIS_ + const RayTracingPipelineStateCreateInfo REF PSOCreateInfo, + IPipelineState** ppPipelineState) PURE; /// Creates a new fence object @@ -224,8 +238,47 @@ DILIGENT_BEGIN_INTERFACE(IRenderDevice, IObject) IFramebuffer** ppFramebuffer) PURE; + /// Creates a bottom-level acceleration structure object (BLAS). + + /// \param [in] Desc - BLAS description, see Diligent::BottomLevelASDesc for details. + /// \param [out] ppBLAS - Address of the memory location where the pointer to the + /// BLAS interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + VIRTUAL void METHOD(CreateBLAS)(THIS_ + const BottomLevelASDesc REF Desc, + IBottomLevelAS** ppBLAS) PURE; + + + /// Creates a top-level acceleration structure object (TLAS). + + /// \param [in] Desc - TLAS description, see Diligent::TopLevelASDesc for details. + /// \param [out] ppTLAS - Address of the memory location where the pointer to the + /// TLAS interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + VIRTUAL void METHOD(CreateTLAS)(THIS_ + const TopLevelASDesc REF Desc, + ITopLevelAS** ppTLAS) PURE; + + + /// Creates a shader resource binding table object (SBT). + + /// \param [in] Desc - SBT description, see Diligent::ShaderBindingTableDesc for details. + /// \param [out] ppSBT - Address of the memory location where the pointer to the + /// SBT interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + VIRTUAL void METHOD(CreateSBT)(THIS_ + const ShaderBindingTableDesc REF Desc, + IShaderBindingTable** ppSBT) PURE; + + /// Gets the device capabilities, see Diligent::DeviceCaps for details VIRTUAL const DeviceCaps REF METHOD(GetDeviceCaps)(THIS) CONST PURE; + + /// Gets the device properties, see Diligent::DeviceProperties for details + VIRTUAL const DeviceProperties REF METHOD(GetDeviceProperties)(THIS) CONST PURE; /// Returns the basic texture format information. @@ -282,25 +335,24 @@ DILIGENT_END_INTERFACE #if DILIGENT_C_INTERFACE // clang-format off - -# define IRenderDevice_CreateBuffer(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateBuffer, This, __VA_ARGS__) -# define IRenderDevice_CreateShader(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateShader, This, __VA_ARGS__) -# define IRenderDevice_CreateTexture(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateTexture, This, __VA_ARGS__) -# define IRenderDevice_CreateSampler(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateSampler, This, __VA_ARGS__) -# define IRenderDevice_CreateResourceMapping(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateResourceMapping, This, __VA_ARGS__) -# define IRenderDevice_CreateGraphicsPipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateGraphicsPipelineState, This, __VA_ARGS__) -# define IRenderDevice_CreateComputePipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateComputePipelineState, This, __VA_ARGS__) -# define IRenderDevice_CreateFence(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateFence, This, __VA_ARGS__) -# define IRenderDevice_CreateQuery(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateQuery, This, __VA_ARGS__) -# define IRenderDevice_CreateRenderPass(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateRenderPass, This, __VA_ARGS__) -# define IRenderDevice_CreateFramebuffer(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateFramebuffer, This, __VA_ARGS__) -# define IRenderDevice_GetDeviceCaps(This) CALL_IFACE_METHOD(RenderDevice, GetDeviceCaps, This) -# define IRenderDevice_GetTextureFormatInfo(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfo, This, __VA_ARGS__) -# define IRenderDevice_GetTextureFormatInfoExt(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfoExt, This, __VA_ARGS__) -# define IRenderDevice_ReleaseStaleResources(This, ...) CALL_IFACE_METHOD(RenderDevice, ReleaseStaleResources, This, __VA_ARGS__) -# define IRenderDevice_IdleGPU(This) CALL_IFACE_METHOD(RenderDevice, IdleGPU, This) -# define IRenderDevice_GetEngineFactory(This) CALL_IFACE_METHOD(RenderDevice, GetEngineFactory, This) - +# define IRenderDevice_CreateBuffer(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateBuffer, This, __VA_ARGS__) +# define IRenderDevice_CreateShader(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateShader, This, __VA_ARGS__) +# define IRenderDevice_CreateTexture(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateTexture, This, __VA_ARGS__) +# define IRenderDevice_CreateSampler(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateSampler, This, __VA_ARGS__) +# define IRenderDevice_CreateResourceMapping(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateResourceMapping, This, __VA_ARGS__) +# define IRenderDevice_CreateGraphicsPipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateGraphicsPipelineState, This, __VA_ARGS__) +# define IRenderDevice_CreateComputePipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateComputePipelineState, This, __VA_ARGS__) +# define IRenderDevice_CreateRayTracingPipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateRayTracingPipelineState, This, __VA_ARGS__) +# define IRenderDevice_CreateFence(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateFence, This, __VA_ARGS__) +# define IRenderDevice_CreateQuery(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateQuery, This, __VA_ARGS__) +# define IRenderDevice_CreateRenderPass(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateRenderPass, This, __VA_ARGS__) +# define IRenderDevice_CreateFramebuffer(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateFramebuffer, This, __VA_ARGS__) +# define IRenderDevice_GetDeviceCaps(This) CALL_IFACE_METHOD(RenderDevice, GetDeviceCaps, This) +# define IRenderDevice_GetTextureFormatInfo(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfo, This, __VA_ARGS__) +# define IRenderDevice_GetTextureFormatInfoExt(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfoExt, This, __VA_ARGS__) +# define IRenderDevice_ReleaseStaleResources(This, ...) CALL_IFACE_METHOD(RenderDevice, ReleaseStaleResources, This, __VA_ARGS__) +# define IRenderDevice_IdleGPU(This) CALL_IFACE_METHOD(RenderDevice, IdleGPU, This) +# define IRenderDevice_GetEngineFactory(This) CALL_IFACE_METHOD(RenderDevice, GetEngineFactory, This) // clang-format on #endif diff --git a/Graphics/GraphicsEngine/interface/Shader.h b/Graphics/GraphicsEngine/interface/Shader.h index bb4acc84..a2bc518d 100644 --- a/Graphics/GraphicsEngine/interface/Shader.h +++ b/Graphics/GraphicsEngine/interface/Shader.h @@ -45,16 +45,22 @@ static const INTERFACE_ID IID_Shader = /// Describes the shader type DILIGENT_TYPED_ENUM(SHADER_TYPE, Uint32) { - SHADER_TYPE_UNKNOWN = 0x000, ///< Unknown shader type - SHADER_TYPE_VERTEX = 0x001, ///< Vertex shader - SHADER_TYPE_PIXEL = 0x002, ///< Pixel (fragment) shader - SHADER_TYPE_GEOMETRY = 0x004, ///< Geometry shader - SHADER_TYPE_HULL = 0x008, ///< Hull (tessellation control) shader - SHADER_TYPE_DOMAIN = 0x010, ///< Domain (tessellation evaluation) shader - SHADER_TYPE_COMPUTE = 0x020, ///< Compute shader - SHADER_TYPE_AMPLIFICATION = 0x040, ///< Amplification (task) shader - SHADER_TYPE_MESH = 0x080, ///< Mesh shader - SHADER_TYPE_LAST = SHADER_TYPE_MESH + SHADER_TYPE_UNKNOWN = 0x0000, ///< Unknown shader type + SHADER_TYPE_VERTEX = 0x0001, ///< Vertex shader + SHADER_TYPE_PIXEL = 0x0002, ///< Pixel (fragment) shader + SHADER_TYPE_GEOMETRY = 0x0004, ///< Geometry shader + SHADER_TYPE_HULL = 0x0008, ///< Hull (tessellation control) shader + SHADER_TYPE_DOMAIN = 0x0010, ///< Domain (tessellation evaluation) shader + SHADER_TYPE_COMPUTE = 0x0020, ///< Compute shader + SHADER_TYPE_AMPLIFICATION = 0x0040, ///< Amplification (task) shader + SHADER_TYPE_MESH = 0x0080, ///< Mesh shader + SHADER_TYPE_RAY_GEN = 0x0100, ///< Ray generation shader + SHADER_TYPE_RAY_MISS = 0x0200, ///< Ray miss shader + SHADER_TYPE_RAY_CLOSEST_HIT = 0x0400, ///< Ray closest hit shader + SHADER_TYPE_RAY_ANY_HIT = 0x0800, ///< Ray any hit shader + SHADER_TYPE_RAY_INTERSECTION = 0x1000, ///< Ray intersection shader + SHADER_TYPE_CALLABLE = 0x2000, ///< Callable shader + SHADER_TYPE_LAST = SHADER_TYPE_CALLABLE }; DEFINE_FLAG_ENUM_OPERATORS(SHADER_TYPE); @@ -106,6 +112,8 @@ DILIGENT_TYPED_ENUM(SHADER_COMPILER, Uint32) /// Legacy HLSL compiler (FXC) for Direct3D11 and Direct3D12 supporting shader models up to 5.1. SHADER_COMPILER_FXC, + + SHADER_COMPILER_LAST = SHADER_COMPILER_FXC }; @@ -351,7 +359,10 @@ DILIGENT_TYPED_ENUM(SHADER_RESOURCE_TYPE, Uint8) /// Input attachment in a render pass SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT, - SHADER_RESOURCE_TYPE_LAST = SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT + /// Acceleration structure + SHADER_RESOURCE_TYPE_ACCEL_STRUCT, + + SHADER_RESOURCE_TYPE_LAST = SHADER_RESOURCE_TYPE_ACCEL_STRUCT }; // clang-format on diff --git a/Graphics/GraphicsEngine/interface/ShaderBindingTable.h b/Graphics/GraphicsEngine/interface/ShaderBindingTable.h new file mode 100644 index 00000000..214b4a17 --- /dev/null +++ b/Graphics/GraphicsEngine/interface/ShaderBindingTable.h @@ -0,0 +1,282 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::IShaderBindingTable interface and related data structures + +#include "../../../Primitives/interface/Object.h" +#include "../../../Primitives/interface/FlagEnum.h" +#include "GraphicsTypes.h" +#include "Constants.h" +#include "Buffer.h" +#include "PipelineState.h" +#include "TopLevelAS.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {1EE12101-7010-4825-AA8E-AC6BB9858BD6} +static const INTERFACE_ID IID_ShaderBindingTable = + {0x1ee12101, 0x7010, 0x4825, {0xaa, 0x8e, 0xac, 0x6b, 0xb9, 0x85, 0x8b, 0xd6}}; + +// clang-format off + +/// Shader binding table description. +struct ShaderBindingTableDesc DILIGENT_DERIVE(DeviceObjectAttribs) + + /// Ray tracing pipeline state object from which shaders will be taken. + IPipelineState* pPSO DEFAULT_INITIALIZER(nullptr); + +#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 bound or inactive. + SHADER_BINDING_VALIDATION_SHADER_ONLY = 0x1, + + /// Checks that shader record data are initialized. + SHADER_BINDING_VALIDATION_SHADER_RECORD = 0x2, + + /// Checks that all TLAS that used in IShaderBindingTable::BindHitGroup() are alive and + /// shader binding indices have not changed. + SHADER_BINDING_VALIDATION_TLAS = 0x4, + + // Enable all validations. + 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) + + +#define DILIGENT_INTERFACE_NAME IShaderBindingTable +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IShaderBindingTableInclusiveMethods \ + IDeviceObjectInclusiveMethods; \ + IShaderBindingTableMethods ShaderBindingTable + +/// Shader binding table interface + +/// Defines the methods to manipulate a SBT object +DILIGENT_BEGIN_INTERFACE(IShaderBindingTable, IDeviceObject) +{ +#if DILIGENT_CPP_INTERFACE + /// Returns the shader binding table description used to create the object + virtual const ShaderBindingTableDesc& DILIGENT_CALL_TYPE GetDesc() const override = 0; +#endif + + /// Check that all shaders are bound, instances and geometries are not changed, shader record data are initialized. + + /// \param [in] Flags - Flags used for validation. + /// \return True if SBT content is valid. + /// + /// \note Access to the SBT must be externally synchronized. + /// This method implemented only for development build and has no effect in release build. + VIRTUAL Bool METHOD(Verify)(THIS_ + SHADER_BINDING_VALIDATION_FLAGS Flags) CONST PURE; + + + /// Reset SBT with the new pipeline state. This is more effecient than creating a new SBT. + + /// \note Access to the SBT must be externally synchronized. + VIRTUAL void METHOD(Reset)(THIS_ + IPipelineState* pPSO) PURE; + + + /// When TLAS or BLAS was rebuilt or updated, hit group shader bindings may have become invalid, + /// you can reset only hit groups and keep ray-gen, miss and callable shader bindings intact. + + /// \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 was specified in RayTracingGeneralShaderGroup::Name. + /// \param [in] pData - Shader record data, can be null. + /// \param [in] DataSize - Shader record data size, should be equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. + VIRTUAL void METHOD(BindRayGenShader)(THIS_ + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), + Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; + + + /// Bind ray-miss shader. + + /// \param [in] pShaderGroupName - Ray-miss shader name that was 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 the 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 be equal to RayTracingPipelineDesc::ShaderRecordSize. + /// + /// \note Access to the SBT must be externally synchronized. + VIRTUAL void METHOD(BindMissShader)(THIS_ + const char* pShaderGroupName, + Uint32 MissIndex, + const void* pData DEFAULT_INITIALIZER(nullptr), + Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; + + + /// Bind hit group for the 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 the 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 was specified in RayTracingTriangleHitShaderGroup::Name and RayTracingProceduralHitShaderGroup::Name, + /// can be null to make the shader inactive. + /// \param [in] pData - Shader record data, can be null. + /// \param [in] DataSize - Shader record data size, should be 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* pInstanceName, + const char* pGeometryName, + Uint32 RayOffsetInHitGroupIndex, + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), + Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; + + + /// Bind hit group to specified location. + + /// \param [in] BindingIndex - location of the hit group. + /// \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. + /// + /// \remarks Use IBottomLevelAS::GetGeometryIndex(), ITopLevelAS::GetBuildInfo(), ITopLevelAS::GetInstanceDesc().ContributionToHitGroupIndex to calculate binding index. + VIRTUAL void METHOD(BindHitGroupByIndex)(THIS_ + Uint32 BindingIndex, + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), + Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; + + + /// 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 the 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 was 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* pInstanceName, + Uint32 RayOffsetInHitGroupIndex, + const char* pShaderGroupName, + const void* pData DEFAULT_INITIALIZER(nullptr), + Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; + + + /// 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 the 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 was 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 the 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* pShaderGroupName, + Uint32 CallableIndex, + const void* pData DEFAULT_INITIALIZER(nullptr), + Uint32 DataSize DEFAULT_INITIALIZER(0)) PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +# 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) +# 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_BindHitGroupByIndex(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroupByIndex, 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__) + +// clang-format on + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngine/interface/TopLevelAS.h b/Graphics/GraphicsEngine/interface/TopLevelAS.h new file mode 100644 index 00000000..272f6c61 --- /dev/null +++ b/Graphics/GraphicsEngine/interface/TopLevelAS.h @@ -0,0 +1,218 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::ITopLevelAS interface and related data structures + +#include "../../../Primitives/interface/Object.h" +#include "../../../Primitives/interface/FlagEnum.h" +#include "GraphicsTypes.h" +#include "Constants.h" +#include "Buffer.h" +#include "BottomLevelAS.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {16561861-294B-4804-96FA-1717333F769A} +static const INTERFACE_ID IID_TopLevelAS = + {0x16561861, 0x294b, 0x4804, {0x96, 0xfa, 0x17, 0x17, 0x33, 0x3f, 0x76, 0x9a}}; + +// clang-format off + +/// Top-level AS description. +struct TopLevelASDesc DILIGENT_DERIVE(DeviceObjectAttribs) + + /// Allocate space for specified number of instances. + Uint32 MaxInstanceCount DEFAULT_INITIALIZER(0); + + /// Ray tracing build flags, see Diligent::RAYTRACING_BUILD_AS_FLAGS. + 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 command (IDeviceContext::CopyTLAS() with COPY_AS_MODE_COMPACT). + Uint32 CompactedSize DEFAULT_INITIALIZER(0); + + /// Defines which command queues this BLAS can be used with. + Uint64 CommandQueueMask DEFAULT_INITIALIZER(1); + +#if DILIGENT_CPP_INTERFACE + TopLevelASDesc() noexcept {} +#endif +}; +typedef struct TopLevelASDesc TopLevelASDesc; + + +/// Defines shader binding mode. +DILIGENT_TYPED_ENUM(HIT_GROUP_BINDING_MODE, Uint8) +{ + /// Reserve space for actual geometry count for each instance in TLAS. + /// Each geometry can have unique hit shader group. + /// See IShaderBindingTable::BindHitGroup(). + HIT_GROUP_BINDING_MODE_PER_GEOMETRY = 0, + + /// Reserve space for each instance in TLAS so each instance can have a unique hit shader group. + /// In this mode SBT buffer will use less memory. See IShaderBindingTable::BindHitGroups(). + HIT_GROUP_BINDING_MODE_PER_INSTANCE, + + /// Reserve space for single hit group for each TLAS. + /// See IShaderBindingTable::BindHitGroupForAll(). + HIT_GROUP_BINDING_MODE_PER_ACCEL_STRUCT, + + /// The user must specify TLASBuildInstanceData::ContributionToHitGroupIndex and only use IShaderBindingTable::BindHitGroupByIndex(). + HIT_GROUP_BINDING_MODE_USER_DEFINED, + + HIT_GROUP_BINDING_MODE_LAST = HIT_GROUP_BINDING_MODE_USER_DEFINED, +}; + + +/// Defines TLAS state that was used in the last build. +struct TLASBuildInfo +{ + /// The number of instances, same as BuildTLASAttribs::InstanceCount. + Uint32 InstanceCount DEFAULT_INITIALIZER(0); + + /// The number of hit shader groups, same as BuildTLASAttribs::HitGroupStride. + Uint32 HitGroupStride DEFAULT_INITIALIZER(0); + + /// Hit shader binding mode, same as BuildTLASAttribs::BindingMode. + HIT_GROUP_BINDING_MODE BindingMode DEFAULT_INITIALIZER(HIT_GROUP_BINDING_MODE_PER_GEOMETRY); + + /// First hit group location, same as BuildTLASAttribs::BaseContributionToHitGroupIndex. + Uint32 FirstContributionToHitGroupIndex DEFAULT_INITIALIZER(0); + + /// Last hit group location. + Uint32 LastContributionToHitGroupIndex DEFAULT_INITIALIZER(0); +}; +typedef struct TLASBuildInfo TLASBuildInfo; + + +/// Top-level AS instance description. +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); + +#if DILIGENT_CPP_INTERFACE + TLASInstanceDesc() noexcept {} +#endif +}; +typedef struct TLASInstanceDesc TLASInstanceDesc; + + +#define DILIGENT_INTERFACE_NAME ITopLevelAS +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define ITopLevelASInclusiveMethods \ + IDeviceObjectInclusiveMethods; \ + ITopLevelASMethods TopLevelAS + +/// Top-level AS interface + +/// Defines the methods to manipulate a TLAS object +DILIGENT_BEGIN_INTERFACE(ITopLevelAS, IDeviceObject) +{ +#if DILIGENT_CPP_INTERFACE + /// Returns the top level AS description used to create the object + virtual const TopLevelASDesc& DILIGENT_CALL_TYPE GetDesc() const override = 0; +#endif + + /// Returns instance description that can be used in shader binding table. + + /// \param [in] Name - Instance name that is specified in TLASBuildInstanceData::InstanceName. + /// \return TLASInstanceDesc object, see Diligent::TLASInstanceDesc. + /// If instance does not exist then TLASInstanceDesc::ContributionToHitGroupIndex + /// and TLASInstanceDesc::InstanceIndex are set to INVALID_INDEX. + /// + /// \note Access to the TLAS must be externally synchronized. + VIRTUAL TLASInstanceDesc METHOD(GetInstanceDesc)(THIS_ + const char* Name) CONST PURE; + + + /// Returns TLAS state after the last build or update operation. + + /// \return TLASBuildInfo object, see Diligent::TLASBuildInfo. + /// + /// \note Access to the TLAS must be externally synchronized. + VIRTUAL TLASBuildInfo METHOD(GetBuildInfo)(THIS) CONST PURE; + + + /// Returns scratch buffer info for the current acceleration structure. + + /// \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 + /// 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 + /// resets the internal acceleration structure state to the given value. + /// This method should be used after the application finished + /// manually managing the acceleration structure state and wants to hand over + /// state management back to the engine. + VIRTUAL void METHOD(SetState)(THIS_ + RESOURCE_STATE State) PURE; + + + /// Returns the internal acceleration structure state + VIRTUAL RESOURCE_STATE METHOD(GetState)(THIS) CONST PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +# define ITopLevelAS_GetInstanceDesc(This, ...) CALL_IFACE_METHOD(TopLevelAS, GetInstanceDesc, This, __VA_ARGS__) +# define ITopLevelAS_GetBuildInfo(This) CALL_IFACE_METHOD(TopLevelAS, GetBuildInfo, This) +# 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 + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngine/src/APIInfo.cpp b/Graphics/GraphicsEngine/src/APIInfo.cpp index e4111327..4facdec4 100644 --- a/Graphics/GraphicsEngine/src/APIInfo.cpp +++ b/Graphics/GraphicsEngine/src/APIInfo.cpp @@ -89,10 +89,12 @@ static APIInfo InitAPIInfo() INIT_STRUCTURE_SIZE(ShaderResourceVariableDesc); INIT_STRUCTURE_SIZE(ImmutableSamplerDesc); INIT_STRUCTURE_SIZE(PipelineResourceLayoutDesc); + INIT_STRUCTURE_SIZE(PipelineStateDesc); INIT_STRUCTURE_SIZE(GraphicsPipelineDesc); INIT_STRUCTURE_SIZE(GraphicsPipelineStateCreateInfo); INIT_STRUCTURE_SIZE(ComputePipelineStateCreateInfo); - INIT_STRUCTURE_SIZE(PipelineStateDesc); + INIT_STRUCTURE_SIZE(RayTracingPipelineDesc); + INIT_STRUCTURE_SIZE(RayTracingPipelineStateCreateInfo); INIT_STRUCTURE_SIZE(RasterizerStateDesc); INIT_STRUCTURE_SIZE(ResourceMappingEntry); INIT_STRUCTURE_SIZE(ResourceMappingDesc); diff --git a/Graphics/GraphicsEngine/src/BottomLevelASBase.cpp b/Graphics/GraphicsEngine/src/BottomLevelASBase.cpp new file mode 100644 index 00000000..3d6e4fbc --- /dev/null +++ b/Graphics/GraphicsEngine/src/BottomLevelASBase.cpp @@ -0,0 +1,188 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "BottomLevelASBase.hpp" + +namespace Diligent +{ + +void ValidateBottomLevelASDesc(const BottomLevelASDesc& Desc) noexcept(false) +{ +#define LOG_BLAS_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of a bottom-level AS '", (Desc.Name ? Desc.Name : ""), "' is invalid: ", ##__VA_ARGS__) + + if (Desc.CompactedSize > 0) + { + if (Desc.pTriangles != nullptr || Desc.pBoxes != nullptr) + LOG_BLAS_ERROR_AND_THROW("If non-zero CompactedSize is specified, pTriangles and pBoxes must both be null"); + + if (Desc.Flags != RAYTRACING_BUILD_AS_NONE) + LOG_BLAS_ERROR_AND_THROW("If non-zero CompactedSize is specified, Flags must be RAYTRACING_BUILD_AS_NONE"); + } + else + { + if (!((Desc.pBoxes != nullptr) ^ (Desc.pTriangles != nullptr))) + LOG_BLAS_ERROR_AND_THROW("Exactly one of pTriangles and pBoxes must be defined"); + + if (Desc.pBoxes == nullptr && Desc.BoxCount > 0) + LOG_BLAS_ERROR_AND_THROW("pBoxes is null, but BoxCount is not 0"); + + if (Desc.pTriangles == nullptr && Desc.TriangleCount > 0) + LOG_BLAS_ERROR_AND_THROW("pTriangles is null, but TriangleCount is not 0"); + + if ((Desc.Flags & RAYTRACING_BUILD_AS_PREFER_FAST_TRACE) != 0 && (Desc.Flags & RAYTRACING_BUILD_AS_PREFER_FAST_BUILD) != 0) + LOG_BLAS_ERROR_AND_THROW("RAYTRACING_BUILD_AS_PREFER_FAST_TRACE and RAYTRACING_BUILD_AS_PREFER_FAST_BUILD flags are mutually exclusive"); + + for (Uint32 i = 0; i < Desc.TriangleCount; ++i) + { + const auto& tri = Desc.pTriangles[i]; + + if (tri.GeometryName == nullptr) + LOG_BLAS_ERROR_AND_THROW("pTriangles[", i, "].GeometryName must not be null"); + + if (tri.VertexValueType != VT_FLOAT32 && tri.VertexValueType != VT_FLOAT16 && tri.VertexValueType != VT_INT16) + LOG_BLAS_ERROR_AND_THROW("pTriangles[", i, "].VertexValueType (", GetValueTypeString(tri.VertexValueType), + ") is invalid. Only the following values are allowed: VT_FLOAT32, VT_FLOAT16, VT_INT16"); + + if (tri.VertexComponentCount != 2 && tri.VertexComponentCount != 3) + LOG_BLAS_ERROR_AND_THROW("pTriangles[", i, "].VertexComponentCount (", tri.VertexComponentCount, ") is invalid. Only 2 or 3 are allowed."); + + if (tri.MaxVertexCount == 0) + LOG_BLAS_ERROR_AND_THROW("pTriangles[", i, "].MaxVertexCount must be greater than 0"); + + if (tri.MaxPrimitiveCount == 0) + LOG_BLAS_ERROR_AND_THROW("pTriangles[", i, "].MaxPrimitiveCount must be greater than 0"); + + if (tri.IndexType == VT_UNDEFINED) + { + if (tri.MaxVertexCount != tri.MaxPrimitiveCount * 3) + LOG_BLAS_ERROR_AND_THROW("pTriangles[", i, "].MaxVertexCount must equal to (MaxPrimitiveCount * 3)"); + } + else + { + if (tri.IndexType != VT_UINT32 && tri.IndexType != VT_UINT16) + LOG_BLAS_ERROR_AND_THROW("pTriangles[", i, "].IndexType must be VT_UINT16 or VT_UINT32"); + } + } + + for (Uint32 i = 0; i < Desc.BoxCount; ++i) + { + const auto& box = Desc.pBoxes[i]; + + if (box.GeometryName == nullptr) + LOG_BLAS_ERROR_AND_THROW("pBoxes[", i, "].GeometryName must not be null"); + + if (box.MaxBoxCount == 0) + LOG_BLAS_ERROR_AND_THROW("pBoxes[", i, "].MaxBoxCount must be greater than 0"); + } + } + +#undef LOG_BLAS_ERROR_AND_THROW +} + +void CopyBLASGeometryDesc(const BottomLevelASDesc& SrcDesc, + BottomLevelASDesc& DstDesc, + FixedLinearAllocator& MemPool, + const BLASNameToIndex* pSrcNameToIndex, + BLASNameToIndex& DstNameToIndex) noexcept(false) +{ + if (SrcDesc.pTriangles != nullptr) + { + MemPool.AddSpace<decltype(*SrcDesc.pTriangles)>(SrcDesc.TriangleCount); + + for (Uint32 i = 0; i < SrcDesc.TriangleCount; ++i) + MemPool.AddSpaceForString(SrcDesc.pTriangles[i].GeometryName); + + MemPool.Reserve(); + + auto* pTriangles = MemPool.CopyArray(SrcDesc.pTriangles, SrcDesc.TriangleCount); + + // Copy strings + for (Uint32 i = 0; i < SrcDesc.TriangleCount; ++i) + { + const auto* SrcGeoName = SrcDesc.pTriangles[i].GeometryName; + pTriangles[i].GeometryName = MemPool.CopyString(SrcGeoName); + 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.TriangleCount = SrcDesc.TriangleCount; + DstDesc.pBoxes = nullptr; + DstDesc.BoxCount = 0; + } + else if (SrcDesc.pBoxes != nullptr) + { + MemPool.AddSpace<decltype(*SrcDesc.pBoxes)>(SrcDesc.BoxCount); + + for (Uint32 i = 0; i < SrcDesc.BoxCount; ++i) + MemPool.AddSpaceForString(SrcDesc.pBoxes[i].GeometryName); + + MemPool.Reserve(); + + auto* pBoxes = MemPool.CopyArray(SrcDesc.pBoxes, SrcDesc.BoxCount); + + // Copy strings + for (Uint32 i = 0; i < SrcDesc.BoxCount; ++i) + { + const auto* SrcGeoName = SrcDesc.pBoxes[i].GeometryName; + pBoxes[i].GeometryName = MemPool.CopyString(SrcGeoName); + 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; + } + else + { + LOG_ERROR_AND_THROW("Either pTriangles or pBoxes must not be null"); + } +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/src/BufferBase.cpp b/Graphics/GraphicsEngine/src/BufferBase.cpp index 0239ed92..5156764b 100644 --- a/Graphics/GraphicsEngine/src/BufferBase.cpp +++ b/Graphics/GraphicsEngine/src/BufferBase.cpp @@ -25,7 +25,6 @@ * of the possibility of such damages. */ -#include "pch.h" #include "Buffer.h" #include "GraphicsAccessories.hpp" @@ -43,8 +42,10 @@ namespace Diligent } while (false) -void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps) +void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps) noexcept(false) { + static_assert(BIND_FLAGS_LAST == 0x400L, "Please update this function to handle the new bind flags"); + constexpr Uint32 AllowedBindFlags = BIND_VERTEX_BUFFER | BIND_INDEX_BUFFER | @@ -52,7 +53,8 @@ void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps) BIND_SHADER_RESOURCE | BIND_STREAM_OUTPUT | BIND_UNORDERED_ACCESS | - BIND_INDIRECT_DRAW_ARGS; + BIND_INDIRECT_DRAW_ARGS | + BIND_RAY_TRACING; VERIFY_BUFFER((Desc.BindFlags & ~AllowedBindFlags) == 0, "the following bind flags are not allowed for a buffer: ", GetBindFlagsString(Desc.BindFlags & ~AllowedBindFlags, ", "), '.'); @@ -112,7 +114,7 @@ void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps) } } -void ValidateBufferInitData(const BufferDesc& Desc, const BufferData* pBuffData) +void ValidateBufferInitData(const BufferDesc& Desc, const BufferData* pBuffData) noexcept(false) { if (Desc.Usage == USAGE_IMMUTABLE && (pBuffData == nullptr || pBuffData->pData == nullptr)) LOG_BUFFER_ERROR_AND_THROW("initial data must not be null as immutable buffers must be initialized at creation time."); @@ -140,4 +142,51 @@ void ValidateBufferInitData(const BufferDesc& Desc, const BufferData* pBuffData) #undef VERIFY_BUFFER #undef LOG_BUFFER_ERROR_AND_THROW +void ValidateAndCorrectBufferViewDesc(const BufferDesc& BuffDesc, BufferViewDesc& ViewDesc) noexcept(false) +{ + if (ViewDesc.ByteWidth == 0) + { + DEV_CHECK_ERR(BuffDesc.uiSizeInBytes > ViewDesc.ByteOffset, "Byte offset (", ViewDesc.ByteOffset, ") exceeds buffer size (", BuffDesc.uiSizeInBytes, ")"); + ViewDesc.ByteWidth = BuffDesc.uiSizeInBytes - ViewDesc.ByteOffset; + } + + if (ViewDesc.ByteOffset + ViewDesc.ByteWidth > BuffDesc.uiSizeInBytes) + LOG_ERROR_AND_THROW("Buffer view range [", ViewDesc.ByteOffset, ", ", ViewDesc.ByteOffset + ViewDesc.ByteWidth, ") is out of the buffer boundaries [0, ", BuffDesc.uiSizeInBytes, ")."); + + if ((BuffDesc.BindFlags & BIND_UNORDERED_ACCESS) || + (BuffDesc.BindFlags & BIND_SHADER_RESOURCE)) + { + if (BuffDesc.Mode == BUFFER_MODE_STRUCTURED || BuffDesc.Mode == BUFFER_MODE_FORMATTED) + { + VERIFY(BuffDesc.ElementByteStride != 0, "Element byte stride is zero"); + if ((ViewDesc.ByteOffset % BuffDesc.ElementByteStride) != 0) + LOG_ERROR_AND_THROW("Buffer view byte offset (", ViewDesc.ByteOffset, ") is not a multiple of element byte stride (", BuffDesc.ElementByteStride, ")."); + if ((ViewDesc.ByteWidth % BuffDesc.ElementByteStride) != 0) + LOG_ERROR_AND_THROW("Buffer view byte width (", ViewDesc.ByteWidth, ") is not a multiple of element byte stride (", BuffDesc.ElementByteStride, ")."); + } + + if (BuffDesc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType == VT_UNDEFINED) + LOG_ERROR_AND_THROW("Format must be specified when creating a view of a formatted buffer"); + + if (BuffDesc.Mode == BUFFER_MODE_FORMATTED || (BuffDesc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType != VT_UNDEFINED)) + { + if (ViewDesc.Format.NumComponents <= 0 || ViewDesc.Format.NumComponents > 4) + LOG_ERROR_AND_THROW("Incorrect number of components (", Uint32{ViewDesc.Format.NumComponents}, "). 1, 2, 3, or 4 are allowed values"); + if (ViewDesc.Format.ValueType == VT_FLOAT32 || ViewDesc.Format.ValueType == VT_FLOAT16) + ViewDesc.Format.IsNormalized = false; + auto ViewElementStride = GetValueSize(ViewDesc.Format.ValueType) * Uint32{ViewDesc.Format.NumComponents}; + if (BuffDesc.Mode == BUFFER_MODE_RAW && BuffDesc.ElementByteStride == 0) + LOG_ERROR_AND_THROW("To enable formatted views of a raw buffer, element byte must be specified during buffer initialization"); + if (ViewElementStride != BuffDesc.ElementByteStride) + LOG_ERROR_AND_THROW("Buffer element byte stride (", BuffDesc.ElementByteStride, ") is not consistent with the size (", ViewElementStride, ") defined by the format of the view (", GetBufferFormatString(ViewDesc.Format), ')'); + } + + if (BuffDesc.Mode == BUFFER_MODE_RAW && ViewDesc.Format.ValueType == VT_UNDEFINED) + { + if ((ViewDesc.ByteOffset % 16) != 0) + LOG_ERROR_AND_THROW("When creating a RAW view, the offset of the first element from the start of the buffer (", ViewDesc.ByteOffset, ") must be a multiple of 16 bytes"); + } + } +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngine/src/DefaultShaderSourceStreamFactory.cpp b/Graphics/GraphicsEngine/src/DefaultShaderSourceStreamFactory.cpp index b14a1707..d9ab87ea 100644 --- a/Graphics/GraphicsEngine/src/DefaultShaderSourceStreamFactory.cpp +++ b/Graphics/GraphicsEngine/src/DefaultShaderSourceStreamFactory.cpp @@ -25,7 +25,6 @@ * of the possibility of such damages. */ -#include "pch.h" #include "DefaultShaderSourceStreamFactory.h" #include "ObjectBase.hpp" #include "RefCntAutoPtr.hpp" diff --git a/Graphics/GraphicsEngine/src/DeviceContextBase.cpp b/Graphics/GraphicsEngine/src/DeviceContextBase.cpp new file mode 100644 index 00000000..72a05729 --- /dev/null +++ b/Graphics/GraphicsEngine/src/DeviceContextBase.cpp @@ -0,0 +1,776 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "DeviceContextBase.hpp" +#include "GraphicsAccessories.hpp" + +namespace Diligent +{ + +#if DILIGENT_DEBUG + +# define CHECK_PARAMETER(Expr, ...) \ + do \ + { \ + VERIFY(Expr, __VA_ARGS__); \ + if (!(Expr)) return false; \ + } while (false) + +#else + +# define CHECK_PARAMETER(Expr, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + LOG_ERROR_MESSAGE(__VA_ARGS__); \ + return false; \ + } \ + } while (false) + +#endif + +bool VerifyDrawAttribs(const DrawAttribs& Attribs) +{ +#define CHECK_DRAW_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw attribs are invalid: ", __VA_ARGS__) + + CHECK_DRAW_ATTRIBS(Attribs.NumVertices != 0, "NumVertices must not be zero."); + +#undef CHECK_DRAW_ATTRIBS + + return true; +} + +bool VerifyDrawIndexedAttribs(const DrawIndexedAttribs& Attribs) +{ +#define CHECK_DRAW_INDEXED_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw indexed attribs are invalid: ", __VA_ARGS__) + + CHECK_DRAW_INDEXED_ATTRIBS(Attribs.IndexType == VT_UINT16 || Attribs.IndexType == VT_UINT32, + "IndexType (", GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); + + CHECK_DRAW_INDEXED_ATTRIBS(Attribs.NumIndices != 0, "NumIndices must not be zero."); + +#undef CHECK_DRAW_INDEXED_ATTRIBS + + return true; +} + +bool VerifyDrawMeshAttribs(Uint32 MaxDrawMeshTasksCount, const DrawMeshAttribs& Attribs) +{ +#define CHECK_DRAW_MESH_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw mesh attribs are invalid: ", __VA_ARGS__) + + CHECK_DRAW_MESH_ATTRIBS(Attribs.ThreadGroupCount != 0, "ThreadGroupCount must not be zero."); + CHECK_DRAW_MESH_ATTRIBS(Attribs.ThreadGroupCount <= MaxDrawMeshTasksCount, + "ThreadGroupCount (", Attribs.ThreadGroupCount, ") must not exceed ", MaxDrawMeshTasksCount); + +#undef CHECK_DRAW_MESH_ATTRIBS + + return true; +} + +bool VerifyDrawIndirectAttribs(const DrawIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) +{ +#define CHECK_DRAW_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw indirect attribs are invalid: ", __VA_ARGS__) + + CHECK_DRAW_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect draw arguments buffer must not be null."); + CHECK_DRAW_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0, + "indirect draw arguments buffer '", pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); + +#undef CHECK_DRAW_INDIRECT_ATTRIBS + + return true; +} + +bool VerifyDrawIndexedIndirectAttribs(const DrawIndexedIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) +{ +#define CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw indexed indirect attribs are invalid: ", __VA_ARGS__) + + CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect draw arguments buffer must not null."); + CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS(Attribs.IndexType == VT_UINT16 || Attribs.IndexType == VT_UINT32, + "IndexType (", GetValueTypeString(Attribs.IndexType), ") must be VT_UINT16 or VT_UINT32."); + CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0, + "indirect draw arguments buffer '", + pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); + +#undef CHECK_DRAW_INDEXED_INDIRECT_ATTRIBS + + return true; +} + +bool VerifyDrawMeshIndirectAttribs(const DrawMeshIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) +{ +#define CHECK_DRAW_MESH_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Draw mesh indirect attribs are invalid: ", __VA_ARGS__) + + CHECK_DRAW_MESH_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect draw arguments buffer must not be null."); + CHECK_DRAW_MESH_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0, + "indirect draw arguments buffer '", pAttribsBuffer->GetDesc().Name, + "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); + +#undef CHECK_DRAW_MESH_INDIRECT_ATTRIBS + + return true; +} + + +bool VerifyDispatchComputeAttribs(const DispatchComputeAttribs& Attribs) +{ +#define CHECK_DISPATCH_COMPUTE_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Dispatch compute attribs are invalid: ", __VA_ARGS__) + + CHECK_DISPATCH_COMPUTE_ATTRIBS(Attribs.ThreadGroupCountX != 0, "ThreadGroupCountX must no be zero."); + CHECK_DISPATCH_COMPUTE_ATTRIBS(Attribs.ThreadGroupCountY != 0, "ThreadGroupCountY must no be zero."); + CHECK_DISPATCH_COMPUTE_ATTRIBS(Attribs.ThreadGroupCountZ != 0, "ThreadGroupCountZ must no be zero."); + +#undef CHECK_DISPATCH_COMPUTE_ATTRIBS + + return true; +} + +bool VerifyDispatchComputeIndirectAttribs(const DispatchComputeIndirectAttribs& Attribs, const IBuffer* pAttribsBuffer) +{ +#define CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Dispatch compute indirect attribs are invalid: ", __VA_ARGS__) + + CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS(pAttribsBuffer != nullptr, "indirect dispatch arguments buffer must not be null."); + CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS((pAttribsBuffer->GetDesc().BindFlags & BIND_INDIRECT_DRAW_ARGS) != 0, + "indirect dispatch arguments buffer '", + pAttribsBuffer->GetDesc().Name, "' was not created with BIND_INDIRECT_DRAW_ARGS flag."); + +#undef CHECK_DISPATCH_COMPUTE_INDIRECT_ATTRIBS + + return true; +} + +bool VerifyResolveTextureSubresourceAttribs(const ResolveTextureSubresourceAttribs& ResolveAttribs, + const TextureDesc& SrcTexDesc, + const TextureDesc& DstTexDesc) +{ +#define CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Resolve texture subresource attribs are invalid: ", __VA_ARGS__) + + CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(SrcTexDesc.SampleCount > 1, "source texture '", SrcTexDesc.Name, "' of a resolve operation is not multi-sampled"); + CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(DstTexDesc.SampleCount == 1, "destination texture '", DstTexDesc.Name, "' of a resolve operation is multi-sampled"); + + auto SrcMipLevelProps = GetMipLevelProperties(SrcTexDesc, ResolveAttribs.SrcMipLevel); + auto DstMipLevelProps = GetMipLevelProperties(DstTexDesc, ResolveAttribs.DstMipLevel); + CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(SrcMipLevelProps.LogicalWidth == DstMipLevelProps.LogicalWidth && SrcMipLevelProps.LogicalHeight == DstMipLevelProps.LogicalHeight, + "the size (", SrcMipLevelProps.LogicalWidth, "x", SrcMipLevelProps.LogicalHeight, + ") of the source subresource of a resolve operation (texture '", + SrcTexDesc.Name, "', mip ", ResolveAttribs.SrcMipLevel, ", slice ", ResolveAttribs.SrcSlice, + ") does not match the size (", DstMipLevelProps.LogicalWidth, "x", DstMipLevelProps.LogicalHeight, + ") of the destination subresource (texture '", DstTexDesc.Name, "', mip ", ResolveAttribs.DstMipLevel, ", slice ", + ResolveAttribs.DstSlice, ")"); + + const auto& SrcFmtAttribs = GetTextureFormatAttribs(SrcTexDesc.Format); + const auto& DstFmtAttribs = GetTextureFormatAttribs(DstTexDesc.Format); + const auto& ResolveFmtAttribs = GetTextureFormatAttribs(ResolveAttribs.Format); + if (!SrcFmtAttribs.IsTypeless && !DstFmtAttribs.IsTypeless) + { + CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(SrcTexDesc.Format == DstTexDesc.Format, + "source (", SrcFmtAttribs.Name, ") and destination (", DstFmtAttribs.Name, + ") texture formats of a resolve operation must match exaclty or be compatible typeless formats"); + CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(ResolveAttribs.Format == TEX_FORMAT_UNKNOWN || SrcTexDesc.Format == ResolveAttribs.Format, "Invalid format of a resolve operation"); + } + if (SrcFmtAttribs.IsTypeless && DstFmtAttribs.IsTypeless) + { + CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(ResolveAttribs.Format != TEX_FORMAT_UNKNOWN, + "format of a resolve operation must not be unknown when both src and dst texture formats are typeless"); + } + if (SrcFmtAttribs.IsTypeless || DstFmtAttribs.IsTypeless) + { + CHECK_RESOLVE_TEX_SUBRES_ATTRIBS(!ResolveFmtAttribs.IsTypeless, + "format of a resolve operation must not be typeless when one of the texture formats is typeless"); + } +#undef CHECK_RESOLVE_TEX_SUBRES_ATTRIBS + + return true; +} + +bool VerifyBeginRenderPassAttribs(const BeginRenderPassAttribs& Attribs) +{ +#define CHECK_BEGIN_RENDER_PASS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Begin render pass attribs are invalid: ", __VA_ARGS__) + + CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.pRenderPass != nullptr, "pRenderPass pass must not be null"); + CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.pFramebuffer != nullptr, "pFramebuffer must not be null"); + + const auto& RPDesc = Attribs.pRenderPass->GetDesc(); + + Uint32 NumRequiredClearValues = 0; + for (Uint32 i = 0; i < RPDesc.AttachmentCount; ++i) + { + const auto& Attchmnt = RPDesc.pAttachments[i]; + if (Attchmnt.LoadOp == ATTACHMENT_LOAD_OP_CLEAR) + NumRequiredClearValues = i + 1; + + const auto& FmtAttribs = GetTextureFormatAttribs(Attchmnt.Format); + if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL) + { + if (Attchmnt.StencilLoadOp == ATTACHMENT_LOAD_OP_CLEAR) + NumRequiredClearValues = i + 1; + } + } + + CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.ClearValueCount >= NumRequiredClearValues, + "at least ", NumRequiredClearValues, " clear values are required, but only ", + Uint32{Attribs.ClearValueCount}, " are provided."); + CHECK_BEGIN_RENDER_PASS_ATTRIBS(Attribs.ClearValueCount == 0 || Attribs.pClearValues != nullptr, + "pClearValues must not be null when ClearValueCount (", Attribs.ClearValueCount, ") is not zero"); + +#undef CHECK_BEGIN_RENDER_PASS_ATTRIBS + + return true; +} + +bool VerifyStateTransitionDesc(const IRenderDevice* pDevice, const StateTransitionDesc& Barrier) +{ +#define CHECK_STATE_TRANSITION_DESC(Expr, ...) CHECK_PARAMETER(Expr, "State transition parameters are invalid: ", __VA_ARGS__) + + CHECK_STATE_TRANSITION_DESC(Barrier.pResource != nullptr, "pResource must not be null"); + CHECK_STATE_TRANSITION_DESC(Barrier.NewState != RESOURCE_STATE_UNKNOWN, "NewState state can't be UNKNOWN"); + + RESOURCE_STATE OldState = RESOURCE_STATE_UNKNOWN; + + if (RefCntAutoPtr<ITexture> pTexture{Barrier.pResource, IID_Texture}) + { + const auto& TexDesc = pTexture->GetDesc(); + + CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(Barrier.NewState, true), "invlaid new state specified for texture '", TexDesc.Name, "'"); + OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pTexture->GetState(); + CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN, + "the state of texture '", TexDesc.Name, + "' is unknown to the engine and is not explicitly specified in the barrier"); + CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(OldState, true), "invlaid old state specified for texture '", TexDesc.Name, "'"); + + CHECK_STATE_TRANSITION_DESC(Barrier.FirstMipLevel < TexDesc.MipLevels, "first mip level (", Barrier.FirstMipLevel, + ") specified by the barrier is out of range. Texture '", + TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); + CHECK_STATE_TRANSITION_DESC(Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.FirstMipLevel + Barrier.MipLevelsCount <= TexDesc.MipLevels, + "mip level range ", Barrier.FirstMipLevel, "..", Barrier.FirstMipLevel + Barrier.MipLevelsCount - 1, + " specified by the barrier is out of range. Texture '", + TexDesc.Name, "' has only ", TexDesc.MipLevels, " mip level(s)"); + + CHECK_STATE_TRANSITION_DESC(Barrier.FirstArraySlice < TexDesc.ArraySize, "first array slice (", Barrier.FirstArraySlice, + ") specified by the barrier is out of range. Array size of texture '", + TexDesc.Name, "' is ", TexDesc.ArraySize); + CHECK_STATE_TRANSITION_DESC(Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.FirstArraySlice + Barrier.ArraySliceCount <= TexDesc.ArraySize, + "array slice range ", Barrier.FirstArraySlice, "..", Barrier.FirstArraySlice + Barrier.ArraySliceCount - 1, + " specified by the barrier is out of range. Array size of texture '", + TexDesc.Name, "' is ", TexDesc.ArraySize); + + auto DevType = pDevice->GetDeviceCaps().DevType; + if (DevType != RENDER_DEVICE_TYPE_D3D12 && DevType != RENDER_DEVICE_TYPE_VULKAN) + { + CHECK_STATE_TRANSITION_DESC(Barrier.FirstMipLevel == 0 && (Barrier.MipLevelsCount == REMAINING_MIP_LEVELS || Barrier.MipLevelsCount == TexDesc.MipLevels), + "failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); + CHECK_STATE_TRANSITION_DESC(Barrier.FirstArraySlice == 0 && (Barrier.ArraySliceCount == REMAINING_ARRAY_SLICES || Barrier.ArraySliceCount == TexDesc.ArraySize), + "failed to transition texture '", TexDesc.Name, "': only whole resources can be transitioned on this device"); + } + } + else if (RefCntAutoPtr<IBuffer> pBuffer{Barrier.pResource, IID_Buffer}) + { + const auto& BuffDesc = pBuffer->GetDesc(); + CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(Barrier.NewState, false), "invlaid new state specified for buffer '", BuffDesc.Name, "'"); + OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pBuffer->GetState(); + CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN, "the state of buffer '", BuffDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier"); + CHECK_STATE_TRANSITION_DESC(VerifyResourceStates(OldState, false), "invlaid old state specified for buffer '", BuffDesc.Name, "'"); + } + else if (RefCntAutoPtr<IBottomLevelAS> pBottomLevelAS{Barrier.pResource, IID_BottomLevelAS}) + { + const auto& BLASDesc = pBottomLevelAS->GetDesc(); + OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pBottomLevelAS->GetState(); + CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN, "the state of BLAS '", BLASDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier"); + CHECK_STATE_TRANSITION_DESC(Barrier.NewState == RESOURCE_STATE_BUILD_AS_READ || Barrier.NewState == RESOURCE_STATE_BUILD_AS_WRITE, + "invlaid new state specified for BLAS '", BLASDesc.Name, "'"); + CHECK_STATE_TRANSITION_DESC(Barrier.TransitionType != STATE_TRANSITION_TYPE_IMMEDIATE, "split barriers are not supported for BLAS"); + } + else if (RefCntAutoPtr<ITopLevelAS> pTopLevelAS{Barrier.pResource, IID_TopLevelAS}) + { + const auto& TLASDesc = pTopLevelAS->GetDesc(); + OldState = Barrier.OldState != RESOURCE_STATE_UNKNOWN ? Barrier.OldState : pTopLevelAS->GetState(); + CHECK_STATE_TRANSITION_DESC(OldState != RESOURCE_STATE_UNKNOWN, "the state of TLAS '", TLASDesc.Name, "' is unknown to the engine and is not explicitly specified in the barrier"); + CHECK_STATE_TRANSITION_DESC(Barrier.NewState == RESOURCE_STATE_BUILD_AS_READ || Barrier.NewState == RESOURCE_STATE_BUILD_AS_WRITE || Barrier.NewState == RESOURCE_STATE_RAY_TRACING, + "invlaid new state specified for TLAS '", TLASDesc.Name, "'"); + CHECK_STATE_TRANSITION_DESC(Barrier.TransitionType != STATE_TRANSITION_TYPE_IMMEDIATE, "split barriers are not supported for TLAS"); + } + else + { + UNEXPECTED("unsupported resource type"); + } + + if (OldState == RESOURCE_STATE_UNORDERED_ACCESS && Barrier.NewState == RESOURCE_STATE_UNORDERED_ACCESS) + { + CHECK_STATE_TRANSITION_DESC(Barrier.TransitionType == STATE_TRANSITION_TYPE_IMMEDIATE, "for UAV barriers, transition type must be STATE_TRANSITION_TYPE_IMMEDIATE"); + } + + if (Barrier.TransitionType == STATE_TRANSITION_TYPE_BEGIN) + { + CHECK_STATE_TRANSITION_DESC(!Barrier.UpdateResourceState, "resource state can't be updated in begin-split barrier"); + } + +#undef CHECK_STATE_TRANSITION_DESC + + return true; +} + + +bool VerifyBuildBLASAttribs(const BuildBLASAttribs& Attribs) +{ +#define CHECK_BUILD_BLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Build BLAS attribs are invalid: ", __VA_ARGS__) + + CHECK_BUILD_BLAS_ATTRIBS(Attribs.pBLAS != nullptr, "pBLAS must not be null"); + CHECK_BUILD_BLAS_ATTRIBS(Attribs.pScratchBuffer != nullptr, "pScratchBuffer must not be null"); + CHECK_BUILD_BLAS_ATTRIBS((Attribs.pTriangleData != nullptr) ^ (Attribs.pBoxData != nullptr), "exactly one of pTriangles and pBoxes must be defined"); + CHECK_BUILD_BLAS_ATTRIBS(Attribs.pBoxData != nullptr || Attribs.BoxDataCount == 0, "pBoxData is null, but BoxDataCount is not 0"); + CHECK_BUILD_BLAS_ATTRIBS(Attribs.pTriangleData != nullptr || Attribs.TriangleDataCount == 0, "pTriangleData is null, but TriangleDataCount is not 0"); + + const auto& BLASDesc = Attribs.pBLAS->GetDesc(); + + 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 was 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 (", Attribs.BoxDataCount, ") does not match the previous value (", GeomCount, ")"); + CHECK_BUILD_BLAS_ATTRIBS(Attribs.TriangleDataCount == 0 || Attribs.TriangleDataCount == GeomCount, + "Update is true, but TriangleDataCount (", Attribs.TriangleDataCount, ") does not match the 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->GetGeometryDescIndex(tri.GeometryName); + + CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != INVALID_INDEX, + "pTriangleData[", i, "].GeometryName (", tri.GeometryName, ") is not found in BLAS description"); + + const auto& TriDesc = BLASDesc.pTriangles[GeomIndex]; + + CHECK_BUILD_BLAS_ATTRIBS(tri.VertexValueType == VT_UNDEFINED || tri.VertexValueType == TriDesc.VertexValueType, + "pTriangleData[", i, "].VertexValueType must be undefined or match the VertexValueType in geometry description"); + + CHECK_BUILD_BLAS_ATTRIBS(tri.VertexComponentCount == 0 || tri.VertexComponentCount == TriDesc.VertexComponentCount, + "pTriangleData[", i, "].VertexComponentCount (", tri.VertexComponentCount, ") must be 0 or match the VertexComponentCount (", + TriDesc.VertexComponentCount, ") in geometry description"); + + CHECK_BUILD_BLAS_ATTRIBS(tri.VertexCount <= TriDesc.MaxVertexCount, + "pTriangleData[", i, "].VertexCount (", tri.VertexCount, ") must not be greater than MaxVertexCount(", TriDesc.MaxVertexCount, ")"); + + CHECK_BUILD_BLAS_ATTRIBS(tri.VertexStride >= VertexSize, + "pTriangleData[", i, "].VertexStride (", tri.VertexStride, ") must be at least ", VertexSize, " bytes"); + + CHECK_BUILD_BLAS_ATTRIBS(tri.pVertexBuffer != nullptr, "pTriangleData[", i, "].pVertexBuffer must not be null"); + + 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 <= 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"); + + CHECK_BUILD_BLAS_ATTRIBS(tri.IndexType == VT_UNDEFINED || tri.IndexType == TriDesc.IndexType, + "pTriangleData[", i, "].IndexType (", GetValueTypeString(tri.IndexType), ") must match the IndexType (", + TriDesc.IndexType, ") in geometry description"); + + CHECK_BUILD_BLAS_ATTRIBS(tri.PrimitiveCount <= TriDesc.MaxPrimitiveCount, + "pTriangleData[", i, "].PrimitiveCount (", tri.PrimitiveCount, ") must not be greater than MaxPrimitiveCount (", + TriDesc.MaxPrimitiveCount, ")"); + + if (TriDesc.IndexType != VT_UNDEFINED) + { + CHECK_BUILD_BLAS_ATTRIBS(tri.pIndexBuffer != nullptr, "pTriangleData[", i, "].pIndexBuffer must not be null"); + + 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"); + + 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"); + } + else + { + CHECK_BUILD_BLAS_ATTRIBS(tri.VertexCount == tri.PrimitiveCount * 3, + "pTriangleData[", i, "].VertexCount (", tri.VertexCount, ") must equal to PrimitiveCount * 3 (", + tri.PrimitiveCount * 3, ")"); + + CHECK_BUILD_BLAS_ATTRIBS(tri.pIndexBuffer == nullptr, "pTriangleData[", i, "].pIndexBuffer must be null if IndexType is VT_UNDEFINED"); + } + + if (tri.pTransformBuffer != nullptr) + { + CHECK_BUILD_BLAS_ATTRIBS((tri.pTransformBuffer->GetDesc().BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, + "pTriangleData[", i, "].pTransformBuffer was not created with BIND_RAY_TRACING flag"); + + CHECK_BUILD_BLAS_ATTRIBS(TriDesc.AllowsTransforms, "pTriangleData[", i, "] uses transform buffer, but AllowsTransforms is false"); + } + } + + for (Uint32 i = 0; i < Attribs.BoxDataCount; ++i) + { + const auto& box = Attribs.pBoxData[i]; + const Uint32 BoxSize = sizeof(float) * 6; + const Uint32 GeomIndex = Attribs.pBLAS->GetGeometryDescIndex(box.GeometryName); + + CHECK_BUILD_BLAS_ATTRIBS(GeomIndex != INVALID_INDEX, + "pBoxData[", i, "].GeometryName (", box.GeometryName, ") is not found in BLAS description"); + + const auto& BoxDesc = BLASDesc.pBoxes[GeomIndex]; + + CHECK_BUILD_BLAS_ATTRIBS(box.BoxCount <= BoxDesc.MaxBoxCount, + "pBoxData[", i, "].BoxCount (", box.BoxCount, ") must not be greated than MaxBoxCount (", BoxDesc.MaxBoxCount, ")"); + + CHECK_BUILD_BLAS_ATTRIBS(box.BoxStride >= BoxSize, + "pBoxData[", i, "].BoxStride (", box.BoxStride, ") must be at least ", BoxSize, " bytes"); + CHECK_BUILD_BLAS_ATTRIBS(box.BoxStride % 8 == 0, + "pBoxData[", i, "].BoxStride (", box.BoxStride, ") must be aligned to 8 bytes"); + + CHECK_BUILD_BLAS_ATTRIBS(box.pBoxBuffer != nullptr, "pBoxData[", i, "].pBoxBuffer must not be null"); + + CHECK_BUILD_BLAS_ATTRIBS((box.pBoxBuffer->GetDesc().BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, + "pBoxData[", i, "].pBoxBuffer was not created with BIND_RAY_TRACING flag"); + } + + const auto& ScratchDesc = Attribs.pScratchBuffer->GetDesc(); + + CHECK_BUILD_BLAS_ATTRIBS(Attribs.ScratchBufferOffset <= ScratchDesc.uiSizeInBytes, + "ScratchBufferOffset (", Attribs.ScratchBufferOffset, ") is greater than the buffer size (", ScratchDesc.uiSizeInBytes, ")"); + + 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"); + +#undef CHECK_BUILD_BLAS_ATTRIBS + + return true; +} + + +bool VerifyBuildTLASAttribs(const BuildTLASAttribs& Attribs) +{ +#define CHECK_BUILD_TLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Build TLAS attribs are invalid: ", __VA_ARGS__) + + CHECK_BUILD_TLAS_ATTRIBS(Attribs.pTLAS != nullptr, "pTLAS must not be null"); + 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.BindingMode == HIT_GROUP_BINDING_MODE_USER_DEFINED || Attribs.HitGroupStride != 0, + "HitGroupStride must be greater than 0 if BindingMode is not HIT_GROUP_BINDING_MODE_USER_DEFINED"); + + const auto& TLASDesc = Attribs.pTLAS->GetDesc(); + + CHECK_BUILD_TLAS_ATTRIBS(Attribs.InstanceCount <= TLASDesc.MaxInstanceCount, + "InstanceCount (", Attribs.InstanceCount, ") must be less than or equal to pTLAS->GetDesc().MaxInstanceCount (", + TLASDesc.MaxInstanceCount, ")"); + + 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"); + + const Uint32 PrevInstanceCount = Attribs.pTLAS->GetBuildInfo().InstanceCount; + CHECK_BUILD_TLAS_ATTRIBS(PrevInstanceCount == Attribs.InstanceCount, + "Update is true, but InstanceCount (", Attribs.InstanceCount, ") does not match with the previous value (", PrevInstanceCount, ")"); + } + + 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) + { + 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(Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO || + (Inst.ContributionToHitGroupIndex & ~BitMask) == 0, + "Only the lower 24 bits are used"); + + CHECK_BUILD_TLAS_ATTRIBS(Inst.InstanceName != nullptr, "pInstances[", i, "].InstanceName must not be null"); + CHECK_BUILD_TLAS_ATTRIBS(Inst.pBLAS != nullptr, "pInstances[", i, "].pBLAS must not be null"); + + 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"); + } + + if (Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO) + ++AutoOffsetCounter; + + CHECK_BUILD_TLAS_ATTRIBS(Attribs.BindingMode == HIT_GROUP_BINDING_MODE_USER_DEFINED || Inst.ContributionToHitGroupIndex == TLAS_INSTANCE_OFFSET_AUTO, + "pInstances[", i, + "].ContributionToHitGroupIndex must be TLAS_INSTANCE_OFFSET_AUTO " + "if BindingMode is not HIT_GROUP_BINDING_MODE_USER_DEFINED"); + } + + CHECK_BUILD_TLAS_ATTRIBS(AutoOffsetCounter == 0 || AutoOffsetCounter == Attribs.InstanceCount, + "all pInstances[i].ContributionToHitGroupIndex must be TLAS_INSTANCE_OFFSET_AUTO, or none of them should"); + + CHECK_BUILD_TLAS_ATTRIBS(Attribs.InstanceBufferOffset <= InstDesc.uiSizeInBytes, + "InstanceBufferOffset (", Attribs.InstanceBufferOffset, ") is greater than the buffer size (", InstDesc.uiSizeInBytes, ")"); + + CHECK_BUILD_TLAS_ATTRIBS(InstDesc.uiSizeInBytes - Attribs.InstanceBufferOffset >= InstDataSize, + "pInstanceBuffer size (", InstDesc.uiSizeInBytes, ") is too small: at least ", + InstDataSize + Attribs.InstanceBufferOffset, " bytes are required"); + + CHECK_BUILD_TLAS_ATTRIBS((InstDesc.BindFlags & BIND_RAY_TRACING) == BIND_RAY_TRACING, + "pInstanceBuffer was not created with BIND_RAY_TRACING flag"); + + const auto& ScratchDesc = Attribs.pScratchBuffer->GetDesc(); + + CHECK_BUILD_TLAS_ATTRIBS(Attribs.ScratchBufferOffset <= ScratchDesc.uiSizeInBytes, + "ScratchBufferOffset (", Attribs.ScratchBufferOffset, ") is greater than the buffer size (", ScratchDesc.uiSizeInBytes, ")"); + + 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"); +#undef CHECK_BUILD_TLAS_ATTRIBS + + return true; +} + + +bool VerifyCopyBLASAttribs(const IRenderDevice* pDevice, const CopyBLASAttribs& Attribs) +{ +#define CHECK_COPY_BLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Copy BLAS attribs are invalid: ", __VA_ARGS__) + + CHECK_COPY_BLAS_ATTRIBS(Attribs.pSrc != nullptr, "pSrc must not be null"); + CHECK_COPY_BLAS_ATTRIBS(Attribs.pDst != nullptr, "pDst must not be null"); + + if (Attribs.Mode == COPY_AS_MODE_CLONE) + { + if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_VULKAN) + { + 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) + { + auto& SrcDesc = Attribs.pSrc->GetDesc(); + auto& DstDesc = Attribs.pDst->GetDesc(); + + CHECK_COPY_BLAS_ATTRIBS((SrcDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION, "must be have been create with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag"); + CHECK_COPY_BLAS_ATTRIBS(DstDesc.CompactedSize != 0, "pDst must have been create with non-zero CompactedSize"); + } + else + { + LOG_ERROR_MESSAGE("IDeviceContext::CopyBLAS: unknown Mode"); + return false; + } + +#undef CHECK_COPY_BLAS_ATTRIBS + + return true; +} + + +bool VerifyCopyTLASAttribs(const CopyTLASAttribs& Attribs) +{ +#define CHECK_COPY_TLAS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Copy TLAS attribs are invalid: ", __VA_ARGS__) + + CHECK_COPY_TLAS_ATTRIBS(Attribs.pSrc != nullptr, "pSrc must not be null"); + CHECK_COPY_TLAS_ATTRIBS(Attribs.pDst != nullptr, "pDst must not be null"); + + if (Attribs.Mode == COPY_AS_MODE_CLONE) + { + auto& SrcDesc = Attribs.pSrc->GetDesc(); + auto& DstDesc = Attribs.pDst->GetDesc(); + + 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) + { + auto& SrcDesc = Attribs.pSrc->GetDesc(); + auto& DstDesc = Attribs.pDst->GetDesc(); + + CHECK_COPY_TLAS_ATTRIBS((SrcDesc.Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION, "pSrc was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag"); + CHECK_COPY_TLAS_ATTRIBS(DstDesc.CompactedSize != 0, "pDst must have been create with non-zero CompactedSize"); + } + else + { + LOG_ERROR_MESSAGE("IDeviceContext::CopyTLAS: unknown Mode"); + return false; + } +#undef CHECK_COPY_TLAS_ATTRIBS + + return true; +} + +bool VerifyWriteBLASCompactedSizeAttribs(const IRenderDevice* pDevice, const WriteBLASCompactedSizeAttribs& Attribs) +{ +#define CHECK_WRITE_BLAS_SIZE_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Write compacted BLAS size attribs are invalid: ", __VA_ARGS__) + + CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.pBLAS != nullptr, "pBLAS must not be null"); + CHECK_WRITE_BLAS_SIZE_ATTRIBS((Attribs.pBLAS->GetDesc().Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION, + "pBLAS was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag"); + + CHECK_WRITE_BLAS_SIZE_ATTRIBS(Attribs.pDestBuffer != nullptr, "pDestBuffer must not be null"); + + 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((DstDesc.BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS, + "pDestBuffer must have been created with BIND_UNORDERED_ACCESS flag in Direct3D12"); + } + +#undef CHECK_WRITE_BLAS_SIZE_ATTRIBS + + return true; +} + +bool VerifyWriteTLASCompactedSizeAttribs(const IRenderDevice* pDevice, const WriteTLASCompactedSizeAttribs& Attribs) +{ +#define CHECK_WRITE_TLAS_SIZE_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Write compacted TLAS size attribs are invalid: ", __VA_ARGS__) + + CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.pTLAS != nullptr, "pTLAS must not be null"); + CHECK_WRITE_TLAS_SIZE_ATTRIBS((Attribs.pTLAS->GetDesc().Flags & RAYTRACING_BUILD_AS_ALLOW_COMPACTION) == RAYTRACING_BUILD_AS_ALLOW_COMPACTION, + "pTLAS was not created with RAYTRACING_BUILD_AS_ALLOW_COMPACTION flag"); + + CHECK_WRITE_TLAS_SIZE_ATTRIBS(Attribs.pDestBuffer != nullptr, "pDestBuffer must not be null"); + + 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((DstDesc.BindFlags & BIND_UNORDERED_ACCESS) == BIND_UNORDERED_ACCESS, + "pDestBuffer must have been created with BIND_UNORDERED_ACCESS flag"); + } + +#undef CHECK_WRITE_TLAS_SIZE_ATTRIBS + + return true; +} + +bool VerifyTraceRaysAttribs(const TraceRaysAttribs& Attribs) +{ +#define CHECK_TRACE_RAYS_ATTRIBS(Expr, ...) CHECK_PARAMETER(Expr, "Trace rays attribs are invalid: ", __VA_ARGS__) + CHECK_TRACE_RAYS_ATTRIBS(Attribs.pSBT != nullptr, "pSBT must not be null"); + +#ifdef DILIGENT_DEVELOPMENT + CHECK_TRACE_RAYS_ATTRIBS(Attribs.pSBT->Verify(SHADER_BINDING_VALIDATION_SHADER_ONLY | SHADER_BINDING_VALIDATION_TLAS), + "not all shaders in SBT are bound or instance to shader mapping is incorrect"); +#endif // DILIGENT_DEVELOPMENT + + CHECK_TRACE_RAYS_ATTRIBS(Attribs.DimensionX != 0, "DimensionX must not be zero."); + CHECK_TRACE_RAYS_ATTRIBS(Attribs.DimensionY != 0, "DimensionY must not be zero."); + CHECK_TRACE_RAYS_ATTRIBS(Attribs.DimensionZ != 0, "DimensionZ must not be zero."); + +#undef CHECK_TRACE_RAYS_ATTRIBS + + return true; +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/src/EngineMemory.cpp b/Graphics/GraphicsEngine/src/EngineMemory.cpp index 198bf0ba..7e8ab0cc 100644 --- a/Graphics/GraphicsEngine/src/EngineMemory.cpp +++ b/Graphics/GraphicsEngine/src/EngineMemory.cpp @@ -29,9 +29,9 @@ // RenderEngine.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information -#include "pch.h" #include "EngineMemory.h" #include "DefaultRawMemoryAllocator.hpp" +#include "Errors.hpp" namespace Diligent { diff --git a/Graphics/GraphicsEngine/src/FramebufferBase.cpp b/Graphics/GraphicsEngine/src/FramebufferBase.cpp index 41943b09..1a173910 100644 --- a/Graphics/GraphicsEngine/src/FramebufferBase.cpp +++ b/Graphics/GraphicsEngine/src/FramebufferBase.cpp @@ -25,14 +25,13 @@ * of the possibility of such damages. */ -#include "pch.h" #include "FramebufferBase.hpp" #include "GraphicsAccessories.hpp" namespace Diligent { -void ValidateFramebufferDesc(const FramebufferDesc& Desc) +void ValidateFramebufferDesc(const FramebufferDesc& Desc) noexcept(false) { #define LOG_FRAMEBUFFER_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of framebuffer '", (Desc.Name ? Desc.Name : ""), "' is invalid: ", ##__VA_ARGS__) diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp index fedc332d..5adc101d 100644 --- a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp @@ -25,7 +25,6 @@ * of the possibility of such damages. */ -#include "pch.h" #include "PipelineStateBase.hpp" namespace Diligent @@ -248,6 +247,102 @@ void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& Cre VALIDATE_SHADER_TYPE(CreateInfo.pCS, SHADER_TYPE_COMPUTE, "compute"); } + +void ValidateRayTracingPipelineCreateInfo(IRenderDevice* pDevice, Uint32 MaxRecursion, const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(false) +{ + const auto& PSODesc = CreateInfo.PSODesc; + if (PSODesc.PipelineType != PIPELINE_TYPE_RAY_TRACING) + LOG_PSO_ERROR_AND_THROW("Pipeline type must be RAY_TRACING"); + + if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_D3D12) + { + if ((CreateInfo.pShaderRecordName != nullptr) != (CreateInfo.RayTracingPipeline.ShaderRecordSize > 0)) + LOG_PSO_ERROR_AND_THROW("pShaderRecordName must not be null if RayTracingPipeline.ShaderRecordSize is not zero"); + } + + if (CreateInfo.RayTracingPipeline.MaxRecursionDepth > MaxRecursion) + { + LOG_PSO_ERROR_AND_THROW("MaxRecursionDepth must not exceed the ", MaxRecursion); + } + + for (Uint32 i = 0; i < CreateInfo.GeneralShaderCount; ++i) + { + const auto& Group = CreateInfo.pGeneralShaders[i]; + if (Group.pShader == nullptr) + LOG_PSO_ERROR_AND_THROW("pGeneralShaders[", i, "].pShader must not be null"); + if (Group.Name == nullptr) + LOG_PSO_ERROR_AND_THROW("pGeneralShaders[", i, "].Name must not be null"); + + switch (Group.pShader->GetDesc().ShaderType) + { + case SHADER_TYPE_RAY_GEN: + case SHADER_TYPE_RAY_MISS: + case SHADER_TYPE_RAY_CLOSEST_HIT: break; + default: + LOG_ERROR_AND_THROW(GetShaderTypeLiteralName(Group.pShader->GetDesc().ShaderType), " is not a valid type for ray tracing general shader"); + } + } + + for (Uint32 i = 0; i < CreateInfo.TriangleHitShaderCount; ++i) + { + const auto& Group = CreateInfo.pTriangleHitShaders[i]; + if (Group.pClosestHitShader == nullptr) + LOG_PSO_ERROR_AND_THROW("pTriangleHitShaders[", i, "].pClosestHitShader must not be null"); + if (Group.Name == nullptr) + LOG_PSO_ERROR_AND_THROW("pTriangleHitShaders[", i, "].Name must not be null"); + + VALIDATE_SHADER_TYPE(Group.pClosestHitShader, SHADER_TYPE_RAY_CLOSEST_HIT, "ray tracing triangle closest hit"); + + if (Group.pAnyHitShader != nullptr) + VALIDATE_SHADER_TYPE(Group.pAnyHitShader, SHADER_TYPE_RAY_ANY_HIT, "ray tracing triangle any hit"); + } + + for (Uint32 i = 0; i < CreateInfo.ProceduralHitShaderCount; ++i) + { + const auto& Group = CreateInfo.pProceduralHitShaders[i]; + if (Group.pIntersectionShader == nullptr) + LOG_PSO_ERROR_AND_THROW("pProceduralHitShaders[", i, "].pIntersectionShader must not be null"); + if (Group.Name == nullptr) + LOG_PSO_ERROR_AND_THROW("pProceduralHitShaders[", i, "].Name must not be null"); + + VALIDATE_SHADER_TYPE(Group.pIntersectionShader, SHADER_TYPE_RAY_INTERSECTION, "ray tracing procedural intersection"); + + if (Group.pClosestHitShader != nullptr) + VALIDATE_SHADER_TYPE(Group.pClosestHitShader, SHADER_TYPE_RAY_CLOSEST_HIT, "ray tracing procedural closest hit"); + if (Group.pAnyHitShader != nullptr) + VALIDATE_SHADER_TYPE(Group.pAnyHitShader, SHADER_TYPE_RAY_ANY_HIT, "ray tracing procedural any hit"); + } +} + +void CopyRayTracingShaderGroups(std::unordered_map<HashMapStringKey, Uint32, HashMapStringKey::Hasher>& NameToGroupIndex, + const RayTracingPipelineStateCreateInfo& CreateInfo, + FixedLinearAllocator& MemPool) noexcept(false) +{ + const auto& PSODesc = CreateInfo.PSODesc; + Uint32 GroupIndex = 0; + + for (Uint32 i = 0; i < CreateInfo.GeneralShaderCount; ++i) + { + bool IsUniqueName = NameToGroupIndex.emplace(HashMapStringKey{MemPool.CopyString(CreateInfo.pGeneralShaders[i].Name)}, GroupIndex++).second; + if (!IsUniqueName) + LOG_PSO_ERROR_AND_THROW("pGeneralShaders[", i, "].Name must be unique"); + } + for (Uint32 i = 0; i < CreateInfo.TriangleHitShaderCount; ++i) + { + bool IsUniqueName = NameToGroupIndex.emplace(HashMapStringKey{MemPool.CopyString(CreateInfo.pTriangleHitShaders[i].Name)}, GroupIndex++).second; + if (!IsUniqueName) + LOG_PSO_ERROR_AND_THROW("pTriangleHitShaders[", i, "].Name must be unique"); + } + for (Uint32 i = 0; i < CreateInfo.ProceduralHitShaderCount; ++i) + { + bool IsUniqueName = NameToGroupIndex.emplace(HashMapStringKey{MemPool.CopyString(CreateInfo.pProceduralHitShaders[i].Name)}, GroupIndex++).second; + if (!IsUniqueName) + LOG_PSO_ERROR_AND_THROW("pProceduralHitShaders[", i, "].Name must be unique"); + } + + VERIFY_EXPR(Uint32{CreateInfo.GeneralShaderCount} + Uint32{CreateInfo.TriangleHitShaderCount} + Uint32{CreateInfo.ProceduralHitShaderCount} == GroupIndex); +} + #undef VALIDATE_SHADER_TYPE #undef LOG_PSO_ERROR_AND_THROW diff --git a/Graphics/GraphicsEngine/src/RenderPassBase.cpp b/Graphics/GraphicsEngine/src/RenderPassBase.cpp index b6b38dd0..d20ff071 100644 --- a/Graphics/GraphicsEngine/src/RenderPassBase.cpp +++ b/Graphics/GraphicsEngine/src/RenderPassBase.cpp @@ -25,7 +25,6 @@ * of the possibility of such damages. */ -#include "pch.h" #include "RenderPassBase.hpp" #include "GraphicsAccessories.hpp" #include "Align.hpp" @@ -33,7 +32,7 @@ namespace Diligent { -void ValidateRenderPassDesc(const RenderPassDesc& Desc) +void ValidateRenderPassDesc(const RenderPassDesc& Desc) noexcept(false) { #define LOG_RENDER_PASS_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of render pass '", (Desc.Name ? Desc.Name : ""), "' is invalid: ", ##__VA_ARGS__) diff --git a/Graphics/GraphicsEngine/src/ResourceMappingBase.cpp b/Graphics/GraphicsEngine/src/ResourceMappingBase.cpp index bf34056c..859dcf88 100644 --- a/Graphics/GraphicsEngine/src/ResourceMappingBase.cpp +++ b/Graphics/GraphicsEngine/src/ResourceMappingBase.cpp @@ -25,20 +25,16 @@ * of the possibility of such damages. */ -#include "pch.h" #include "ResourceMappingImpl.hpp" #include "DeviceObjectBase.hpp" -using namespace std; - namespace Diligent { + ResourceMappingImpl::~ResourceMappingImpl() { } -IMPLEMENT_QUERY_INTERFACE(ResourceMappingImpl, IID_ResourceMapping, TObjectBase) - ThreadingTools::LockHelper ResourceMappingImpl::Lock() { return ThreadingTools::LockHelper(m_LockFlag); @@ -55,10 +51,7 @@ void ResourceMappingImpl::AddResourceArray(const Char* Name, Uint32 StartIndex, auto* pObject = ppObjects[Elem]; // Try to construct new element in place - auto Elems = - m_HashTable.emplace( - make_pair(Diligent::ResMappingHashKey(Name, true, StartIndex + Elem), // Make a copy of the source string - Diligent::RefCntAutoPtr<IDeviceObject>(pObject))); + auto Elems = m_HashTable.emplace(ResMappingHashKey{Name, true /*Make copy*/, StartIndex + Elem}, pObject); // If there is already element with the same name, replace it if (!Elems.second && Elems.first->second != pObject) { @@ -88,7 +81,7 @@ void ResourceMappingImpl::RemoveResourceByName(const Char* Name, Uint32 ArrayInd auto LockHelper = Lock(); // Remove object with the given name // Name will be implicitly converted to HashMapStringKey without making a copy - m_HashTable.erase(ResMappingHashKey(Name, false, ArrayIndex)); + m_HashTable.erase(ResMappingHashKey{Name, false, ArrayIndex}); } void ResourceMappingImpl::GetResource(const Char* Name, IDeviceObject** ppResource, Uint32 ArrayIndex) @@ -108,7 +101,7 @@ void ResourceMappingImpl::GetResource(const Char* Name, IDeviceObject** ppResour // Find an object with the requested name // Name will be implicitly converted to HashMapStringKey without making a copy - auto It = m_HashTable.find(ResMappingHashKey(Name, false, ArrayIndex)); + auto It = m_HashTable.find(ResMappingHashKey{Name, false, ArrayIndex}); if (It != m_HashTable.end()) { *ppResource = It->second.RawPtr(); @@ -121,4 +114,5 @@ size_t ResourceMappingImpl::GetSize() { return m_HashTable.size(); } + } // namespace Diligent diff --git a/Graphics/GraphicsEngine/src/ShaderBindingTableBase.cpp b/Graphics/GraphicsEngine/src/ShaderBindingTableBase.cpp new file mode 100644 index 00000000..e0cb3461 --- /dev/null +++ b/Graphics/GraphicsEngine/src/ShaderBindingTableBase.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "ShaderBindingTableBase.hpp" + +namespace Diligent +{ + +void ValidateShaderBindingTableDesc(const ShaderBindingTableDesc& Desc, Uint32 ShaderGroupHandleSize, Uint32 MaxShaderRecordStride) noexcept(false) +{ +#define LOG_SBT_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of Shader binding table '", (Desc.Name ? Desc.Name : ""), "' is invalid: ", ##__VA_ARGS__) + + if (Desc.pPSO == nullptr) + { + LOG_SBT_ERROR_AND_THROW("pPSO must not be null"); + } + + if (Desc.pPSO->GetDesc().PipelineType != PIPELINE_TYPE_RAY_TRACING) + { + LOG_SBT_ERROR_AND_THROW("pPSO must be ray tracing pipeline"); + } + + + const auto ShaderRecordSize = Desc.pPSO->GetRayTracingPipelineDesc().ShaderRecordSize; + const auto ShaderRecordStride = ShaderRecordSize + ShaderGroupHandleSize; + + if (ShaderRecordStride > MaxShaderRecordStride) + { + LOG_SBT_ERROR_AND_THROW("ShaderRecordSize(", ShaderRecordSize, ") is too big, max size is: ", MaxShaderRecordStride - ShaderGroupHandleSize); + } + + if (ShaderRecordStride % ShaderGroupHandleSize != 0) + { + LOG_SBT_ERROR_AND_THROW("ShaderRecordSize (", ShaderRecordSize, ") plus ShaderGroupHandleSize (", ShaderGroupHandleSize, + ") must be a multiple of ", ShaderGroupHandleSize); + } +#undef LOG_SBT_ERROR_AND_THROW +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngine/src/TextureBase.cpp b/Graphics/GraphicsEngine/src/TextureBase.cpp index 93050ff1..0ecfc538 100644 --- a/Graphics/GraphicsEngine/src/TextureBase.cpp +++ b/Graphics/GraphicsEngine/src/TextureBase.cpp @@ -25,14 +25,14 @@ * of the possibility of such damages. */ -#include "pch.h" #include "Texture.h" +#include "DeviceContext.h" #include "GraphicsAccessories.hpp" namespace Diligent { -void ValidateTextureDesc(const TextureDesc& Desc) +void ValidateTextureDesc(const TextureDesc& Desc) noexcept(false) { #define LOG_TEXTURE_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Texture '", (Desc.Name ? Desc.Name : ""), "': ", ##__VA_ARGS__) @@ -311,4 +311,230 @@ void ValidateMapTextureParams(const TextureDesc& TexDesc, } } +void ValidatedAndCorrectTextureViewDesc(const TextureDesc& TexDesc, TextureViewDesc& ViewDesc) noexcept(false) +{ +#define TEX_VIEW_VALIDATION_ERROR(...) LOG_ERROR_AND_THROW("\n Failed to create texture view '", (ViewDesc.Name ? ViewDesc.Name : ""), "' for texture '", TexDesc.Name, "': ", ##__VA_ARGS__) + + if (!(ViewDesc.ViewType > TEXTURE_VIEW_UNDEFINED && ViewDesc.ViewType < TEXTURE_VIEW_NUM_VIEWS)) + TEX_VIEW_VALIDATION_ERROR("Texture view type is not specified"); + + if (ViewDesc.MostDetailedMip >= TexDesc.MipLevels) + TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") is out of range. The texture has only ", TexDesc.MipLevels, " mip ", (TexDesc.MipLevels > 1 ? "levels." : "level.")); + + if (ViewDesc.NumMipLevels != REMAINING_MIP_LEVELS && ViewDesc.MostDetailedMip + ViewDesc.NumMipLevels > TexDesc.MipLevels) + TEX_VIEW_VALIDATION_ERROR("Most detailed mip (", ViewDesc.MostDetailedMip, ") and number of mip levels in the view (", ViewDesc.NumMipLevels, ") is out of range. The texture has only ", TexDesc.MipLevels, " mip ", (TexDesc.MipLevels > 1 ? "levels." : "level.")); + + if (ViewDesc.Format == TEX_FORMAT_UNKNOWN) + ViewDesc.Format = GetDefaultTextureViewFormat(TexDesc.Format, ViewDesc.ViewType, TexDesc.BindFlags); + + if (ViewDesc.TextureDim == RESOURCE_DIM_UNDEFINED) + { + if (TexDesc.Type == RESOURCE_DIM_TEX_CUBE || TexDesc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) + { + switch (ViewDesc.ViewType) + { + case TEXTURE_VIEW_SHADER_RESOURCE: + ViewDesc.TextureDim = TexDesc.Type; + break; + + case TEXTURE_VIEW_RENDER_TARGET: + case TEXTURE_VIEW_DEPTH_STENCIL: + case TEXTURE_VIEW_UNORDERED_ACCESS: + ViewDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; + break; + + default: UNEXPECTED("Unexpected view type"); + } + } + else + { + ViewDesc.TextureDim = TexDesc.Type; + } + } + + switch (TexDesc.Type) + { + case RESOURCE_DIM_TEX_1D: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 1D view: only Texture 1D is allowed"); + } + break; + + case RESOURCE_DIM_TEX_1D_ARRAY: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_1D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect view type for Texture 1D Array: only Texture 1D or Texture 1D Array are allowed"); + } + break; + + case RESOURCE_DIM_TEX_2D: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D view: only Texture 2D or Texture 2D Array are allowed"); + } + break; + + case RESOURCE_DIM_TEX_2D_ARRAY: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 2D Array view: only Texture 2D or Texture 2D Array are allowed"); + } + break; + + case RESOURCE_DIM_TEX_3D: + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_3D) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture 3D view: only Texture 3D is allowed"); + } + break; + + case RESOURCE_DIM_TEX_CUBE: + if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube SRV: Texture 2D, Texture 2D array or Texture Cube is allowed"); + } + } + else + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube non-shader resource view: Texture 2D or Texture 2D array is allowed"); + } + } + break; + + case RESOURCE_DIM_TEX_CUBE_ARRAY: + if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_CUBE_ARRAY) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array SRV: Texture 2D, Texture 2D array, Texture Cube or Texture Cube Array is allowed"); + } + } + else + { + if (ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D && + ViewDesc.TextureDim != RESOURCE_DIM_TEX_2D_ARRAY) + { + TEX_VIEW_VALIDATION_ERROR("Incorrect texture type for Texture cube array non-shader resource view: Texture 2D or Texture 2D array is allowed"); + } + } + break; + + default: + UNEXPECTED("Unexpected texture type"); + break; + } + + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE) + { + if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) + TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); + if (ViewDesc.NumArraySlices != 6 && ViewDesc.NumArraySlices != 0 && ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES) + TEX_VIEW_VALIDATION_ERROR("Texture cube SRV is expected to have 6 array slices, while ", ViewDesc.NumArraySlices, " is provided"); + if (ViewDesc.FirstArraySlice != 0) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture cube SRV"); + } + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) + { + if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) + TEX_VIEW_VALIDATION_ERROR("Unexpected view type: SRV is expected"); + if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && (ViewDesc.NumArraySlices % 6) != 0) + TEX_VIEW_VALIDATION_ERROR("Number of slices in texture cube array SRV is expected to be multiple of 6. ", ViewDesc.NumArraySlices, " slices is provided."); + } + + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D) + { + if (ViewDesc.FirstArraySlice != 0) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") must be 0 for non-array texture 1D/2D views"); + + if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.NumArraySlices > 1) + TEX_VIEW_VALIDATION_ERROR("Number of slices in the view (", ViewDesc.NumArraySlices, ") must be 1 (or 0) for non-array texture 1D/2D views"); + } + else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) + { + if (ViewDesc.FirstArraySlice >= TexDesc.ArraySize) + TEX_VIEW_VALIDATION_ERROR("First array slice (", ViewDesc.FirstArraySlice, ") exceeds the number of slices in the texture array (", TexDesc.ArraySize, ")"); + + if (ViewDesc.NumArraySlices != REMAINING_ARRAY_SLICES && ViewDesc.FirstArraySlice + ViewDesc.NumArraySlices > TexDesc.ArraySize) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstArraySlice, ") and number of slices in the view (", ViewDesc.NumArraySlices, ") specify more slices than target texture has (", TexDesc.ArraySize, ")"); + } + else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) + { + auto MipDepth = TexDesc.Depth >> ViewDesc.MostDetailedMip; + if (ViewDesc.FirstDepthSlice + ViewDesc.NumDepthSlices > MipDepth) + TEX_VIEW_VALIDATION_ERROR("First slice (", ViewDesc.FirstDepthSlice, ") and number of slices in the view (", ViewDesc.NumDepthSlices, ") specify more slices than target 3D texture mip level has (", MipDepth, ")"); + } + else + { + UNEXPECTED("Unexpected texture dimension"); + } + + if (GetTextureFormatAttribs(ViewDesc.Format).IsTypeless) + { + TEX_VIEW_VALIDATION_ERROR("Texture view format (", GetTextureFormatAttribs(ViewDesc.Format).Name, ") cannot be typeless"); + } + + if ((ViewDesc.Flags & TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION) != 0) + { + if ((TexDesc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) == 0) + TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only set if the texture was created with MISC_TEXTURE_FLAG_GENERATE_MIPS flag"); + + if (ViewDesc.ViewType != TEXTURE_VIEW_SHADER_RESOURCE) + TEX_VIEW_VALIDATION_ERROR("TEXTURE_VIEW_FLAG_ALLOW_MIP_MAP_GENERATION flag can only be used with TEXTURE_VIEW_SHADER_RESOURCE view type"); + } + +#undef TEX_VIEW_VALIDATION_ERROR + + if (ViewDesc.NumMipLevels == 0 || ViewDesc.NumMipLevels == REMAINING_MIP_LEVELS) + { + if (ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE) + ViewDesc.NumMipLevels = TexDesc.MipLevels - ViewDesc.MostDetailedMip; + else + ViewDesc.NumMipLevels = 1; + } + + if (ViewDesc.NumArraySlices == 0 || ViewDesc.NumArraySlices == REMAINING_ARRAY_SLICES) + { + if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_1D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_2D_ARRAY || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE || + ViewDesc.TextureDim == RESOURCE_DIM_TEX_CUBE_ARRAY) + ViewDesc.NumArraySlices = TexDesc.ArraySize - ViewDesc.FirstArraySlice; + else if (ViewDesc.TextureDim == RESOURCE_DIM_TEX_3D) + { + auto MipDepth = TexDesc.Depth >> ViewDesc.MostDetailedMip; + ViewDesc.NumDepthSlices = MipDepth - ViewDesc.FirstDepthSlice; + } + else + ViewDesc.NumArraySlices = 1; + } + + if ((ViewDesc.ViewType == TEXTURE_VIEW_RENDER_TARGET) && + (ViewDesc.Format == TEX_FORMAT_R8_SNORM || ViewDesc.Format == TEX_FORMAT_RG8_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA8_SNORM || + ViewDesc.Format == TEX_FORMAT_R16_SNORM || ViewDesc.Format == TEX_FORMAT_RG16_SNORM || ViewDesc.Format == TEX_FORMAT_RGBA16_SNORM)) + { + const auto* FmtName = GetTextureFormatAttribs(ViewDesc.Format).Name; + LOG_WARNING_MESSAGE(FmtName, " render target view is created.\n" + "There might be an issue in OpenGL driver on NVidia hardware: when rendering to SNORM textures, all negative values are clamped to zero.\n" + "Use UNORM format instead."); + } +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/pch.h b/Graphics/GraphicsEngine/src/TopLevelASBase.cpp index 74889e44..5ccc51c4 100644 --- a/Graphics/GraphicsEngine/include/pch.h +++ b/Graphics/GraphicsEngine/src/TopLevelASBase.cpp @@ -25,21 +25,41 @@ * of the possibility of such damages. */ -/// \file -/// Precomputed header - -#pragma once - -#include <vector> -#include <list> -#include <set> -#include <map> -#include <unordered_map> -#include <memory> -#include <algorithm> -#include "GraphicsTypes.h" -#include "RefCntAutoPtr.hpp" -#include "Errors.hpp" -#include "DebugUtilities.hpp" -#include "RenderDeviceBase.hpp" -#include "DeviceContextBase.hpp"
\ No newline at end of file +#include "TopLevelASBase.hpp" + +namespace Diligent +{ + +void ValidateTopLevelASDesc(const TopLevelASDesc& Desc) noexcept(false) +{ +#define LOG_TLAS_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of a top-level AS '", (Desc.Name ? Desc.Name : ""), "' is invalid: ", ##__VA_ARGS__) + + if (Desc.CompactedSize > 0) + { + if (Desc.MaxInstanceCount != 0) + { + LOG_TLAS_ERROR_AND_THROW("If non-zero CompactedSize is specified, MaxInstanceCount must be zero"); + } + + if (Desc.Flags != RAYTRACING_BUILD_AS_NONE) + { + LOG_TLAS_ERROR_AND_THROW("If non-zero CompactedSize is specified, Flags must be RAYTRACING_BUILD_AS_NONE"); + } + } + else + { + if (Desc.MaxInstanceCount == 0) + { + LOG_TLAS_ERROR_AND_THROW("MaxInstanceCount must not be zero"); + } + + if ((Desc.Flags & RAYTRACING_BUILD_AS_PREFER_FAST_TRACE) != 0 && (Desc.Flags & RAYTRACING_BUILD_AS_PREFER_FAST_BUILD) != 0) + { + LOG_TLAS_ERROR_AND_THROW("RAYTRACING_BUILD_AS_PREFER_FAST_TRACE and RAYTRACING_BUILD_AS_PREFER_FAST_BUILD flags are mutually exclusive"); + } + } + +#undef LOG_TLAS_ERROR_AND_THROW +} + +} // namespace Diligent |
