You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp

542 lines
19 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 <Atom/RHI/DrawListTagRegistry.h>
#include <Atom/RPI.Public/Pass/PassSystem.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/SceneBus.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Public/ViewProviderBus.h>
#include <Atom/RPI.Public/WindowContext.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
namespace AZ
{
namespace RPI
{
RenderPipelinePtr RenderPipeline::CreateRenderPipeline(const RenderPipelineDescriptor& desc)
{
PassSystemInterface* passSystem = PassSystemInterface::Get();
RenderPipeline* pipeline = aznew RenderPipeline();
Name passName{ desc.m_name };
if (!desc.m_rootPassTemplate.empty())
{
// Create pass from asset if there is a valid one
PassRequest rootRequest;
rootRequest.m_passName = passName;
rootRequest.m_templateName = desc.m_rootPassTemplate;
Ptr<Pass> rootPass = passSystem->CreatePassFromRequest(&rootRequest);
AZ_Assert(rootPass != nullptr && rootPass->AsParent() != nullptr, "Error creating root pass for pipeline!");
pipeline->m_rootPass = rootPass->AsParent();
}
else
{
// Otherwise create an empty root pass with pipeline name
pipeline->m_rootPass = passSystem->CreatePass<ParentPass>(passName);
}
InitializeRenderPipeline(pipeline, desc);
return RenderPipelinePtr(pipeline);
}
RenderPipelinePtr RenderPipeline::CreateRenderPipelineFromAsset(Data::Asset<AnyAsset> pipelineAsset)
{
const RenderPipelineDescriptor* renderPipelineDescriptor = GetDataFromAnyAsset<RenderPipelineDescriptor>(pipelineAsset);
if (renderPipelineDescriptor == nullptr)
{
return nullptr;
}
RenderPipelinePtr pipeline = RenderPipeline::CreateRenderPipeline(*renderPipelineDescriptor);
if (pipeline == nullptr)
{
AZ_Error("RPISystem", false, "Failed to create render pipeline from asset %s", pipelineAsset.GetHint().c_str());
return nullptr;
}
return pipeline;
}
RenderPipelinePtr RenderPipeline::CreateRenderPipelineForWindow(Data::Asset<AnyAsset> pipelineAsset, const WindowContext& windowContext)
{
const RenderPipelineDescriptor* renderPipelineDescriptor = GetDataFromAnyAsset<RenderPipelineDescriptor>(pipelineAsset);
if (renderPipelineDescriptor == nullptr)
{
return nullptr;
}
return CreateRenderPipelineForWindow(*renderPipelineDescriptor, windowContext);
}
RenderPipelinePtr RenderPipeline::CreateRenderPipelineForWindow(const RenderPipelineDescriptor& desc, const WindowContext& windowContext)
{
RenderPipeline* pipeline = aznew RenderPipeline();
PassDescriptor swapChainDescriptor(Name(desc.m_name));
pipeline->m_rootPass = aznew SwapChainPass(swapChainDescriptor, &windowContext, Name(desc.m_rootPassTemplate.c_str()));
pipeline->m_windowHandle = windowContext.GetWindowHandle();
InitializeRenderPipeline(pipeline, desc);
return RenderPipelinePtr(pipeline);
}
void RenderPipeline::InitializeRenderPipeline(RenderPipeline* pipeline, const RenderPipelineDescriptor& desc)
{
pipeline->m_mainViewTag = Name(desc.m_mainViewTagName);
pipeline->m_nameId = desc.m_name.data();
pipeline->m_executeOnce = desc.m_executeOnce;
pipeline->m_originalRenderSettings = desc.m_renderSettings;
pipeline->m_activeRenderSettings = desc.m_renderSettings;
pipeline->m_rootPass->SetRenderPipeline(pipeline);
// Manually create the pipeline so we can gather the view tags from it's passes
pipeline->m_rootPass->Build();
pipeline->m_rootPass->Initialize();
pipeline->m_rootPass->OnInitializationFinished();
pipeline->BuildPipelineViews();
}
RenderPipeline::~RenderPipeline()
{
if (m_rootPass)
{
m_rootPass->SetRenderPipeline(nullptr);
}
}
void RenderPipeline::BuildPipelineViews()
{
if (m_rootPass == nullptr)
{
return;
}
// Get view tags from all passes.
SortedPipelineViewTags viewTags;
m_rootPass->GetPipelineViewTags(viewTags);
// Use a new list for building pipeline views since we may need information from the previous list in m_views in the process
PipelineViewMap newViewsByTag;
for (const auto& tag : viewTags)
{
PipelineViews pipelineViews;
if (m_pipelineViewsByTag.find(tag) != m_pipelineViewsByTag.end())
{
// Copy the content from existing if it already exists
pipelineViews = m_pipelineViewsByTag[tag];
pipelineViews.m_drawListMask.reset();
if (pipelineViews.m_type == PipelineViewType::Transient)
{
pipelineViews.m_views.clear();
}
}
else
{
pipelineViews.m_viewTag = tag;
pipelineViews.m_type = PipelineViewType::Unknown;
}
newViewsByTag[tag] = pipelineViews;
CollectDrawListMaskForViews(newViewsByTag[tag]);
}
m_pipelineViewsByTag = AZStd::move(newViewsByTag);
}
void RenderPipeline::CollectDrawListMaskForViews(PipelineViews& views)
{
views.m_drawListMask.reset();
views.m_passesByDrawList.clear();
m_rootPass->GetViewDrawListInfo(views.m_drawListMask, views.m_passesByDrawList, views.m_viewTag);
}
void RenderPipeline::SetPersistentView(const PipelineViewTag& viewTag, ViewPtr view)
{
auto viewItr = m_pipelineViewsByTag.find(viewTag);
if (viewItr != m_pipelineViewsByTag.end())
{
PipelineViews& pipelineViews = viewItr->second;
if (pipelineViews.m_type == PipelineViewType::Transient)
{
AZ_Assert(false, "View [%s] was set as transient view. Use AddTransientView function to add a view for this tag.", viewTag.GetCStr());
return;
}
if (pipelineViews.m_type == PipelineViewType::Unknown)
{
pipelineViews.m_type = PipelineViewType::Persistent;
pipelineViews.m_views.resize(1);
}
ViewPtr previousView = pipelineViews.m_views[0];
pipelineViews.m_views[0] = view;
if (previousView)
{
previousView->SetPassesByDrawList(nullptr);
}
if (m_scene)
{
SceneNotificationBus::Event(m_scene->GetId(), &SceneNotification::OnRenderPipelinePersistentViewChanged, this, viewTag, view, previousView);
}
}
else
{
AZ_Assert(false, "View [%s] doesn't exist in render pipeline [%s]", viewTag.GetCStr(), m_nameId.GetCStr());
}
}
void RenderPipeline::SetDefaultView(ViewPtr view)
{
SetPersistentView(m_mainViewTag, view);
}
ViewPtr RenderPipeline::GetDefaultView()
{
ViewPtr defaultView;
const AZStd::vector<ViewPtr>& views = GetViews(m_mainViewTag);
if (!views.empty())
{
defaultView = views[0];
}
return defaultView;
}
void RenderPipeline::SetDefaultViewFromEntity(EntityId entityId)
{
ViewPtr cameraView;
ViewProviderBus::EventResult(cameraView, entityId, &ViewProvider::GetView);
if (cameraView)
{
SetDefaultView(cameraView);
}
}
void RenderPipeline::AddTransientView(const PipelineViewTag& viewTag, ViewPtr view)
{
auto viewItr = m_pipelineViewsByTag.find(viewTag);
if (viewItr != m_pipelineViewsByTag.end())
{
PipelineViews& pipelineViews = viewItr->second;
if (pipelineViews.m_type == PipelineViewType::Persistent)
{
AZ_Assert(false, "View [%s] was set as persistent view. Use SetPersistentView function to set a view for this tag", viewTag.GetCStr());
return;
}
if (pipelineViews.m_type == PipelineViewType::Unknown)
{
pipelineViews.m_type = PipelineViewType::Transient;
}
view->SetPassesByDrawList(&pipelineViews.m_passesByDrawList);
pipelineViews.m_views.push_back(view);
}
}
bool RenderPipeline::HasViewTag(const PipelineViewTag& viewTag) const
{
return m_pipelineViewsByTag.find(viewTag) != m_pipelineViewsByTag.end();
}
PipelineViewTag RenderPipeline::GetMainViewTag() const
{
return m_mainViewTag;
}
const AZStd::vector<ViewPtr>& RenderPipeline::GetViews(const PipelineViewTag& viewTag) const
{
auto viewItr = m_pipelineViewsByTag.find(viewTag);
if (viewItr != m_pipelineViewsByTag.end())
{
return viewItr->second.m_views;
}
static AZStd::vector<ViewPtr> emptyList;
return emptyList;
}
const RHI::DrawListMask& RenderPipeline::GetDrawListMask(const PipelineViewTag& viewTag) const
{
auto viewItr = m_pipelineViewsByTag.find(viewTag);
if (viewItr != m_pipelineViewsByTag.end())
{
return viewItr->second.m_drawListMask;
}
static RHI::DrawListMask emptyMask;
return emptyMask;
}
const RenderPipeline::PipelineViewMap& RenderPipeline::GetPipelineViews() const
{
return m_pipelineViewsByTag;
}
void RenderPipeline::OnAddedToScene(Scene* scene)
{
AZ_Assert(m_scene == nullptr, "Pipeline was added to another scene");
m_scene = scene;
PassSystemInterface::Get()->GetRootPass()->AddChild(m_rootPass);
if (m_renderMode != RenderMode::NoRender)
{
m_rootPass->SetEnabled(true);
}
}
void RenderPipeline::OnRemovedFromScene([[maybe_unused]] Scene* scene)
{
AZ_Assert(m_scene == scene, "Pipeline isn't added to the specified scene");
m_scene = nullptr;
m_rootPass->SetEnabled(false);
m_rootPass->QueueForRemoval();
m_drawFilterTag.Reset();
m_drawFilterMask = 0;
}
void RenderPipeline::OnPassModified()
{
if (m_needsPassRecreate)
{
PassSystemInterface* passSystem = PassSystemInterface::Get();
// Process any queued changes before we attempt to reload the pipeline
passSystem->ProcessQueuedChanges();
passSystem->SetHotReloading(true);
// Attempt to re-create hierarchy under root pass
Ptr<ParentPass> newRoot = m_rootPass->Recreate();
newRoot->SetRenderPipeline(this);
// Force processing of queued changes so we can validate the new pipeline
passSystem->ProcessQueuedChanges();
// Validate the new root
PassValidationResults validation;
newRoot->Validate(validation);
if (validation.IsValid())
{
// Remove old pass
m_rootPass->SetRenderPipeline(nullptr);
m_rootPass->QueueForRemoval();
// Set new root
m_rootPass = newRoot;
passSystem->GetRootPass()->AddChild(m_rootPass);
m_wasPassModified = true;
}
else
{
AZ_Printf("PassSystem", "\n>> Pass validation failed after hot reloading pas assets. Reverting to previously valid render pipeline.\n");
validation.PrintValidationIfError();
#if AZ_RPI_ENABLE_PASS_DEBUGGING
AZ_Printf("PassSystem", "\nConstructed pass hierarchy with validation errors is as follows:\n");
newRoot->DebugPrint();
#endif
}
passSystem->SetHotReloading(false);
m_needsPassRecreate = false;
}
if (m_wasPassModified)
{
m_rootPass->SetRenderPipeline(this);
BuildPipelineViews();
m_wasPassModified = false;
if (m_scene)
{
SceneNotificationBus::Event(m_scene->GetId(), &SceneNotification::OnRenderPipelinePassesChanged, this);
}
}
}
bool RenderPipeline::IsExecuteOnce()
{
return m_executeOnce;
}
void RenderPipeline::RemoveFromScene()
{
if (m_scene == nullptr)
{
AZ_Assert(false, "Pipeline isn't added to the any scene");
return;
}
m_scene->RemoveRenderPipeline(m_nameId);
}
void RenderPipeline::OnStartFrame(const TickTimeInfo& tick)
{
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
m_lastRenderStartTime = tick.m_currentGameTime;
OnPassModified();
for (auto& viewItr : m_pipelineViewsByTag)
{
PipelineViews& pipelineViews = viewItr.second;
if (pipelineViews.m_type == PipelineViewType::Transient)
{
// Clear transient views
pipelineViews.m_views.clear();
}
else if (pipelineViews.m_type == PipelineViewType::Persistent)
{
// Reset persistent view: clean draw list mask and draw lists
pipelineViews.m_views[0]->Reset();
pipelineViews.m_views[0]->SetPassesByDrawList(&pipelineViews.m_passesByDrawList);
}
}
}
void RenderPipeline::OnFrameEnd()
{
if (m_renderMode == RenderMode::RenderOnce)
{
RemoveFromRenderTick();
}
}
void RenderPipeline::CollectPersistentViews(AZStd::map<ViewPtr, RHI::DrawListMask>& outViewMasks) const
{
for (auto& viewItr : m_pipelineViewsByTag)
{
const PipelineViews& pipelineViews = viewItr.second;
if (pipelineViews.m_type == PipelineViewType::Persistent)
{
ViewPtr view = pipelineViews.m_views[0];
if (outViewMasks.find(view) == outViewMasks.end())
{
// Add the view to the map with its DrawListMask if the view isn't in the list
outViewMasks[view] = pipelineViews.m_drawListMask;
}
else
{
// Combine the DrawListMask with the existing one if the view already exist.
outViewMasks[view] |= pipelineViews.m_drawListMask;
}
}
}
}
RenderPipelineId RenderPipeline::GetId() const
{
return m_nameId;
}
const Ptr<ParentPass>& RenderPipeline::GetRootPass() const
{
return m_rootPass;
}
void RenderPipeline::SetPassModified()
{
m_wasPassModified = true;
}
void RenderPipeline::SetPassNeedsRecreate()
{
m_needsPassRecreate = true;
}
Scene* RenderPipeline::GetScene() const
{
return m_scene;
}
AzFramework::NativeWindowHandle RenderPipeline::GetWindowHandle() const
{
return m_windowHandle;
}
PipelineRenderSettings& RenderPipeline::GetRenderSettings()
{
return m_activeRenderSettings;
}
const PipelineRenderSettings& RenderPipeline::GetRenderSettings() const
{
return m_activeRenderSettings;
}
void RenderPipeline::RevertRenderSettings()
{
m_activeRenderSettings = m_originalRenderSettings;
}
void RenderPipeline::AddToRenderTickOnce()
{
m_rootPass->SetEnabled(true);
m_renderMode = RenderMode::RenderOnce;
}
void RenderPipeline::AddToRenderTick()
{
m_rootPass->SetEnabled(true);
m_renderMode = RenderMode::RenderEveryTick;
}
void RenderPipeline::RemoveFromRenderTick()
{
m_renderMode = RenderMode::NoRender;
m_rootPass->SetEnabled(false);
}
RenderPipeline::RenderMode RenderPipeline::GetRenderMode() const
{
return m_renderMode;
}
bool RenderPipeline::NeedsRender() const
{
return m_renderMode != RenderMode::NoRender;
}
RHI::DrawFilterTag RenderPipeline::GetDrawFilterTag() const
{
return m_drawFilterTag;
}
RHI::DrawFilterMask RenderPipeline::GetDrawFilterMask() const
{
return m_drawFilterMask;
}
void RenderPipeline::SetDrawFilterTag(RHI::DrawFilterTag tag)
{
m_drawFilterTag = tag;
if (m_drawFilterTag.IsValid())
{
m_drawFilterMask = 1 << tag.GetIndex();
}
else
{
m_drawFilterMask = 0;
}
}
}
}