convert atom to task graph (#4230)

* Intial attempt to convert the Atom/RHI/FrameScheduler to use the new TaskGraph api

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Avoid enqueuing work on the active task thread if the submitted task
graph is waitable

When submitting a task graph, supplying a wait event implies that
dependent jobs must occur on threads that do not wait on the event (in
the absence of work stealing). This change prevents this by adding a
notion of a task thread enable/disable state, and prohibiting dependent
jobs from being enqueued on waiting threads.

Signed-off-by: Jeremy Ong <jcong@amazon.com>

* Convert RPI/Scene to use TaskGraph pass 1, Culling jobs remain on the old system

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* RemoveTask Graph changes from the FrameScheduler::ExecuteGroups, use old job system instead

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Per review, removing commented out code

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Cleanup debug code, & build fix

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Add a cvar & interface to query whether to use jobs or task graph

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Make TaskGraph assert if you try to wait inside a job

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Fix TaskTest SpawnSubgraph to account for the new TaskGraphEvent assert on wait in a running task

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* 3 minor cleanups. 1) Events always store a ptr to their executor 2) Fix clang compile error 3) remove an early out.

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Fix double group end that was causing assert/crash plus misc minor diff's with development

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Fix deallocation failure on deactivation of the TaskGraphSystemComponent. Also make the system component account for multiple creation in Unit Tests.

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Update with PR feedback
1) Rename UseTaskGraph to IsTaskGraphActive & update related code
2) prefer TaskExecutor::SetInstance
3) add comments and remove commented out code

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Fix incorrect RTTI name for TaskGraphActiveInterface

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

* Move TaskGraphSystemComponent CRC calculation to a shared variable

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>

Co-authored-by: Jeremy Ong <jcong@amazon.com>
This commit is contained in:
rgba16f [Amazon]
2021-09-30 17:45:33 -05:00
committed by GitHub
parent 60a0d2ba01
commit e1c49e436d
18 changed files with 652 additions and 130 deletions
+220 -61
View File
@@ -23,6 +23,8 @@
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Jobs/JobEmpty.h>
#include <AzCore/Task/TaskGraph.h>
#include <AzFramework/Entity/EntityContext.h>
namespace AZ
@@ -92,7 +94,14 @@ namespace AZ
Scene::~Scene()
{
WaitAndCleanCompletionJob(m_simulationCompletion);
if (m_taskGraphActive)
{
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
}
else
{
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
@@ -346,6 +355,47 @@ namespace AZ
return nullptr;
}
void Scene::SimulateTaskGraph()
{
static const AZ::TaskDescriptor simulationTGDesc{"RPI::Scene::Simulate", "Graphics"};
AZ::TaskGraph simulationTG;
for (FeatureProcessorPtr& fp : m_featureProcessors)
{
FeatureProcessor* featureProcessor = fp.get();
simulationTG.AddTask(
simulationTGDesc,
[this, featureProcessor]()
{
featureProcessor->Simulate(m_simulatePacket);
});
}
simulationTG.Detach();
m_simulationFinishedWorkActive = true;
simulationTG.Submit(&m_simulationFinishedTGEvent);
}
void Scene::SimulateJobs()
{
// 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::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
{
AZ_PROFILE_SCOPE(RPI, "Scene: Simulate");
@@ -353,7 +403,17 @@ namespace AZ
m_simulationTime = tickInfo.m_currentGameTime;
// If previous simulation job wasn't done, wait for it to finish.
WaitAndCleanCompletionJob(m_simulationCompletion);
if (m_taskGraphActive)
{
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
}
else
{
WaitAndCleanCompletionJob(m_simulationCompletion);
}
auto taskGraphActiveInterface = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
m_taskGraphActive = taskGraphActiveInterface && taskGraphActiveInterface->IsTaskGraphActive();
if (jobPolicy == RHI::JobPolicy::Serial)
{
@@ -364,22 +424,27 @@ namespace AZ
}
else
{
// Create a new job to track completion.
m_simulationCompletion = aznew AZ::JobCompletion();
for (FeatureProcessorPtr& fp : m_featureProcessors)
if (m_taskGraphActive)
{
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();
SimulateTaskGraph();
}
//[GFX TODO]: the completion job should start here
else
{
SimulateJobs();
}
}
}
void Scene::WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn )
{
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob");
if (!workToWaitOn || workToWaitOn->load())
{
completionTGEvent.Wait();
}
if (workToWaitOn)
{
workToWaitOn->store(false);
}
}
@@ -394,7 +459,7 @@ namespace AZ
completionJob = nullptr;
}
}
void Scene::ConnectEvent(PrepareSceneSrgEvent::Handler& handler)
{
handler.Connect(m_prepareSrgEvent);
@@ -418,12 +483,139 @@ namespace AZ
}
}
void Scene::CollectDrawPacketsTaskGraph()
{
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
AZ::TaskGraphEvent collectDrawPacketsTGEvent;
static const AZ::TaskDescriptor collectDrawPacketsTGDesc{"RPI_Scene_PrepareRender_CollectDrawPackets", "Graphics"};
AZ::TaskGraph collectDrawPacketsTG;
// Launch FeatureProcessor::Render() jobs
for (auto& fp : m_featureProcessors)
{
collectDrawPacketsTG.AddTask(
collectDrawPacketsTGDesc,
[this, &fp]()
{
fp->Render(m_renderPacket);
});
}
collectDrawPacketsTG.Submit(&collectDrawPacketsTGEvent);
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs if m_parallelOctreeTraversal)
bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal;
m_cullingScene->BeginCulling(m_renderPacket.m_views);
AZ::JobCompletion processCullablesCompletion;
for (ViewPtr& viewPtr : m_renderPacket.m_views)
{
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
{
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
},
true, nullptr); //auto-deletes
if (parallelOctreeTraversal)
{
processCullablesJob->SetDependent(&processCullablesCompletion);
processCullablesJob->Start();
}
else
{
processCullablesJob->StartAndWaitForCompletion();
}
}
WaitTGEvent(collectDrawPacketsTGEvent);
processCullablesCompletion.StartAndWaitForCompletion();
}
void Scene::CollectDrawPacketsJobs()
{
AZ_PROFILE_SCOPE(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); // can't call directly because ProcessCullables needs a parent job
},
true, nullptr); //auto-deletes
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
{
processCullablesJob->SetDependent(collectDrawPacketsCompletion);
processCullablesJob->Start();
}
else
{
processCullablesJob->StartAndWaitForCompletion();
}
}
WaitAndCleanCompletionJob(collectDrawPacketsCompletion);
}
void Scene::FinalizeDrawListsTaskGraph()
{
AZ::TaskGraphEvent finalizeDrawListsTGEvent;
static const AZ::TaskDescriptor finalizeDrawListsTGDesc{"RPI_Scene_PrepareRender_FinalizeDrawLists", "Graphics"};
AZ::TaskGraph finalizeDrawListsTG;
for (auto& view : m_renderPacket.m_views)
{
finalizeDrawListsTG.AddTask(
finalizeDrawListsTGDesc,
[view]()
{
view->FinalizeDrawLists();
});
}
finalizeDrawListsTG.Submit(&finalizeDrawListsTGEvent);
WaitTGEvent(finalizeDrawListsTGEvent);
}
void Scene::FinalizeDrawListsJobs()
{
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();
}
WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
}
void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
{
AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
if (m_taskGraphActive)
{
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
}
else
{
AZ_PROFILE_SCOPE(RPI, "WaitForSimulationCompletion");
WaitAndCleanCompletionJob(m_simulationCompletion);
}
@@ -496,44 +688,16 @@ namespace AZ
}
{
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion();
// Launch FeatureProcessor::Render() jobs
for (auto& fp : m_featureProcessors)
if (m_taskGraphActive)
{
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();
CollectDrawPacketsTaskGraph();
}
// 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)
else
{
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();
}
CollectDrawPacketsJobs();
}
WaitAndCleanCompletionJob(collectDrawPacketsCompletion);
m_cullingScene->EndCulling();
// Add dynamic draw data for all the views
@@ -556,20 +720,15 @@ namespace AZ
}
else
{
AZ::JobCompletion* finalizeDrawListsCompletion = aznew AZ::JobCompletion();
for (auto& view : m_renderPacket.m_views)
if (m_taskGraphActive)
{
const auto finalizeDrawListsLambda = [view]()
{
view->FinalizeDrawLists();
};
AZ::Job* finalizeDrawListsJob = AZ::CreateJobFunction(AZStd::move(finalizeDrawListsLambda), true, nullptr); //auto-deletes
finalizeDrawListsJob->SetDependent(finalizeDrawListsCompletion);
finalizeDrawListsJob->Start();
FinalizeDrawListsTaskGraph();
}
else
{
FinalizeDrawListsJobs();
}
AZ_PROFILE_END(RPI);
WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
}
}