diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index 089a6168b1..d4053d0b31 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -91,6 +91,8 @@ #include #include +#include +#include #include #include #include @@ -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); diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp new file mode 100644 index 0000000000..2c0c7e986e --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + RPI::Ptr RayTracingPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew RayTracingPass(descriptor); + return pass; + } + + RayTracingPass::RayTracingPass(const RPI::PassDescriptor& descriptor) + : RenderPass(descriptor) + , m_passDescriptor(descriptor) + { + RHI::Ptr 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 device = RHI::RHISystemInterface::Get()->GetDevice(); + + m_passData = RPI::PassUtils::GetPassData(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 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 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 RayTracingPass::LoadShader(const RPI::AssetReference& shaderAssetReference) + { + Data::Asset 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(); + if (!rayTracingFeatureProcessor) + { + return; + } + + if (!m_rayTracingShaderTable) + { + RHI::Ptr 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(); + 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 descriptor = AZStd::make_shared(); + + 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(); + 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 shaderResourceGroups = + { + m_shaderResourceGroup->GetRHIShaderResourceGroup(), + rayTracingFeatureProcessor->GetRayTracingSceneSrg()->GetRHIShaderResourceGroup() + }; + + if (m_requiresViewSrg) + { + const AZStd::vector& views = m_pipeline->GetViews(m_passData->m_pipelineViewTag); + if (views.size() > 0) + { + shaderResourceGroups.push_back(views[0]->GetRHIShaderResourceGroup()); + } + } + + dispatchRaysItem.m_shaderResourceGroupCount = aznumeric_cast(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& 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 diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h new file mode 100644 index 0000000000..935d034513 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.h @@ -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 +#include +#include +#include +#include +#include + +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 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& 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 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 m_rayGenerationShader; + Data::Instance m_missShader; + Data::Instance m_closestHitShader; + RHI::Ptr m_rayTracingPipelineState; + RHI::ConstPtr m_globalPipelineState; + RHI::Ptr m_rayTracingShaderTable; + bool m_requiresViewSrg = false; + }; + } // namespace RPI +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h new file mode 100644 index 0000000000..bc15a8372c --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPassData.h @@ -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 +#include + +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(context)) + { + serializeContext->Class() + ->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 + diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 8926b0c19f..064e49922d 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -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 diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h index c376112290..52b75c4157 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Base.h @@ -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; }; } }