Changed the RayTracingShaderTable build to be queued with the RHISystem and processed during FrameScheduler::Compile, after the Srgs are compiled.

Removed the RayTracingShaderTable scopes and moved the calls to Build to the ray tracing dispatch scopes.
This commit is contained in:
dmcdiar
2021-05-13 19:03:38 -07:00
parent bf62687b37
commit c5982c0115
14 changed files with 135 additions and 103 deletions
@@ -49,11 +49,6 @@ namespace AZ
}
}
DiffuseProbeGridRayTracingPass::~DiffuseProbeGridRayTracingPass()
{
delete m_rayTracingScopeProducerShaderTable;
}
void DiffuseProbeGridRayTracingPass::CreateRayTracingPipelineState()
{
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
@@ -118,19 +113,28 @@ namespace AZ
void DiffuseProbeGridRayTracingPass::FrameBeginInternal(FramePrepareParams params)
{
RPI::Scene* scene = m_pipeline->GetScene();
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
if (!rayTracingFeatureProcessor)
{
return;
}
if (!m_initialized)
{
CreateRayTracingPipelineState();
CreateShaderTableScope();
m_initialized = true;
}
if (!m_rayTracingShaderTable)
{
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
RHI::RayTracingBufferPools& rayTracingBufferPools = rayTracingFeatureProcessor->GetBufferPools();
m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
m_rayTracingShaderTable->Init(*device.get(), rayTracingBufferPools);
}
RPI::Scene* scene = m_pipeline->GetScene();
DiffuseProbeGridFeatureProcessor* diffuseProbeGridFeatureProcessor = scene->GetFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
if (!diffuseProbeGridFeatureProcessor || diffuseProbeGridFeatureProcessor->GetProbeGrids().empty())
{
@@ -138,70 +142,9 @@ namespace AZ
return;
}
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
uint32_t rayTracingRevision = rayTracingFeatureProcessor->GetRevision();
if (m_rayTracingRevision != rayTracingRevision)
{
// scene changed, need to rebuild the shader table
m_rayTracingRevision = rayTracingRevision;
// [GFX TODO][ATOM-13575] Move the RHI::RayTracingShaderTable build into the RHI frame and remove this scope
params.m_frameGraphBuilder->ImportScopeProducer(*m_rayTracingScopeProducerShaderTable);
}
RenderPass::FrameBeginInternal(params);
}
void DiffuseProbeGridRayTracingPass::CreateShaderTableScope()
{
struct ScopeData { };
const auto prepareFunction = [this]([[maybe_unused]] RHI::FrameGraphInterface& scopeBuilder, [[maybe_unused]] ScopeData& scopeData) {};
const auto compileFunction = [this]([[maybe_unused]] const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData) {};
const auto executeFunction = [this]([[maybe_unused]] const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
{
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
RayTracingFeatureProcessor* rayTracingFeatureProcessor = m_pipeline->GetScene()->GetFeatureProcessor<RayTracingFeatureProcessor>();
RHI::RayTracingBufferPools& rayTracingBufferPools = rayTracingFeatureProcessor->GetBufferPools();
if (!rayTracingFeatureProcessor->GetSubMeshCount())
{
m_rayTracingShaderTable = nullptr;
return;
}
// build the ray tracing shader table descriptor
RHI::RayTracingShaderTableDescriptor descriptor;
RHI::RayTracingShaderTableDescriptor* descriptorBuild = descriptor.Build(AZ::Name("RayTracingShaderTable"), m_rayTracingPipelineState)
->RayGenerationRecord(AZ::Name("RayGen"))
->MissRecord(AZ::Name("Miss"));
// add a hit group for each mesh to the shader table
for (uint32_t i = 0; i < rayTracingFeatureProcessor->GetSubMeshCount(); ++i)
{
descriptorBuild->HitGroupRecord(AZ::Name("HitGroup"));
}
m_rayTracingShaderTable->Init(*device.get(), &descriptor, rayTracingBufferPools);
};
AZStd::string uuidString = AZ::Uuid::CreateRandom().ToString<AZStd::string>();
AZStd::string scopeName = AZStd::string::format("DiffuseProbeRayTracingBuildShaderTable_%s", uuidString.c_str());
m_rayTracingScopeProducerShaderTable =
aznew RHI::ScopeProducerFunction<
ScopeData,
decltype(prepareFunction),
decltype(compileFunction),
decltype(executeFunction)>(
RHI::ScopeId{ scopeName },
ScopeData{ },
prepareFunction,
compileFunction,
executeFunction);
}
void DiffuseProbeGridRayTracingPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
{
RenderPass::SetupFrameGraphDependencies(frameGraph);
@@ -211,7 +154,6 @@ namespace AZ
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
frameGraph.SetEstimatedItemCount(aznumeric_cast<uint32_t>(diffuseProbeGridFeatureProcessor->GetProbeGrids().size()));
frameGraph.ExecuteAfter(m_rayTracingScopeProducerShaderTable->GetScopeId());
for (const auto& diffuseProbeGrid : diffuseProbeGridFeatureProcessor->GetProbeGrids())
{
@@ -326,6 +268,31 @@ namespace AZ
diffuseProbeGrid->GetRayTraceSrg()->Compile();
}
}
uint32_t rayTracingRevision = rayTracingFeatureProcessor->GetRevision();
if (m_rayTracingRevision != rayTracingRevision)
{
// scene changed, need to rebuild the shader table
m_rayTracingRevision = rayTracingRevision;
m_rayTracingShaderTableDescriptor = AZStd::make_shared<RHI::RayTracingShaderTableDescriptor>();
if (rayTracingFeatureProcessor->GetSubMeshCount())
{
// build the ray tracing shader table descriptor
RHI::RayTracingShaderTableDescriptor* descriptorBuild = m_rayTracingShaderTableDescriptor->Build(AZ::Name("RayTracingShaderTable"), m_rayTracingPipelineState)
->RayGenerationRecord(AZ::Name("RayGen"))
->MissRecord(AZ::Name("Miss"));
// add a hit group for each mesh to the shader table
for (uint32_t i = 0; i < rayTracingFeatureProcessor->GetSubMeshCount(); ++i)
{
descriptorBuild->HitGroupRecord(AZ::Name("HitGroup"));
}
}
m_rayTracingShaderTable->Build(m_rayTracingShaderTableDescriptor);
}
}
void DiffuseProbeGridRayTracingPass::BuildCommandListInternal([[maybe_unused]] const RHI::FrameGraphExecuteContext& context)
@@ -35,8 +35,6 @@ namespace AZ
AZ_RTTI(DiffuseProbeGridRayTracingPass, "{CB0DF817-3D07-4AC7-8574-F5EE529B8DCA}", RPI::RenderPass);
AZ_CLASS_ALLOCATOR(DiffuseProbeGridRayTracingPass, SystemAllocator, 0);
virtual ~DiffuseProbeGridRayTracingPass() override;
//! Creates a DiffuseProbeGridRayTracingPass
static RPI::Ptr<DiffuseProbeGridRayTracingPass> Create(const RPI::PassDescriptor& descriptor);
@@ -44,7 +42,6 @@ namespace AZ
explicit DiffuseProbeGridRayTracingPass(const RPI::PassDescriptor& descriptor);
void CreateRayTracingPipelineState();
void CreateShaderTableScope();
// Scope producer functions
void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override;
@@ -65,7 +62,7 @@ namespace AZ
// ray tracing shader table
RHI::Ptr<RHI::RayTracingShaderTable> m_rayTracingShaderTable;
RHI::ScopeProducer* m_rayTracingScopeProducerShaderTable = nullptr;
AZStd::shared_ptr<RHI::RayTracingShaderTableDescriptor> m_rayTracingShaderTableDescriptor;
// ray tracing global shader resource group asset and pipeline state
Data::Asset<RPI::ShaderResourceGroupAsset> m_globalSrgAsset;
@@ -31,6 +31,7 @@ namespace AZ
{
class ShaderResourceGroupPool;
class FrameGraphExecuteGroup;
class RayTracingShaderTable;
//! @brief Fill this descriptor when initializing a FrameScheduler instance.
struct FrameSchedulerDescriptor
@@ -181,6 +182,9 @@ namespace AZ
const TransientAttachmentPoolDescriptor* GetTransientAttachmentPoolDescriptor() const;
//! Adds a RayTracingShaderTable to be built this frame
void QueueRayTracingShaderTableForBuild(RayTracingShaderTable* rayTracingShaderTable);
private:
const ScopeId m_rootScopeId{"Root"};
@@ -190,6 +194,7 @@ namespace AZ
void PrepareProducers();
void CompileProducers();
void CompileShaderResourceGroups();
void BuildRayTracingShaderTables();
ScopeProducer* FindScopeProducer(const ScopeId& scopeId);
@@ -224,6 +229,9 @@ namespace AZ
AZStd::unique_ptr<ScopeProducerEmpty> m_rootScopeProducer;
AZStd::vector<ScopeProducer*> m_scopeProducers;
AZStd::unordered_map<ScopeId, ScopeProducer*> m_scopeProducerLookup;
// list of RayTracingShaderTables that should be built this frame
AZStd::vector<RayTracingShaderTable*> m_rayTracingShaderTablesToBuild;
};
}
}
@@ -57,6 +57,7 @@ namespace AZ
const RHI::TransientAttachmentStatistics* GetTransientAttachmentStatistics() const override;
const RHI::TransientAttachmentPoolDescriptor* GetTransientAttachmentPoolDescriptor() const override;
ConstPtr<PlatformLimitsDescriptor> GetPlatformLimitsDescriptor() const override;
void QueueRayTracingShaderTableForBuild(RayTracingShaderTable* rayTracingShaderTable) override;
//////////////////////////////////////////////////////////////////////////
private:
@@ -26,6 +26,7 @@ namespace AZ
class PipelineState;
class PipelineStateCache;
class PlatformLimitsDescriptor;
class RayTracingShaderTable;
struct CpuTimingStatistics;
struct FrameSchedulerCompileRequest;
struct TransientAttachmentStatistics;
@@ -61,6 +62,8 @@ namespace AZ
virtual const RHI::TransientAttachmentPoolDescriptor* GetTransientAttachmentPoolDescriptor() const = 0;
virtual ConstPtr<PlatformLimitsDescriptor> GetPlatformLimitsDescriptor() const = 0;
virtual void QueueRayTracingShaderTableForBuild(RayTracingShaderTable* rayTracingShaderTable) = 0;
};
//! This bus exists to give RHI samples the ability to slot in scopes manually
@@ -97,18 +97,29 @@ namespace AZ
virtual ~RayTracingShaderTable() = default;
static RHI::Ptr<RHI::RayTracingShaderTable> CreateRHIRayTracingShaderTable();
void Init(Device& device, const RayTracingBufferPools& rayTracingBufferPools);
ResultCode Init(Device& device, const RayTracingShaderTableDescriptor* descriptor, const RayTracingBufferPools& rayTracingBufferPools);
//! Queues this RayTracingShaderTable to be built by the FrameScheduler
void Build(const AZStd::shared_ptr<RayTracingShaderTableDescriptor> descriptor);
protected:
AZStd::weak_ptr<RayTracingShaderTableDescriptor> m_descriptor;
const RayTracingBufferPools* m_bufferPools = nullptr;
private:
// explicit shutdown is not allowed for this type
void Shutdown() override final;
friend class FrameScheduler;
/// Called by the FrameScheduler to validate the state prior to building
void Validate();
//////////////////////////////////////////////////////////////////////////
// Platform API
virtual RHI::ResultCode InitInternal(RHI::Device& deviceBase, const RHI::RayTracingShaderTableDescriptor* descriptor, const RayTracingBufferPools& bufferPools) = 0;
virtual RHI::ResultCode BuildInternal() = 0;
//////////////////////////////////////////////////////////////////////////
bool m_isQueuedForBuild = false;
};
}
}
@@ -27,6 +27,7 @@
#include <Atom/RHI/ShaderResourceGroupPool.h>
#include <Atom/RHI/TransientAttachmentPool.h>
#include <Atom/RHI/ResourcePoolDatabase.h>
#include <Atom/RHI/RayTracingShaderTable.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Jobs/Algorithms.h>
@@ -204,6 +205,9 @@ namespace AZ
// Compile all invalidated shader resource groups.
CompileShaderResourceGroups();
// Build RayTracingShaderTables
BuildRayTracingShaderTables();
}
return outcome;
}
@@ -314,6 +318,25 @@ namespace AZ
}
}
void FrameScheduler::BuildRayTracingShaderTables()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender);
AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: BuildRayTracingShaderTables");
for (auto rayTracingShaderTable : m_rayTracingShaderTablesToBuild)
{
rayTracingShaderTable->Validate();
[[maybe_unused]] ResultCode resultCode = rayTracingShaderTable->BuildInternal();
AZ_Assert(resultCode == ResultCode::Success, "RayTracingShaderTable build failed");
rayTracingShaderTable->m_isQueuedForBuild = false;
}
// clear the list now that all RayTracingShaderTables have been built for this frame
m_rayTracingShaderTablesToBuild.clear();
}
ResultCode FrameScheduler::BeginFrame()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender);
@@ -528,5 +551,10 @@ namespace AZ
{
return m_transientAttachmentPool ? &m_transientAttachmentPool->GetDescriptor() : nullptr;
}
void FrameScheduler::QueueRayTracingShaderTableForBuild(RayTracingShaderTable* rayTracingShaderTable)
{
m_rayTracingShaderTablesToBuild.push_back(rayTracingShaderTable);
}
}
}
@@ -290,5 +290,9 @@ namespace AZ
return m_platformLimitsDescriptor;
}
void RHISystem::QueueRayTracingShaderTableForBuild(RayTracingShaderTable* rayTracingShaderTable)
{
m_frameScheduler.QueueRayTracingShaderTableForBuild(rayTracingShaderTable);
}
} //namespace RPI
} //namespace AZ
@@ -12,6 +12,7 @@
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/RayTracingShaderTable.h>
#include <Atom/RHI/RHISystemInterface.h>
namespace AZ
{
@@ -77,21 +78,29 @@ namespace AZ
return rayTracingShaderTable;
}
ResultCode RayTracingShaderTable::Init(Device& device, const RayTracingShaderTableDescriptor* descriptor, const RayTracingBufferPools& bufferPools)
void RayTracingShaderTable::Init(Device& device, const RayTracingBufferPools& bufferPools)
{
#if defined (AZ_RHI_ENABLE_VALIDATION)
// [GFX TODO][ATOM-5217] Validate shaders in the ray tracing shader table are present in the pipeline state
#endif
ResultCode resultCode = InitInternal(device, descriptor, bufferPools);
if (resultCode == ResultCode::Success)
{
DeviceObject::Init(device);
}
return resultCode;
DeviceObject::Init(device);
m_bufferPools = &bufferPools;
}
void RayTracingShaderTable::Shutdown()
void RayTracingShaderTable::Build(const AZStd::shared_ptr<RayTracingShaderTableDescriptor> descriptor)
{
AZ_Assert(!m_isQueuedForBuild, "Attempting to build a RayTracingShaderTable that's already been queued. Only build once per frame.")
m_descriptor = descriptor;
RHI::RHISystemInterface::Get()->QueueRayTracingShaderTableForBuild(this);
m_isQueuedForBuild = true;
}
void RayTracingShaderTable::Validate()
{
AZ_Assert(m_isQueuedForBuild, "Attempting to build a RayTracingShaderTable that is not queued.");
AZ_Assert(!m_descriptor.expired(), "RayTracingShaderTable descriptor is no longer valid, make sure it is not freed after calling Build.");
AZ_Assert(m_bufferPools, "RayTracingBufferPools pointer is null.");
}
}
}
@@ -121,15 +121,17 @@ namespace AZ
}
#endif
RHI::ResultCode RayTracingShaderTable::InitInternal([[maybe_unused]] RHI::Device& deviceBase, [[maybe_unused]] const RHI::RayTracingShaderTableDescriptor* descriptor, [[maybe_unused]] const RHI::RayTracingBufferPools& bufferPools)
RHI::ResultCode RayTracingShaderTable::BuildInternal()
{
#ifdef AZ_DX12_DXR_SUPPORT
// advance to the next buffer
m_currentBufferIndex = (m_currentBufferIndex + 1) % BufferCount;
ShaderTableBuffers& buffers = m_buffers[m_currentBufferIndex];
// clear the shader table if a null descriptor was passed in
if (!descriptor)
AZStd::shared_ptr<RHI::RayTracingShaderTableDescriptor> descriptor = m_descriptor.lock();
// clear the shader table if the descriptor has no ray generation shader
if (descriptor->GetRayGenerationRecord().empty())
{
buffers.m_rayGenerationTable = nullptr;
buffers.m_rayGenerationTableSize = 0;
@@ -156,7 +158,7 @@ namespace AZ
AZ_Assert(descriptor->GetRayGenerationRecord().size() == 1, "Descriptor must contain one and only one RayGeneration record");
uint32_t shaderRecordSize = RHI::AlignUp(FindLargestRecordSize(descriptor->GetRayGenerationRecord()), D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT);
buffers.m_rayGenerationTable = BuildTable(deviceBase, bufferPools, descriptor->GetRayGenerationRecord(), shaderRecordSize, L"Ray Generation Shader Table", stateObjectProperties);
buffers.m_rayGenerationTable = BuildTable(GetDevice(), *m_bufferPools, descriptor->GetRayGenerationRecord(), shaderRecordSize, L"Ray Generation Shader Table", stateObjectProperties);
buffers.m_rayGenerationTableSize = shaderRecordSize;
}
@@ -164,7 +166,7 @@ namespace AZ
{
uint32_t shaderRecordSize = RHI::AlignUp(FindLargestRecordSize(descriptor->GetMissRecords()), D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT);
buffers.m_missTable = BuildTable(deviceBase, bufferPools, descriptor->GetMissRecords(), shaderRecordSize, L"Miss Shader Table", stateObjectProperties);
buffers.m_missTable = BuildTable(GetDevice(), *m_bufferPools, descriptor->GetMissRecords(), shaderRecordSize, L"Miss Shader Table", stateObjectProperties);
buffers.m_missTableSize = shaderRecordSize * static_cast<uint32_t>(descriptor->GetMissRecords().size());
buffers.m_missTableStride = shaderRecordSize;
}
@@ -173,7 +175,7 @@ namespace AZ
{
uint32_t shaderRecordSize = RHI::AlignUp(FindLargestRecordSize(descriptor->GetHitGroupRecords()), D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT);
buffers.m_hitGroupTable = BuildTable(deviceBase, bufferPools, descriptor->GetHitGroupRecords(), shaderRecordSize, L"HitGroup Shader Table", stateObjectProperties);
buffers.m_hitGroupTable = BuildTable(GetDevice(), *m_bufferPools, descriptor->GetHitGroupRecords(), shaderRecordSize, L"HitGroup Shader Table", stateObjectProperties);
buffers.m_hitGroupTableSize = shaderRecordSize * static_cast<uint32_t>(descriptor->GetHitGroupRecords().size());
buffers.m_hitGroupTableStride = shaderRecordSize;
}
@@ -61,8 +61,8 @@ namespace AZ
#endif
//////////////////////////////////////////////////////////////////////////
// RHI::PipelineState
RHI::ResultCode InitInternal(RHI::Device& deviceBase, const RHI::RayTracingShaderTableDescriptor* descriptor, const RHI::RayTracingBufferPools& bufferPools) override;
// RHI::RayTracingShaderTable
RHI::ResultCode BuildInternal() override;
//////////////////////////////////////////////////////////////////////////
static const uint32_t BufferCount = 3;
@@ -33,8 +33,8 @@ namespace AZ
RayTracingShaderTable() = default;
//////////////////////////////////////////////////////////////////////////
// RHI::PipelineState
RHI::ResultCode InitInternal([[maybe_unused]] RHI::Device& deviceBase, [[maybe_unused]] const RHI::RayTracingShaderTableDescriptor* descriptor, [[maybe_unused]] const RHI::RayTracingBufferPools& bufferPools) override {return RHI::ResultCode::Success;}
// RHI::RayTracingShaderTable
RHI::ResultCode BuildInternal() override {return RHI::ResultCode::Success;}
//////////////////////////////////////////////////////////////////////////
};
}
@@ -75,20 +75,22 @@ namespace AZ
return static_cast<Buffer*>(shaderTableBuffer.get());
}
RHI::ResultCode RayTracingShaderTable::InitInternal([[maybe_unused]] RHI::Device& deviceBase, [[maybe_unused]] const RHI::RayTracingShaderTableDescriptor* descriptor, [[maybe_unused]] const RHI::RayTracingBufferPools& bufferPools)
RHI::ResultCode RayTracingShaderTable::BuildInternal()
{
auto& device = static_cast<Device&>(deviceBase);
auto& device = static_cast<Device&>(GetDevice());
auto& physicalDevice = static_cast<const PhysicalDevice&>(device.GetPhysicalDevice());
const VkPhysicalDeviceRayTracingPipelinePropertiesKHR& rayTracingPipelineProperties = physicalDevice.GetPhysicalDeviceRayTracingPipelineProperties();
uint32_t shaderHandleSize = rayTracingPipelineProperties.shaderGroupHandleSize;
uint32_t alignedShaderHandleSize = RHI::AlignUp(shaderHandleSize, rayTracingPipelineProperties.shaderGroupBaseAlignment);
AZStd::shared_ptr<RHI::RayTracingShaderTableDescriptor> descriptor = m_descriptor.lock();
// advance to the next buffer
m_currentBufferIndex = (m_currentBufferIndex + 1) % BufferCount;
ShaderTableBuffers& buffers = m_buffers[m_currentBufferIndex];
// clear the shader table if a null descriptor was passed in
if (!descriptor)
// clear the shader table if the descriptor has no ray generation shader
if (descriptor->GetRayGenerationRecord().empty())
{
buffers.m_rayGenerationTable = nullptr;
buffers.m_rayGenerationTableStride = 0;
@@ -118,7 +120,7 @@ namespace AZ
buffers.m_rayGenerationTable = BuildTable(
rayTracingPipelineProperties,
rayTracingPipelineState,
bufferPools,
*m_bufferPools,
descriptor->GetRayGenerationRecord(),
buffers.m_rayGenerationTableStride,
"RayGenerationTable");
@@ -126,7 +128,7 @@ namespace AZ
buffers.m_missTable = BuildTable(
rayTracingPipelineProperties,
rayTracingPipelineState,
bufferPools,
*m_bufferPools,
descriptor->GetMissRecords(),
buffers.m_missTableStride,
"MissTable");
@@ -134,7 +136,7 @@ namespace AZ
buffers.m_hitGroupTable = BuildTable(
rayTracingPipelineProperties,
rayTracingPipelineState,
bufferPools,
*m_bufferPools,
descriptor->GetHitGroupRecords(),
buffers.m_hitGroupTableStride,
"HitGroupTable");
@@ -59,7 +59,7 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
// RHI::RayTracingShaderTable
RHI::ResultCode InitInternal(RHI::Device& deviceBase, const RHI::RayTracingShaderTableDescriptor* descriptor, const RHI::RayTracingBufferPools& bufferPools) override;
RHI::ResultCode BuildInternal() override;
//////////////////////////////////////////////////////////////////////////
static const uint32_t BufferCount = 3;