EMotion FX: Taskify dual quat skinning deformer (#5368)

* Moved from job system to task graph for the multi-threaded dual quaternion skinning deformer.
* Prepare the task graph at init time and reuse it at runtime.

Signed-off-by: Benjamin Jillich <jillich@amazon.com>
This commit is contained in:
Benjamin Jillich
2021-11-09 13:07:20 +01:00
committed by GitHub
parent 3b05e6ab1e
commit f918b12b5a
2 changed files with 58 additions and 20 deletions
@@ -27,6 +27,8 @@ namespace EMotionFX
DualQuatSkinDeformer::DualQuatSkinDeformer(Mesh* mesh)
: MeshDeformer(mesh)
{
AZ::TaskGraphActiveInterface* taskGraphActiveInterface = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
m_useTaskGraph = taskGraphActiveInterface && taskGraphActiveInterface->IsTaskGraphActive();
}
DualQuatSkinDeformer::~DualQuatSkinDeformer()
@@ -79,9 +81,8 @@ namespace EMotionFX
{
const Actor* actor = actorInstance->GetActor();
const Pose* pose = actorInstance->GetTransformData()->GetCurrentPose();
const uint32 numVertices = m_mesh->GetNumVertices();
// pre-calculate the skinning matrices
// Calculate the skinning matrices based on the current pose.
for (BoneInfo& boneInfo : m_bones)
{
const size_t nodeIndex = boneInfo.m_nodeNr;
@@ -89,27 +90,38 @@ namespace EMotionFX
boneInfo.m_dualQuat.FromRotationTranslation(skinTransform.m_rotation, skinTransform.m_position);
}
AZ::JobCompletion jobCompletion;
// Split up the skinned vertices into batches.
const AZ::u32 numBatches = aznumeric_caster(ceilf(aznumeric_cast<float>(numVertices) / aznumeric_cast<float>(s_numVerticesPerBatch)));
for (AZ::u32 batchIndex = 0; batchIndex < numBatches; ++batchIndex)
if (m_useTaskGraph)
{
const AZ::u32 startVertex = batchIndex * s_numVerticesPerBatch;
const AZ::u32 endVertex = AZStd::min(startVertex + s_numVerticesPerBatch, numVertices);
// Create a job for every batch and skin them simultaneously.
AZ::JobContext* jobContext = nullptr;
AZ::Job* job = AZ::CreateJobFunction([this, startVertex, endVertex]()
{
SkinRange(m_mesh, startVertex, endVertex, m_bones);
}, /*isAutoDelete=*/true, jobContext);
job->SetDependent(&jobCompletion);
job->Start();
// Skin the vertices by executing the task graph.
AZ::TaskGraphEvent finishedEvent;
m_taskGraph.Submit(&finishedEvent);
finishedEvent.Wait();
}
else
{
AZ::JobCompletion jobCompletion;
jobCompletion.StartAndWaitForCompletion();
// Split up the skinned vertices into batches.
const uint32 numVertices = m_mesh->GetNumVertices();
const AZ::u32 numBatches = aznumeric_caster(ceilf(aznumeric_cast<float>(numVertices) / aznumeric_cast<float>(s_numVerticesPerBatch)));
for (AZ::u32 batchIndex = 0; batchIndex < numBatches; ++batchIndex)
{
const AZ::u32 startVertex = batchIndex * s_numVerticesPerBatch;
const AZ::u32 endVertex = AZStd::min(startVertex + s_numVerticesPerBatch, numVertices);
// Create a job for every batch and skin them simultaneously.
AZ::JobContext* jobContext = nullptr;
AZ::Job* job = AZ::CreateJobFunction([this, startVertex, endVertex]()
{
SkinRange(m_mesh, startVertex, endVertex, m_bones);
}, /*isAutoDelete=*/true, jobContext);
job->SetDependent(&jobCompletion);
job->Start();
}
jobCompletion.StartAndWaitForCompletion();
}
}
void DualQuatSkinDeformer::SkinRange(Mesh* mesh, AZ::u32 startVertex, AZ::u32 endVertex, const AZStd::vector<BoneInfo>& boneInfos)
@@ -340,5 +352,28 @@ namespace EMotionFX
}
}
}
if (m_useTaskGraph)
{
// Prepare the task graph
// Split up the to be skinned vertices into batches. As the mesh does not change at runtime, the task graph can
// be prepared at init time and be reused at runtime.
const uint32 numVertices = m_mesh->GetNumVertices();
const AZ::u32 numBatches = aznumeric_caster(ceilf(aznumeric_cast<float>(numVertices) / aznumeric_cast<float>(s_numVerticesPerBatch)));
for (AZ::u32 batchIndex = 0; batchIndex < numBatches; ++batchIndex)
{
const AZ::u32 startVertex = batchIndex * s_numVerticesPerBatch;
const AZ::u32 endVertex = AZStd::min(startVertex + s_numVerticesPerBatch, numVertices);
// Create a task for every batch and skin them simultaneously.
AZ::TaskDescriptor taskDescriptor{"DualQuatSkinRange", "Animation"};
m_taskGraph.AddTask(
taskDescriptor,
[this, startVertex, endVertex]()
{
SkinRange(m_mesh, startVertex, endVertex, m_bones);
});
}
}
}
} // namespace EMotionFX
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzCore/Task/TaskGraph.h>
#include <AzCore/Outcome/Outcome.h>
#include "EMotionFXConfig.h"
#include <MCore/Source/DualQuaternion.h>
@@ -138,6 +139,8 @@ namespace EMotionFX
//! Number of vertices per batch/job used for multi-threaded software skinning.
static constexpr AZ::u32 s_numVerticesPerBatch = 10000;
AZ::TaskGraph m_taskGraph;
bool m_useTaskGraph = true;
/**
* Default constructor.