Part 2 of enabling Atom to use TaskGraph

Enable Atom CullingScene to use TaskGraph
Enable Atom PNG save to use TaskGraph for red/blue color channel swap on save out.

Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com>
This commit is contained in:
rgba16f [Amazon]
2021-11-02 15:02:08 -05:00
committed by GitHub
parent cf1d13fa43
commit 5c072a5d51
10 changed files with 508 additions and 286 deletions
@@ -363,7 +363,11 @@ namespace AZ
{
++m_graphsRemaining;
event->m_executor = this; // Used to validate event is not waited for inside a job
if (event)
{
event->IncWaitCount();
event->m_executor = this; // Used to validate event is not waited for inside a job
}
// Submit all tasks that have no inbound edges
for (Internal::Task& task : graph.Tasks())
@@ -20,6 +20,46 @@ namespace AZ
m_semaphore.acquire();
}
void TaskGraphEvent::IncWaitCount()
{
// guess zero to optimize for single task graph using an event, if multiple are using it then this will take 2+ comp_exch calls
int expectedValue = 0;
while(!m_waitCount.compare_exchange_weak(expectedValue, expectedValue + 1))
{
// value will be negative once event is ready to signal or has been signaled. Shouldn't happen.
AZ_Assert(expectedValue >= 0, "Called TaskGraphEvent::IncWaitCount on a signalled event");
if (expectedValue < 0) // event already signaled, skip
{
return;
}
};
}
void TaskGraphEvent::Signal()
{
// guess one to optimize for single task graph using an event, if multiple are using it then this will take 2+ comp_exch calls
int expectedValue = 1;
while(!m_waitCount.compare_exchange_weak(expectedValue, expectedValue - 1))
{
// It's an error for Signal to be called if no one is waiting, or the event has already been signaled
AZ_Assert(expectedValue > 0, "Called TaskGraphEvent::Signal when event is either signaled or unused");
if (expectedValue < 0) // return if already signaled
{
return;
}
};
if (expectedValue == 1) // This call to Signal decremented the value to 0.
{
expectedValue = 0;
// validate no one incremented the wait count and mark signalling state
if (m_waitCount.compare_exchange_strong(expectedValue, -1))
{
m_semaphore.release();
}
}
}
void TaskToken::PrecedesInternal(TaskToken& comesAfter)
{
AZ_Assert(!m_parent.m_submitted, "Cannot mutate a TaskGraph that was previously submitted.");
@@ -61,14 +61,14 @@ namespace AZ
uint32_t m_index;
};
// A TaskGraphEvent may be used to block until a task graph has finished executing. Usage
// A TaskGraphEvent may be used to block until one or more task graphs has finished executing. Usage
// is NOT recommended for the majority of tasks (prefer to simply containing expanding/contracting
// the graph without synchronization over the course of the frame). However, the event
// is useful for the edges of the computation graph.
//
// You are responsible for ensuring the event object lifetime exceeds the task graph lifetime.
//
// After the TaskGraphEvent is signaled, you are allowed to reuse the same TaskGraphEvent
// After the TaskGraphEvent is signaled, you are NOT allowed to reuse the same TaskGraphEvent
// for a future submission.
class TaskGraphEvent
{
@@ -81,10 +81,12 @@ namespace AZ
friend class TaskGraph;
friend class TaskExecutor;
void IncWaitCount();
void Signal();
AZStd::binary_semaphore m_semaphore;
TaskExecutor* m_executor = nullptr;
AZStd::atomic_int m_waitCount = 0;
TaskExecutor* m_executor = nullptr;
};
// The TaskGraph encapsulates a set of tasks and their interdependencies. After adding
@@ -33,11 +33,6 @@ namespace AZ
return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 });
}
inline void TaskGraphEvent::Signal()
{
m_semaphore.release();
}
template<typename Lambda>
TaskToken TaskGraph::AddTask(TaskDescriptor const& desc, Lambda&& lambda)
{
+6 -5
View File
@@ -610,15 +610,16 @@ namespace UnitTest
g.Follows(e, f);
g.Precedes(d);
TaskGraphEvent ev;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
TaskGraphEvent ev1;
graph.SubmitOnExecutor(*m_executor, &ev1);
ev1.Wait();
EXPECT_EQ(3 | 0b100000, x);
x = 0;
graph.SubmitOnExecutor(*m_executor, &ev);
ev.Wait();
TaskGraphEvent ev2;
graph.SubmitOnExecutor(*m_executor, &ev2);
ev2.Wait();
EXPECT_EQ(3 | 0b100000, x);
}