Added RayTracingPass and RayTracingPassData

This commit is contained in:
Doug McDiarmid
2021-05-24 01:19:37 -07:00
parent d112ae403b
commit c29c1825cb
6 changed files with 492 additions and 0 deletions
@@ -91,6 +91,8 @@
#include <ImGui/ImGuiPass.h>
#include <RayTracing/RayTracingAccelerationStructurePass.h>
#include <RayTracing/RayTracingPass.h>
#include <RayTracing/RayTracingPassData.h>
#include <DiffuseProbeGrid/DiffuseProbeGridRayTracingPass.h>
#include <DiffuseProbeGrid/DiffuseProbeGridBlendIrradiancePass.h>
#include <DiffuseProbeGrid/DiffuseProbeGridBlendDistancePass.h>
@@ -132,6 +134,7 @@ namespace AZ
SMAAFeatureProcessor::Reflect(context);
PostProcessFeatureProcessor::Reflect(context);
ImGuiPassData::Reflect(context);
RayTracingPassData::Reflect(context);
LightingPreset::Reflect(context);
ModelPreset::Reflect(context);
@@ -275,6 +278,9 @@ namespace AZ
passSystem->AddPassCreator(Name("ReflectionScreenSpaceBlurChildPass"), &Render::ReflectionScreenSpaceBlurChildPass::Create);
passSystem->AddPassCreator(Name("ReflectionCopyFrameBufferPass"), &Render::ReflectionCopyFrameBufferPass::Create);
// Add RayTracing pas
passSystem->AddPassCreator(Name("RayTracingPass"), &Render::RayTracingPass::Create);
// setup handler for load pass template mappings
m_loadTemplatesHandler = RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler([this]() { this->LoadPassTemplateMappings(); });
RPI::PassSystemInterface::Get()->ConnectEvent(m_loadTemplatesHandler);
@@ -0,0 +1,326 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <Atom/RHI/CommandList.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/FrameScheduler.h>
#include <Atom/RHI/DispatchRaysItem.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RHI/PipelineState.h>
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/View.h>
#include <RayTracing/RayTracingPass.h>
#include <RayTracing/RayTracingPassData.h>
#include <RayTracing/RayTracingFeatureProcessor.h>
namespace AZ
{
namespace Render
{
RPI::Ptr<RayTracingPass> RayTracingPass::Create(const RPI::PassDescriptor& descriptor)
{
RPI::Ptr<RayTracingPass> pass = aznew RayTracingPass(descriptor);
return pass;
}
RayTracingPass::RayTracingPass(const RPI::PassDescriptor& descriptor)
: RenderPass(descriptor)
, m_passDescriptor(descriptor)
{
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
if (device->GetFeatures().m_rayTracing == false)
{
// raytracing is not supported on this platform
SetEnabled(false);
}
Init();
}
RayTracingPass::~RayTracingPass()
{
RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
}
void RayTracingPass::Init()
{
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
m_passData = RPI::PassUtils::GetPassData<RayTracingPassData>(m_passDescriptor);
if (m_passData == nullptr)
{
AZ_Error("PassSystem", false, "RayTracingPass [%s]: Invalid RayTracingPassData", GetPathName().GetCStr());
return;
}
// ray generation shader
m_rayGenerationShader = LoadShader(m_passData->m_rayGenerationShaderAssetReference);
if (m_rayGenerationShader == nullptr)
{
AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load RayGeneration shader [%s]", GetPathName().GetCStr(), m_passData->m_rayGenerationShaderAssetReference.m_filePath.data());
return;
}
auto shaderVariant = m_rayGenerationShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
RHI::PipelineStateDescriptorForRayTracing rayGenerationShaderDescriptor;
shaderVariant.ConfigurePipelineState(rayGenerationShaderDescriptor);
// closest hit shader
m_closestHitShader = LoadShader(m_passData->m_closestHitShaderAssetReference);
if (m_closestHitShader == nullptr)
{
AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load ClosestHit shader [%s]", GetPathName().GetCStr(), m_passData->m_closestHitShaderAssetReference.m_filePath.data());
return;
}
shaderVariant = m_closestHitShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
RHI::PipelineStateDescriptorForRayTracing closestHitShaderDescriptor;
shaderVariant.ConfigurePipelineState(closestHitShaderDescriptor);
// miss shader
m_missShader = LoadShader(m_passData->m_missShaderAssetReference);
if (m_missShader == nullptr)
{
AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load Miss shader [%s]", GetPathName().GetCStr(), m_passData->m_missShaderAssetReference.m_filePath.data());
return;
}
shaderVariant = m_missShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
RHI::PipelineStateDescriptorForRayTracing missShaderDescriptor;
shaderVariant.ConfigurePipelineState(missShaderDescriptor);
// retrieve global pipeline state
m_globalPipelineState = m_rayGenerationShader->AcquirePipelineState(rayGenerationShaderDescriptor);
AZ_Assert(m_globalPipelineState, "Failed to acquire ray tracing global pipeline state");
// create global srg
static const uint32_t RayTracingGlobalSrgBindingSlot = 0;
Data::Asset<RPI::ShaderResourceGroupAsset> globalSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingGlobalSrgBindingSlot);
AZ_Error("PassSystem", globalSrgAsset.GetId().IsValid(), "RayTracingPass [%s] Failed to find RayTracingGlobalSrg asset", GetPathName().GetCStr());
AZ_Error("PassSystem", globalSrgAsset.IsReady(), "RayTracingPass [%s] asset is not loaded for shader", GetPathName().GetCStr());
m_shaderResourceGroup = RPI::ShaderResourceGroup::Create(globalSrgAsset);
AZ_Assert(m_shaderResourceGroup, "RayTracingPass [%s]: Failed to create RayTracingGlobalSrg", GetPathName().GetCStr());
RPI::PassUtils::BindDataMappingsToSrg(m_passDescriptor, m_shaderResourceGroup.get());
// check to see if the shader requires a ViewSrg
Data::Asset<RPI::ShaderResourceGroupAsset> viewSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RPI::SrgBindingSlot::View);
m_requiresViewSrg = viewSrgAsset.GetId().IsValid();
// build the ray tracing pipeline state descriptor
RHI::RayTracingPipelineStateDescriptor descriptor;
descriptor.Build()
->PipelineState(m_globalPipelineState.get())
->MaxPayloadSize(m_passData->m_maxPayloadSize)
->MaxAttributeSize(m_passData->m_maxAttributeSize)
->MaxRecursionDepth(m_passData->m_maxRecursionDepth)
->ShaderLibrary(rayGenerationShaderDescriptor)
->RayGenerationShaderName(AZ::Name(m_passData->m_rayGenerationShaderName.c_str()))
->ShaderLibrary(missShaderDescriptor)
->MissShaderName(AZ::Name(m_passData->m_missShaderName.c_str()))
->ShaderLibrary(closestHitShaderDescriptor)
->ClosestHitShaderName(AZ::Name(m_passData->m_closestHitShaderName.c_str()))
->HitGroup(AZ::Name("HitGroup"))
->ClosestHitShaderName(AZ::Name(m_passData->m_closestHitShaderName.c_str()));
// create the ray tracing pipeline state object
m_rayTracingPipelineState = RHI::Factory::Get().CreateRayTracingPipelineState();
m_rayTracingPipelineState->Init(*device.get(), &descriptor);
// make sure the shader table rebuilds if we're hotreloading
m_rayTracingRevision = 0;
RPI::ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_rayGenerationShaderAssetReference.m_assetId);
RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_closestHitShaderAssetReference.m_assetId);
RPI::ShaderReloadNotificationBus::MultiHandler::BusConnect(m_passData->m_missShaderAssetReference.m_assetId);
}
Data::Instance<RPI::Shader> RayTracingPass::LoadShader(const RPI::AssetReference& shaderAssetReference)
{
Data::Asset<RPI::ShaderAsset> shaderAsset;
if (shaderAssetReference.m_assetId.IsValid())
{
shaderAsset = RPI::FindShaderAsset(shaderAssetReference.m_assetId, shaderAssetReference.m_filePath);
}
if (!shaderAsset.GetId().IsValid())
{
AZ_Error("PassSystem", false, "RayTracingPass [%s]: Failed to load shader asset [%s]", GetPathName().GetCStr(), shaderAssetReference.m_filePath.data());
return nullptr;
}
return RPI::Shader::FindOrCreate(shaderAsset);
}
void RayTracingPass::FrameBeginInternal(FramePrepareParams params)
{
RPI::Scene* scene = m_pipeline->GetScene();
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
if (!rayTracingFeatureProcessor)
{
return;
}
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::RenderPass::FrameBeginInternal(params);
}
void RayTracingPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
{
RPI::RenderPass::SetupFrameGraphDependencies(frameGraph);
frameGraph.SetEstimatedItemCount(1);
}
void RayTracingPass::CompileResources(const RHI::FrameGraphCompileContext& context)
{
RPI::Scene* scene = m_pipeline->GetScene();
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor");
if (m_shaderResourceGroup != nullptr)
{
BindPassSrg(context, m_shaderResourceGroup);
m_shaderResourceGroup->Compile();
}
uint32_t rayTracingRevision = rayTracingFeatureProcessor->GetRevision();
if (m_rayTracingRevision != rayTracingRevision)
{
// scene changed, need to rebuild the shader table
m_rayTracingRevision = rayTracingRevision;
AZStd::shared_ptr<RHI::RayTracingShaderTableDescriptor> descriptor = AZStd::make_shared<RHI::RayTracingShaderTableDescriptor>();
if (rayTracingFeatureProcessor->GetSubMeshCount())
{
// build the ray tracing shader table descriptor
RHI::RayTracingShaderTableDescriptor* descriptorBuild = descriptor->Build(AZ::Name("RayTracingShaderTable"), m_rayTracingPipelineState)
->RayGenerationRecord(AZ::Name(m_passData->m_rayGenerationShaderName.c_str()))
->MissRecord(AZ::Name(m_passData->m_missShaderName.c_str()));
// 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(descriptor);
}
}
void RayTracingPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
{
RPI::Scene* scene = m_pipeline->GetScene();
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor");
if (!rayTracingFeatureProcessor ||
!rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer() ||
!rayTracingFeatureProcessor->GetSubMeshCount() ||
!m_rayTracingShaderTable)
{
return;
}
RHI::DispatchRaysItem dispatchRaysItem;
// calculate thread counts if this is a full screen raytracing pass
if (m_passData->m_makeFullscreenPass)
{
RPI::PassAttachment* outputAttachment = nullptr;
if (GetOutputCount() > 0)
{
outputAttachment = GetOutputBinding(0).m_attachment.get();
}
else if (GetInputOutputCount() > 0)
{
outputAttachment = GetInputOutputBinding(0).m_attachment.get();
}
AZ_Assert(outputAttachment != nullptr, "[RayTracingPass '%s']: A fullscreen RayTracing pass must have a valid output or input/output.", GetPathName().GetCStr());
AZ_Assert(outputAttachment->GetAttachmentType() == RHI::AttachmentType::Image, "[RayTracingPass '%s']: The output of a fullscreen RayTracing pass must be an image.", GetPathName().GetCStr());
RHI::Size imageSize = outputAttachment->m_descriptor.m_image.m_size;
dispatchRaysItem.m_width = imageSize.m_width;
dispatchRaysItem.m_height = imageSize.m_height;
dispatchRaysItem.m_depth = imageSize.m_depth;
}
else
{
dispatchRaysItem.m_width = m_passData->m_threadCountX;
dispatchRaysItem.m_height = m_passData->m_threadCountY;
dispatchRaysItem.m_depth = m_passData->m_threadCountZ;
}
// bind RayTracingGlobal, RayTracingScene, and View Srgs
// [GFX TODO][ATOM-15610] Add RenderPass::SetSrgsForRayTracingDispatch
AZStd::vector<RHI::ShaderResourceGroup*> shaderResourceGroups =
{
m_shaderResourceGroup->GetRHIShaderResourceGroup(),
rayTracingFeatureProcessor->GetRayTracingSceneSrg()->GetRHIShaderResourceGroup()
};
if (m_requiresViewSrg)
{
const AZStd::vector<RPI::ViewPtr>& views = m_pipeline->GetViews(m_passData->m_pipelineViewTag);
if (views.size() > 0)
{
shaderResourceGroups.push_back(views[0]->GetRHIShaderResourceGroup());
}
}
dispatchRaysItem.m_shaderResourceGroupCount = aznumeric_cast<uint32_t>(shaderResourceGroups.size());
dispatchRaysItem.m_shaderResourceGroups = shaderResourceGroups.data();
dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState.get();
dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get();
dispatchRaysItem.m_globalPipelineState = m_globalPipelineState.get();
// submit the DispatchRays item
context.GetCommandList()->Submit(dispatchRaysItem);
}
void RayTracingPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader& shader)
{
Init();
}
void RayTracingPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset<RPI::ShaderAsset>& shaderAsset)
{
Init();
}
void RayTracingPass::OnShaderVariantReinitialized([[maybe_unused]] const RPI::Shader& shader, [[maybe_unused]] const RPI::ShaderVariantId& shaderVariantId, [[maybe_unused]] RPI::ShaderVariantStableId shaderVariantStableId)
{
Init();
}
} // namespace RPI
} // namespace AZ
@@ -0,0 +1,81 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <Atom/RHI/RayTracingPipelineState.h>
#include <Atom/RHI/RayTracingShaderTable.h>
#include <Atom/RPI.Public/Pass/RenderPass.h>
#include <Atom/RPI.Public/Shader/Shader.h>
#include <Atom/RPI.Public/Shader/ShaderReloadNotificationBus.h>
namespace AZ
{
namespace Render
{
struct RayTracingPassData;
//! This pass executes a raytracing shader as specified in the PassData.
class RayTracingPass
: public RPI::RenderPass
, private RPI::ShaderReloadNotificationBus::MultiHandler
{
AZ_RPI_PASS(RayTracingPass);
public:
AZ_RTTI(RayTracingPass, "{7A68A36E-956A-4258-93FE-38686042C4D9}", RPI::RenderPass);
AZ_CLASS_ALLOCATOR(RayTracingPass, SystemAllocator, 0);
virtual ~RayTracingPass();
//! Creates a RayTracingPass
static RPI::Ptr<RayTracingPass> Create(const RPI::PassDescriptor& descriptor);
protected:
RayTracingPass(const RPI::PassDescriptor& descriptor);
// Pass overrides
void FrameBeginInternal(FramePrepareParams params) override;
// Scope producer functions
void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override;
void CompileResources(const RHI::FrameGraphCompileContext& context) override;
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
// ShaderReloadNotificationBus::Handler overrides
void OnShaderReinitialized(const RPI::Shader& shader) override;
void OnShaderAssetReinitialized(const Data::Asset<RPI::ShaderAsset>& shaderAsset) override;
void OnShaderVariantReinitialized(const RPI::Shader& shader, const RPI::ShaderVariantId& shaderVariantId, RPI::ShaderVariantStableId shaderVariantStableId) override;
// load the raytracing shaders and setup pipeline states
void Init();
// helper for loading a shader from a shader asset reference
Data::Instance<RPI::Shader> LoadShader(const RPI::AssetReference& shaderAssetReference);
// pass data
RPI::PassDescriptor m_passDescriptor;
const RayTracingPassData* m_passData = nullptr;
// revision number of the ray tracing TLAS when the shader table was built
uint32_t m_rayTracingRevision = 0;
// raytracing shaders, pipeline states, and shader table
Data::Instance<RPI::Shader> m_rayGenerationShader;
Data::Instance<RPI::Shader> m_missShader;
Data::Instance<RPI::Shader> m_closestHitShader;
RHI::Ptr<RHI::RayTracingPipelineState> m_rayTracingPipelineState;
RHI::ConstPtr<RHI::PipelineState> m_globalPipelineState;
RHI::Ptr<RHI::RayTracingShaderTable> m_rayTracingShaderTable;
bool m_requiresViewSrg = false;
};
} // namespace RPI
} // namespace AZ
@@ -0,0 +1,73 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Atom/RPI.Reflect/Asset/AssetReference.h>
#include <Atom/RPI.Reflect/Pass/RenderPassData.h>
namespace AZ
{
namespace Render
{
//! Custom data for the RayTracingPass, specified in the PassRequest.
struct RayTracingPassData
: public RPI::RenderPassData
{
AZ_RTTI(RayTracingPassData, "{26C2E2FD-D30A-4142-82A3-0167BC94B3EE}", RPI::RenderPassData);
AZ_CLASS_ALLOCATOR(RayTracingPassData, SystemAllocator, 0);
RayTracingPassData() = default;
virtual ~RayTracingPassData() = default;
static void Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<RayTracingPassData, RenderPassData>()
->Version(1)
->Field("RayGenerationShaderAsset", &RayTracingPassData::m_rayGenerationShaderAssetReference)
->Field("RayGenerationShaderName", &RayTracingPassData::m_rayGenerationShaderName)
->Field("ClosestHitShaderAsset", &RayTracingPassData::m_closestHitShaderAssetReference)
->Field("ClosestHitShaderName", &RayTracingPassData::m_closestHitShaderName)
->Field("MissShaderAsset", &RayTracingPassData::m_missShaderAssetReference)
->Field("MissShaderName", &RayTracingPassData::m_missShaderName)
->Field("MaxPayloadSize", &RayTracingPassData::m_maxPayloadSize)
->Field("MaxAttributeSize", &RayTracingPassData::m_maxAttributeSize)
->Field("MaxRecursionDepth", &RayTracingPassData::m_maxRecursionDepth)
->Field("Thread Count X", &RayTracingPassData::m_threadCountX)
->Field("Thread Count Y", &RayTracingPassData::m_threadCountY)
->Field("Thread Count Z", &RayTracingPassData::m_threadCountZ)
->Field("Make Fullscreen Pass", &RayTracingPassData::m_makeFullscreenPass)
;
}
}
RPI::AssetReference m_rayGenerationShaderAssetReference;
AZStd::string m_rayGenerationShaderName;
RPI::AssetReference m_closestHitShaderAssetReference;
AZStd::string m_closestHitShaderName;
RPI::AssetReference m_missShaderAssetReference;
AZStd::string m_missShaderName;
uint32_t m_maxPayloadSize = 64;
uint32_t m_maxAttributeSize = 32;
uint32_t m_maxRecursionDepth = 1;
uint32_t m_threadCountX = 1;
uint32_t m_threadCountY = 1;
uint32_t m_threadCountZ = 1;
bool m_makeFullscreenPass = false;
};
} // namespace RPI
} // namespace AZ
@@ -252,6 +252,9 @@ set(FILES
Source/RayTracing/RayTracingFeatureProcessor.cpp
Source/RayTracing/RayTracingAccelerationStructurePass.cpp
Source/RayTracing/RayTracingAccelerationStructurePass.h
Source/RayTracing/RayTracingPass.cpp
Source/RayTracing/RayTracingPass.h
Source/RayTracing/RayTracingPassData.h
Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp
Source/ReflectionProbe/ReflectionProbe.cpp
Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp
@@ -59,7 +59,10 @@ namespace AZ
static constexpr uint32_t Draw = 0;
static constexpr uint32_t Object = 1;
static constexpr uint32_t Material = 2;
static constexpr uint32_t SubPass = 3;
static constexpr uint32_t Pass = 4;
static constexpr uint32_t View = 5;
static constexpr uint32_t Scene = 6;
};
}
}