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.
816 lines
32 KiB
C++
816 lines
32 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <Atom/RHI/CpuProfiler.h>
|
|
|
|
#include <Atom/RPI.Public/Culling.h>
|
|
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawSystem.h>
|
|
#include <Atom/RPI.Public/FeatureProcessorFactory.h>
|
|
#include <Atom/RPI.Public/FeatureProcessor.h>
|
|
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
|
|
#include <Atom/RPI.Public/Pass/RasterPass.h>
|
|
#include <Atom/RPI.Public/RenderPipeline.h>
|
|
#include <Atom/RHI/RHISystemInterface.h>
|
|
#include <Atom/RPI.Public/Scene.h>
|
|
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
|
#include <Atom/RPI.Public/View.h>
|
|
|
|
#include <AzCore/Debug/EventTrace.h>
|
|
#include <AzCore/Debug/Profiler.h>
|
|
#include <AzCore/Jobs/JobFunction.h>
|
|
#include <AzCore/Jobs/JobEmpty.h>
|
|
|
|
#include <AzFramework/Entity/EntityContext.h>
|
|
|
|
namespace AZ
|
|
{
|
|
namespace RPI
|
|
{
|
|
ScenePtr Scene::CreateScene(const SceneDescriptor& sceneDescriptor)
|
|
{
|
|
Scene* scene = aznew Scene();
|
|
for (const auto& fpId : sceneDescriptor.m_featureProcessorNames)
|
|
{
|
|
scene->EnableFeatureProcessor(FeatureProcessorId{ fpId });
|
|
}
|
|
|
|
auto sceneSrgLayout = RPISystemInterface::Get()->GetSceneSrgLayout();
|
|
if (sceneSrgLayout)
|
|
{
|
|
auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
|
|
scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName());
|
|
}
|
|
|
|
return ScenePtr(scene);
|
|
}
|
|
|
|
ScenePtr Scene::CreateSceneFromAsset(Data::Asset<AnyAsset> sceneAsset)
|
|
{
|
|
const SceneDescriptor* sceneDescriptor = GetDataFromAnyAsset<SceneDescriptor>(sceneAsset);
|
|
if (sceneDescriptor == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
ScenePtr scene = Scene::CreateScene(*sceneDescriptor);
|
|
if (scene == nullptr)
|
|
{
|
|
AZ_Error("RPISystem", false, "Failed to create scene from asset %s", sceneAsset.GetHint().c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
return scene;
|
|
}
|
|
|
|
Scene* Scene::GetSceneForEntityContextId(AzFramework::EntityContextId entityContextId)
|
|
{
|
|
// Find the scene for this entity context.
|
|
AZStd::shared_ptr<AzFramework::Scene> scene = AzFramework::EntityContext::FindContainingScene(entityContextId);
|
|
if (scene)
|
|
{
|
|
// Get the RPI::Scene subsystem from the AZFramework Scene.
|
|
RPI::ScenePtr* scenePtr = scene->FindSubsystem<RPI::ScenePtr>();
|
|
if (scenePtr)
|
|
{
|
|
return scenePtr->get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
Scene::Scene()
|
|
{
|
|
m_id = Uuid::CreateRandom();
|
|
m_cullingScene = aznew CullingScene();
|
|
SceneRequestBus::Handler::BusConnect(m_id);
|
|
m_drawFilterTagRegistry = RHI::DrawFilterTagRegistry::Create();
|
|
}
|
|
|
|
Scene::~Scene()
|
|
{
|
|
WaitAndCleanCompletionJob(m_simulationCompletion);
|
|
SceneRequestBus::Handler::BusDisconnect();
|
|
|
|
// Remove all the render pipelines. Need to process queued changes with pass system before and after remove render pipelines
|
|
AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges();
|
|
for (auto it = m_pipelines.begin(); it != m_pipelines.end(); ++it)
|
|
{
|
|
RenderPipelinePtr pipelineToRemove = (*it);
|
|
pipelineToRemove->OnRemovedFromScene(this);
|
|
}
|
|
m_pipelines.clear();
|
|
AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges();
|
|
|
|
Deactivate();
|
|
|
|
delete m_cullingScene;
|
|
}
|
|
|
|
void Scene::Activate()
|
|
{
|
|
AZ_Assert(!m_activated, "Already activated");
|
|
|
|
m_activated = true;
|
|
|
|
m_cullingScene->Activate(this);
|
|
|
|
// We have to tick the PassSystem in order for all the pass attachments to get created.
|
|
// This has to be done before FeatureProcessors are activated, because they may try to
|
|
// create PipelineState objects (PSOs) which would require data from attachments in the
|
|
// the pass tree.
|
|
AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges();
|
|
|
|
for (auto& fp : m_featureProcessors)
|
|
{
|
|
fp->Activate();
|
|
}
|
|
|
|
m_dynamicDrawSystem = static_cast<DynamicDrawSystem*>(RPI::DynamicDrawInterface::Get());
|
|
}
|
|
|
|
void Scene::Deactivate()
|
|
{
|
|
if (!m_activated)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (auto& fp : m_featureProcessors)
|
|
{
|
|
fp->Deactivate();
|
|
}
|
|
|
|
m_cullingScene->Deactivate();
|
|
|
|
m_activated = false;
|
|
m_pipelineStatesLookup.clear();
|
|
m_dynamicDrawSystem = nullptr;
|
|
}
|
|
|
|
FeatureProcessor* Scene::EnableFeatureProcessor(const FeatureProcessorId& featureProcessorId)
|
|
{
|
|
FeatureProcessor* foundFeatureProcessor = GetFeatureProcessor(featureProcessorId);
|
|
|
|
if (foundFeatureProcessor)
|
|
{
|
|
AZ_Warning("Scene", false, "FeatureProcessor '%s' is already enabled for this scene. Will not re-enable.", featureProcessorId.GetCStr());
|
|
return foundFeatureProcessor;
|
|
}
|
|
|
|
// Check to make sure there aren't multiple different implementations of the same interface enabled for the scene
|
|
// otherwise it would be ambiguous which feature processor is returned when GetFeatureProcessor is called with the interface.
|
|
AZ::TypeId interfaceTypeId = FeatureProcessorFactory::Get()->GetFeatureProcessorInterfaceTypeId(featureProcessorId);
|
|
if (!interfaceTypeId.IsNull())
|
|
{
|
|
foundFeatureProcessor = GetFeatureProcessor(interfaceTypeId);
|
|
|
|
if (foundFeatureProcessor)
|
|
{
|
|
AZ_Error("Scene", false, "FeatureProcessor '%s' is already enabled for this scene, which implements the same interface as %s. You cannot have more than one implementation of the same feature processor interface in a scene.", foundFeatureProcessor->RTTI_GetTypeName(), featureProcessorId.GetCStr());
|
|
return foundFeatureProcessor;
|
|
}
|
|
}
|
|
|
|
// The feature processor was not found, so create it
|
|
FeatureProcessorPtr createdFeatureProcessor = FeatureProcessorFactory::Get()->CreateFeatureProcessor(featureProcessorId);
|
|
if (!createdFeatureProcessor)
|
|
{
|
|
AZ_Error("Scene", false, "FeatureProcessor '%s' could not be created. Check to make sure it has been registered with the FeatureProcessorFactory.", featureProcessorId.GetCStr());
|
|
return nullptr;
|
|
}
|
|
|
|
foundFeatureProcessor = createdFeatureProcessor.get();
|
|
|
|
AddFeatureProcessor(AZStd::move(createdFeatureProcessor));
|
|
|
|
return foundFeatureProcessor;
|
|
}
|
|
|
|
void Scene::AddFeatureProcessor(FeatureProcessorPtr fp)
|
|
{
|
|
fp->m_parentScene = this;
|
|
|
|
// If the Scene is not active then we should not activate the new FeatureProcessor either.
|
|
// In this case, the FeatureProcessor will be activated when Scene::Activate() is called.
|
|
if (m_activated)
|
|
{
|
|
fp->Activate();
|
|
}
|
|
|
|
m_featureProcessors.emplace_back(AZStd::move(fp));
|
|
}
|
|
|
|
void Scene::EnableAllFeatureProcessors()
|
|
{
|
|
FeatureProcessorFactory::Get()->EnableAllForScene(this);
|
|
}
|
|
|
|
void Scene::DisableFeatureProcessor(const FeatureProcessorId& featureProcessorId)
|
|
{
|
|
auto foundFeatureProcessor = AZStd::find_if(
|
|
AZStd::begin(m_featureProcessors),
|
|
AZStd::end(m_featureProcessors),
|
|
[featureProcessorId](const FeatureProcessorPtr& fp) { return FeatureProcessorId{ fp->RTTI_GetTypeName() } == featureProcessorId; });
|
|
|
|
if (foundFeatureProcessor != AZStd::end(m_featureProcessors))
|
|
{
|
|
// If the Scene is not active then the removed FeatureProcessor is not active either, so no need to deactivate it.
|
|
if (m_activated)
|
|
{
|
|
(*foundFeatureProcessor)->Deactivate();
|
|
}
|
|
|
|
m_featureProcessors.erase(foundFeatureProcessor);
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("Scene", false, "FeatureProcessor '%s' is already disabled for this scene. Will not re-disable.", featureProcessorId.GetCStr());
|
|
}
|
|
}
|
|
|
|
void Scene::DisableAllFeatureProcessors()
|
|
{
|
|
for (auto& fp : m_featureProcessors)
|
|
{
|
|
fp->Deactivate();
|
|
}
|
|
m_featureProcessors.clear();
|
|
}
|
|
|
|
FeatureProcessor* Scene::GetFeatureProcessor(const FeatureProcessorId& featureProcessorId) const
|
|
{
|
|
AZ::TypeId featureProcessorTypeId = FeatureProcessorFactory::Get()->GetFeatureProcessorTypeId(featureProcessorId);
|
|
|
|
return GetFeatureProcessor(featureProcessorTypeId);
|
|
}
|
|
|
|
FeatureProcessor* Scene::GetFeatureProcessor(const TypeId& featureProcessorTypeId) const
|
|
{
|
|
auto foundFP = AZStd::find_if(
|
|
AZStd::begin(m_featureProcessors),
|
|
AZStd::end(m_featureProcessors),
|
|
[featureProcessorTypeId](const FeatureProcessorPtr& fp) { return fp->RTTI_IsTypeOf(featureProcessorTypeId); });
|
|
|
|
return foundFP == AZStd::end(m_featureProcessors) ? nullptr : (*foundFP).get();
|
|
}
|
|
|
|
void Scene::AddRenderPipeline(RenderPipelinePtr pipeline)
|
|
{
|
|
if (pipeline->m_scene != nullptr)
|
|
{
|
|
AZ_Assert(false, "Pipeline was added to another scene");
|
|
return;
|
|
}
|
|
|
|
auto pipelineId = pipeline->GetId();
|
|
if (GetRenderPipeline(pipelineId))
|
|
{
|
|
AZ_Assert(false, "Pipeline with same name id is already added to this scene. Please set the pipeline with a different id");
|
|
return;
|
|
}
|
|
|
|
pipeline->SetDrawFilterTag(m_drawFilterTagRegistry->AcquireTag(pipelineId));
|
|
|
|
m_pipelines.push_back(pipeline);
|
|
|
|
// Set this pipeline as default if the default pipeline was empty. This pipeline should be the first pipeline be added to the scene
|
|
if (m_defaultPipeline == nullptr)
|
|
{
|
|
m_defaultPipeline = pipeline;
|
|
}
|
|
|
|
pipeline->OnAddedToScene(this);
|
|
PassSystemInterface::Get()->ProcessQueuedChanges();
|
|
pipeline->BuildPipelineViews();
|
|
|
|
// Force to update the lookup table since adding render pipeline would effect any pipeline states created before pass system tick
|
|
RebuildPipelineStatesLookup();
|
|
|
|
AZ_Assert(!m_id.IsNull(), "RPI::Scene needs to have a valid uuid.");
|
|
SceneNotificationBus::Event(m_id, &SceneNotification::OnRenderPipelineAdded, pipeline);
|
|
}
|
|
|
|
void Scene::RemoveRenderPipeline(const RenderPipelineId& pipelineId)
|
|
{
|
|
bool removed = false;
|
|
for (auto it = m_pipelines.begin(); it != m_pipelines.end(); ++it)
|
|
{
|
|
if (pipelineId == (*it)->GetId())
|
|
{
|
|
// process queued changes first before remove pipeline passes
|
|
AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges();
|
|
RenderPipelinePtr pipelineToRemove = (*it);
|
|
|
|
if (m_defaultPipeline == pipelineToRemove)
|
|
{
|
|
m_defaultPipeline = nullptr;
|
|
}
|
|
|
|
m_drawFilterTagRegistry->ReleaseTag(pipelineToRemove->GetDrawFilterTag());
|
|
|
|
pipelineToRemove->OnRemovedFromScene(this);
|
|
m_pipelines.erase(it);
|
|
|
|
SceneNotificationBus::Event(m_id, &SceneNotification::OnRenderPipelineRemoved, pipelineToRemove.get());
|
|
|
|
// If the default pipeline was removed, set to the first one in the list
|
|
if (m_defaultPipeline == nullptr && m_pipelines.size() > 0)
|
|
{
|
|
m_defaultPipeline = m_pipelines[0];
|
|
}
|
|
|
|
AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges();
|
|
RebuildPipelineStatesLookup();
|
|
|
|
removed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
AZ_Assert(removed, "Pipeline %s is not added to this Scene", pipelineId.GetCStr());
|
|
}
|
|
|
|
RenderPipelinePtr Scene::GetRenderPipeline(const RenderPipelineId& pipelineId) const
|
|
{
|
|
for (auto& pipeline : m_pipelines)
|
|
{
|
|
if (pipeline->GetId() == pipelineId)
|
|
{
|
|
return pipeline;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: Simulate");
|
|
|
|
m_simulationTime = tickInfo.m_currentGameTime;
|
|
|
|
// If previous simulation job wasn't done, wait for it to finish.
|
|
WaitAndCleanCompletionJob(m_simulationCompletion);
|
|
|
|
if (jobPolicy == RHI::JobPolicy::Serial)
|
|
{
|
|
for (auto& fp : m_featureProcessors)
|
|
{
|
|
fp->Simulate(m_simulatePacket);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create a new job to track completion.
|
|
m_simulationCompletion = aznew AZ::JobCompletion();
|
|
|
|
for (FeatureProcessorPtr& fp : m_featureProcessors)
|
|
{
|
|
FeatureProcessor* featureProcessor = fp.get();
|
|
const auto jobLambda = [this, featureProcessor]()
|
|
{
|
|
featureProcessor->Simulate(m_simulatePacket);
|
|
};
|
|
|
|
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
|
|
simulationJob->SetDependent(m_simulationCompletion);
|
|
simulationJob->Start();
|
|
}
|
|
//[GFX TODO]: the completion job should start here
|
|
}
|
|
}
|
|
|
|
void Scene::WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob)
|
|
{
|
|
if (completionJob)
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: WaitAndCleanCompletionJob");
|
|
//[GFX TODO]: the completion job should start earlier and wait for completion here
|
|
completionJob->StartAndWaitForCompletion();
|
|
delete completionJob;
|
|
completionJob = nullptr;
|
|
}
|
|
}
|
|
|
|
void Scene::ConnectEvent(PrepareSceneSrgEvent::Handler& handler)
|
|
{
|
|
handler.Connect(m_prepareSrgEvent);
|
|
}
|
|
|
|
void Scene::PrepareSceneSrg()
|
|
{
|
|
if (m_srg)
|
|
{
|
|
// Set value for constants defined in SceneTimeSrg.azsli
|
|
RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
|
|
if (timeIndex.IsValid())
|
|
{
|
|
m_srg->SetConstant(timeIndex, m_simulationTime);
|
|
}
|
|
|
|
// signal any handlers to update values for their partial scene srg
|
|
m_prepareSrgEvent.Signal(m_srg.get());
|
|
|
|
m_srg->Compile();
|
|
}
|
|
}
|
|
|
|
void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: PrepareRender");
|
|
|
|
{
|
|
AZ_PROFILE_SCOPE(RPI, "WaitForSimulationCompletion");
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "WaitForSimulationCompletion");
|
|
WaitAndCleanCompletionJob(m_simulationCompletion);
|
|
}
|
|
|
|
SceneNotificationBus::Event(GetId(), &SceneNotification::OnBeginPrepareRender);
|
|
|
|
// Get active pipelines which need to be rendered and notify them frame started
|
|
AZStd::vector<RenderPipelinePtr> activePipelines;
|
|
{
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene: OnStartFrame");
|
|
for (auto& pipeline : m_pipelines)
|
|
{
|
|
if (pipeline->NeedsRender())
|
|
{
|
|
activePipelines.push_back(pipeline);
|
|
pipeline->OnStartFrame(tickInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return if there is no active render pipeline
|
|
if (activePipelines.empty())
|
|
{
|
|
SceneNotificationBus::Event(GetId(), &SceneNotification::OnEndPrepareRender);
|
|
return;
|
|
}
|
|
|
|
// Init render packet
|
|
m_renderPacket.m_views.clear();
|
|
AZ_Assert(m_cullingScene, "m_cullingScene is not initialized");
|
|
m_renderPacket.m_cullingScene = m_cullingScene;
|
|
m_renderPacket.m_jobPolicy = jobPolicy;
|
|
|
|
|
|
{
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Setup Views");
|
|
|
|
// Collect persistent views from all pipelines to be rendered
|
|
AZStd::map<ViewPtr, RHI::DrawListMask> persistentViews;
|
|
for (const auto& pipeline : activePipelines)
|
|
{
|
|
pipeline->CollectPersistentViews(persistentViews);
|
|
}
|
|
|
|
// Set combined draw list mask for each persistent view and get a global draw list mask for late use
|
|
for (auto& viewInfo : persistentViews)
|
|
{
|
|
viewInfo.first->SetDrawListMask(viewInfo.second);
|
|
m_renderPacket.m_views.push_back(viewInfo.first);
|
|
m_renderPacket.m_drawListMask |= viewInfo.second;
|
|
}
|
|
|
|
// Collect transient views from each feature processor
|
|
FeatureProcessor::PrepareViewsPacket prepareViewPacket;
|
|
AZStd::vector<AZStd::pair<PipelineViewTag, ViewPtr>> transientViews;
|
|
for (auto& fp : m_featureProcessors)
|
|
{
|
|
fp->PrepareViews(prepareViewPacket, transientViews);
|
|
}
|
|
|
|
// Save transient views to RenderPacket and add them to each pipeline.
|
|
for (auto view : transientViews)
|
|
{
|
|
m_renderPacket.m_views.push_back(view.second);
|
|
m_renderPacket.m_drawListMask |= view.second->GetDrawListMask();
|
|
for (auto& itr : activePipelines)
|
|
{
|
|
itr->AddTransientView(view.first, view.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "CollectDrawPackets");
|
|
AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion();
|
|
|
|
// Launch FeatureProcessor::Render() jobs
|
|
for (auto& fp : m_featureProcessors)
|
|
{
|
|
const auto renderLambda = [this, &fp]()
|
|
{
|
|
fp->Render(m_renderPacket);
|
|
};
|
|
|
|
AZ::Job* renderJob = AZ::CreateJobFunction(AZStd::move(renderLambda), true, nullptr); //auto-deletes
|
|
renderJob->SetDependent(collectDrawPacketsCompletion);
|
|
renderJob->Start();
|
|
}
|
|
|
|
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs)
|
|
m_cullingScene->BeginCulling(m_renderPacket.m_views);
|
|
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
|
{
|
|
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
|
{
|
|
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob);
|
|
},
|
|
true, nullptr); //auto-deletes
|
|
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
|
|
{
|
|
processCullablesJob->SetDependent(collectDrawPacketsCompletion);
|
|
processCullablesJob->Start();
|
|
}
|
|
else
|
|
{
|
|
processCullablesJob->StartAndWaitForCompletion();
|
|
}
|
|
}
|
|
|
|
WaitAndCleanCompletionJob(collectDrawPacketsCompletion);
|
|
|
|
m_cullingScene->EndCulling();
|
|
|
|
// Add dynamic draw data for all the views
|
|
if (m_dynamicDrawSystem)
|
|
{
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "DynamicDraw SubmitDrawData");
|
|
m_dynamicDrawSystem->SubmitDrawData(this, m_renderPacket.m_views);
|
|
}
|
|
}
|
|
|
|
{
|
|
AZ_PROFILE_BEGIN(RPI, "FinalizeDrawLists");
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "FinalizeDrawLists");
|
|
if (jobPolicy == RHI::JobPolicy::Serial)
|
|
{
|
|
for (auto& view : m_renderPacket.m_views)
|
|
{
|
|
view->FinalizeDrawLists();
|
|
}
|
|
AZ_PROFILE_END(RPI);
|
|
}
|
|
else
|
|
{
|
|
AZ::JobCompletion* finalizeDrawListsCompletion = aznew AZ::JobCompletion();
|
|
for (auto& view : m_renderPacket.m_views)
|
|
{
|
|
const auto finalizeDrawListsLambda = [view]()
|
|
{
|
|
view->FinalizeDrawLists();
|
|
};
|
|
|
|
AZ::Job* finalizeDrawListsJob = AZ::CreateJobFunction(AZStd::move(finalizeDrawListsLambda), true, nullptr); //auto-deletes
|
|
finalizeDrawListsJob->SetDependent(finalizeDrawListsCompletion);
|
|
finalizeDrawListsJob->Start();
|
|
}
|
|
AZ_PROFILE_END(RPI);
|
|
WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
|
|
}
|
|
}
|
|
|
|
{
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene OnEndPrepareRender");
|
|
SceneNotificationBus::Event(GetId(), &SceneNotification::OnEndPrepareRender);
|
|
}
|
|
}
|
|
|
|
void Scene::OnFrameEnd()
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: OnFrameEnd");
|
|
for (auto& pipeline : m_pipelines)
|
|
{
|
|
if (pipeline->NeedsRender())
|
|
{
|
|
pipeline->OnFrameEnd();
|
|
}
|
|
}
|
|
for (auto& fp : m_featureProcessors)
|
|
{
|
|
fp->OnRenderEnd();
|
|
}
|
|
}
|
|
|
|
void Scene::UpdateSrgs()
|
|
{
|
|
PrepareSceneSrg();
|
|
|
|
for (auto& view : m_renderPacket.m_views)
|
|
{
|
|
view->UpdateSrg();
|
|
}
|
|
}
|
|
|
|
const RHI::ShaderResourceGroup* Scene::GetRHIShaderResourceGroup() const
|
|
{
|
|
if (m_srg.get())
|
|
{
|
|
return m_srg.get()->GetRHIShaderResourceGroup();
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Data::Instance<ShaderResourceGroup> Scene::GetShaderResourceGroup() const
|
|
{
|
|
return m_srg;
|
|
}
|
|
|
|
const SceneId& Scene::GetId() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
bool Scene::SetDefaultRenderPipeline(const RenderPipelineId& pipelineId)
|
|
{
|
|
RenderPipelinePtr newPipeline = GetRenderPipeline(pipelineId);
|
|
if (newPipeline)
|
|
{
|
|
m_defaultPipeline = newPipeline;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RenderPipelinePtr Scene::GetDefaultRenderPipeline() const
|
|
{
|
|
return m_defaultPipeline;
|
|
}
|
|
|
|
const AZStd::vector<RenderPipelinePtr>& Scene::GetRenderPipelines() const
|
|
{
|
|
return m_pipelines;
|
|
}
|
|
|
|
void Scene::OnSceneNotifictaionHandlerConnected(SceneNotification* handler)
|
|
{
|
|
for (auto renderPipeline : m_pipelines)
|
|
{
|
|
handler->OnRenderPipelineAdded(renderPipeline);
|
|
const RenderPipeline::PipelineViewMap& viewsInfo = renderPipeline->GetPipelineViews();
|
|
for (RenderPipeline::PipelineViewMap::const_iterator itr = viewsInfo.begin(); itr != viewsInfo.end(); itr++)
|
|
{
|
|
if (itr->second.m_type == PipelineViewType::Persistent && itr->second.m_views.size() == 1)
|
|
{
|
|
handler->OnRenderPipelinePersistentViewChanged(renderPipeline.get(), itr->first, itr->second.m_views[0], nullptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Scene::ConfigurePipelineState(RHI::DrawListTag drawListTag, RHI::PipelineStateDescriptorForDraw& outPipelineState) const
|
|
{
|
|
auto pipelineStatesItr = m_pipelineStatesLookup.find(drawListTag);
|
|
|
|
if (pipelineStatesItr != m_pipelineStatesLookup.end())
|
|
{
|
|
const PipelineStateList& pipelineStateList = pipelineStatesItr->second;
|
|
|
|
AZ_Error("RPI", pipelineStateList.size() == 1, "Scene::ConfigurePipelineState called for drawListTag [%s] which has [%zu] different pipeline states."
|
|
"Using first pipeline state by default.",
|
|
RHI::RHISystemInterface::Get()->GetDrawListTagRegistry()->GetName(drawListTag).GetCStr(),
|
|
pipelineStateList.size()
|
|
);
|
|
|
|
AZ_Assert(pipelineStateList.size() > 0, "Scene::ConfigurePipelineState called for drawListTag [%s] which has [%zu] pipeline states.",
|
|
RHI::RHISystemInterface::Get()->GetDrawListTagRegistry()->GetName(drawListTag).GetCStr(),
|
|
pipelineStateList.size()
|
|
);
|
|
|
|
outPipelineState.m_renderAttachmentConfiguration = pipelineStateList[0].m_renderAttachmentConfiguration;
|
|
outPipelineState.m_renderStates.m_multisampleState = pipelineStateList[0].m_multisampleState;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const Scene::PipelineStateList& Scene::GetPipelineStates(RHI::DrawListTag drawListTag) const
|
|
{
|
|
static const PipelineStateList emptyList;
|
|
|
|
auto pipelineStatesItr = m_pipelineStatesLookup.find(drawListTag);
|
|
|
|
if (pipelineStatesItr != m_pipelineStatesLookup.end())
|
|
{
|
|
return pipelineStatesItr->second;
|
|
}
|
|
|
|
return emptyList;
|
|
}
|
|
|
|
bool Scene::HasOutputForPipelineState(RHI::DrawListTag drawListTag) const
|
|
{
|
|
return m_pipelineStatesLookup.find(drawListTag) != m_pipelineStatesLookup.end();
|
|
}
|
|
|
|
void Scene::RebuildPipelineStatesLookup()
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: RebuildPipelineStatesLookup");
|
|
m_pipelineStatesLookup.clear();
|
|
|
|
AZStd::queue<ParentPass*> parents;
|
|
for (auto renderPipeline : m_pipelines)
|
|
{
|
|
parents.push(renderPipeline->GetRootPass().get());
|
|
|
|
// Visit all the passes under this root pass
|
|
while (!parents.empty())
|
|
{
|
|
// Visit the first parent pass in the parents queue then remove it from the queue
|
|
ParentPass* parent = parents.front();
|
|
parents.pop();
|
|
|
|
for (const Ptr<Pass>& child : parent->GetChildren())
|
|
{
|
|
ParentPass* asParent = child->AsParent();
|
|
if (asParent)
|
|
{
|
|
// Add to parent queue for late visiting
|
|
parents.push(asParent);
|
|
}
|
|
else if (child->HasDrawListTag())
|
|
{
|
|
// only need to process RasterPass since it and its derived classes need to use draw list tag to
|
|
// acquire OutputAttachmentLayout and MultisampleState
|
|
RasterPass* rasterPass = azrtti_cast<RasterPass*>(child.get());
|
|
if (rasterPass == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RHI::DrawListTag drawListTag = child->GetDrawListTag();
|
|
auto pipelineStatesItr = m_pipelineStatesLookup.find(drawListTag);
|
|
|
|
if (pipelineStatesItr == m_pipelineStatesLookup.end())
|
|
{
|
|
m_pipelineStatesLookup[drawListTag].push_back();
|
|
m_pipelineStatesLookup[drawListTag][0].m_multisampleState = rasterPass->GetMultisampleState();
|
|
m_pipelineStatesLookup[drawListTag][0].m_renderAttachmentConfiguration = rasterPass->GetRenderAttachmentConfiguration();
|
|
rasterPass->SetPipelineStateDataIndex(0);
|
|
}
|
|
else
|
|
{
|
|
PipelineStateList& pipelineStateList = pipelineStatesItr->second;
|
|
size_t size = pipelineStateList.size();
|
|
|
|
u32 index = 0;
|
|
|
|
auto multisampleState = rasterPass->GetMultisampleState();
|
|
auto renderAttachmentConfg = rasterPass->GetRenderAttachmentConfiguration();
|
|
|
|
for (; index < size; ++index)
|
|
{
|
|
PipelineStateData stateData = pipelineStateList[index];
|
|
if (stateData.m_multisampleState == multisampleState
|
|
&& stateData.m_renderAttachmentConfiguration == renderAttachmentConfg)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index < size)
|
|
{
|
|
// Found matching pipeline state data, set index
|
|
rasterPass->SetPipelineStateDataIndex(index);
|
|
}
|
|
else
|
|
{
|
|
// No match found, add new pipeline state data
|
|
pipelineStateList.push_back();
|
|
pipelineStateList[size].m_multisampleState = rasterPass->GetMultisampleState();
|
|
pipelineStateList[size].m_renderAttachmentConfiguration = rasterPass->GetRenderAttachmentConfiguration();
|
|
rasterPass->SetPipelineStateDataIndex(static_cast<AZ::u32>(size));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RenderPipelinePtr Scene::FindRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle)
|
|
{
|
|
for (auto renderPipeline : m_pipelines)
|
|
{
|
|
if (renderPipeline->GetWindowHandle() == windowHandle)
|
|
{
|
|
return renderPipeline;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|