Files
o3de/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingPass.cpp
T
Chris Santora c158ca178f Added new shader reinitialization signaling.
This was done while working on "ATOM-15728 Shader Hot Reload Fails in Debug Build", but it turned out these changes did not actually fix the issue (or any other known hot-reload issue). Still, these improvements are appropriate as they correct logical oversights.

ShaderVariant was not listening to asset reloads. It needs to know when the ShaderVariantAsset reload happens so it can reinitialize it's members as well as propagate reinitialization messages. I added a member for the ShaderAsset as the class needs this to reinitialize itself. So now the class listens for reloads of both the ShaderVariantAsset and the ShaderAsset.

Shader was not listening for ShaderAsset reinitialization events.

Updated the API for ShaderReloadNotificationBus's OnShaderVariantReinitialized to include the ShaderVariant which is the most relevant information (the other information wasn't really being used anyway).
2021-06-15 19:26:11 -07:00

363 lines
18 KiB
C++

/*
* 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);
return;
}
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
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 the View and RayTracingMaterial Srgs
Data::Asset<RPI::ShaderResourceGroupAsset> viewSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RPI::SrgBindingSlot::View);
m_requiresViewSrg = viewSrgAsset.GetId().IsValid();
Data::Asset<RPI::ShaderResourceGroupAsset> rayTracingMaterialSrgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(RayTracingMaterialSrgBindingSlot);
m_requiresRayTracingMaterialSrg = rayTracingMaterialSrgAsset.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::Scene* scene = m_pipeline->GetScene();
RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
AZ_Assert(rayTracingFeatureProcessor, "RayTracingPass requires the RayTracingFeatureProcessor");
RPI::RenderPass::SetupFrameGraphDependencies(frameGraph);
frameGraph.SetEstimatedItemCount(1);
// TLAS
{
const RHI::Ptr<RHI::Buffer>& rayTracingTlasBuffer = rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer();
if (rayTracingTlasBuffer)
{
AZ::RHI::AttachmentId tlasAttachmentId = rayTracingFeatureProcessor->GetTlasAttachmentId();
if (frameGraph.GetAttachmentDatabase().IsAttachmentValid(tlasAttachmentId) == false)
{
[[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportBuffer(tlasAttachmentId, rayTracingTlasBuffer);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to import ray tracing TLAS buffer with error %d", result);
}
uint32_t tlasBufferByteCount = aznumeric_cast<uint32_t>(rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer()->GetDescriptor().m_byteCount);
RHI::BufferViewDescriptor tlasBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, tlasBufferByteCount);
RHI::BufferScopeAttachmentDescriptor desc;
desc.m_attachmentId = tlasAttachmentId;
desc.m_bufferViewDescriptor = tlasBufferViewDescriptor;
desc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Load;
frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
}
}
}
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());
}
}
if (m_requiresRayTracingMaterialSrg)
{
shaderResourceGroups.push_back(rayTracingFeatureProcessor->GetRayTracingMaterialSrg()->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(const RPI::ShaderVariant&)
{
Init();
}
} // namespace Render
} // namespace AZ