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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user