Add async animations updating via Task Graph
This commit is contained in:
@@ -6,8 +6,7 @@
|
||||
#include "Engine/Level/Actors/AnimatedModel.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
|
||||
Array<AnimatedModel*> UpdateList;
|
||||
#include "Engine/Threading/TaskGraph.h"
|
||||
|
||||
class AnimationsService : public EngineService
|
||||
{
|
||||
@@ -18,70 +17,110 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void Update() override;
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
class AnimationsSystem : public TaskGraphSystem
|
||||
{
|
||||
public:
|
||||
float DeltaTime, UnscaledDeltaTime, Time, UnscaledTime;
|
||||
void Job(int32 index);
|
||||
void Execute(TaskGraph* graph) override;
|
||||
void PostExecute(TaskGraph* graph) override;
|
||||
};
|
||||
|
||||
AnimationsService AnimationManagerInstance;
|
||||
Array<AnimatedModel*> UpdateList;
|
||||
TaskGraphSystem* Animations::System = nullptr;
|
||||
Delegate<Asset*, ScriptingObject*, uint32, uint32> Animations::DebugFlow;
|
||||
|
||||
void AnimationsService::Update()
|
||||
bool AnimationsService::Init()
|
||||
{
|
||||
PROFILE_CPU_NAMED("Animations");
|
||||
|
||||
// TODO: implement the thread jobs pipeline to run set of tasks at once (use it for multi-threaded rendering and animations evaluation)
|
||||
|
||||
const auto& tickData = Time::Update;
|
||||
const float deltaTime = tickData.DeltaTime.GetTotalSeconds();
|
||||
const float unscaledDeltaTime = tickData.UnscaledDeltaTime.GetTotalSeconds();
|
||||
const float time = tickData.Time.GetTotalSeconds();
|
||||
const float unscaledTime = tickData.UnscaledTime.GetTotalSeconds();
|
||||
|
||||
for (int32 i = 0; i < UpdateList.Count(); i++)
|
||||
{
|
||||
auto animatedModel = UpdateList[i];
|
||||
if (animatedModel->SkinnedModel == nullptr || !animatedModel->SkinnedModel->IsLoaded())
|
||||
continue;
|
||||
|
||||
// Prepare skinning data
|
||||
animatedModel->SetupSkinningData();
|
||||
|
||||
// Update the animation graph and the skinning
|
||||
auto graph = animatedModel->AnimationGraph.Get();
|
||||
if (graph && graph->IsLoaded() && graph->Graph.CanUseWithSkeleton(animatedModel->SkinnedModel)
|
||||
#if USE_EDITOR
|
||||
&& graph->Graph.Parameters.Count() == animatedModel->GraphInstance.Parameters.Count() // It may happen in editor so just add safe check to prevent any crashes
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Lock in editor only (more reloads during asset live editing)
|
||||
ScopeLock lock(animatedModel->AnimationGraph->Locker);
|
||||
#endif
|
||||
|
||||
// Animation delta time can be based on a time since last update or the current delta
|
||||
float dt = animatedModel->UseTimeScale ? deltaTime : unscaledDeltaTime;
|
||||
float t = animatedModel->UseTimeScale ? time : unscaledTime;
|
||||
const float lastUpdateTime = animatedModel->GraphInstance.LastUpdateTime;
|
||||
if (lastUpdateTime > 0 && t > lastUpdateTime)
|
||||
{
|
||||
dt = t - lastUpdateTime;
|
||||
}
|
||||
animatedModel->GraphInstance.LastUpdateTime = t;
|
||||
|
||||
// Evaluate animated nodes pose
|
||||
graph->GraphExecutor.Update(animatedModel->GraphInstance, dt);
|
||||
|
||||
// Update gameplay
|
||||
animatedModel->OnAnimationUpdated();
|
||||
}
|
||||
}
|
||||
UpdateList.Clear();
|
||||
Animations::System = New<AnimationsSystem>();
|
||||
Engine::UpdateGraph->AddSystem(Animations::System);
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimationsService::Dispose()
|
||||
{
|
||||
UpdateList.Resize(0);
|
||||
SAFE_DELETE(Animations::System);
|
||||
}
|
||||
|
||||
void AnimationsSystem::Job(int32 index)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Animations.Job");
|
||||
auto animatedModel = UpdateList[index];
|
||||
auto skinnedModel = animatedModel->SkinnedModel.Get();
|
||||
auto graph = animatedModel->AnimationGraph.Get();
|
||||
if (graph && graph->IsLoaded() && graph->Graph.CanUseWithSkeleton(skinnedModel)
|
||||
#if USE_EDITOR
|
||||
&& graph->Graph.Parameters.Count() == animatedModel->GraphInstance.Parameters.Count() // It may happen in editor so just add safe check to prevent any crashes
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// Prepare skinning data
|
||||
animatedModel->SetupSkinningData();
|
||||
|
||||
// Animation delta time can be based on a time since last update or the current delta
|
||||
float dt = animatedModel->UseTimeScale ? DeltaTime : UnscaledDeltaTime;
|
||||
float t = animatedModel->UseTimeScale ? Time : UnscaledTime;
|
||||
const float lastUpdateTime = animatedModel->GraphInstance.LastUpdateTime;
|
||||
if (lastUpdateTime > 0 && t > lastUpdateTime)
|
||||
{
|
||||
dt = t - lastUpdateTime;
|
||||
}
|
||||
animatedModel->GraphInstance.LastUpdateTime = t;
|
||||
|
||||
// Evaluate animated nodes pose
|
||||
graph->GraphExecutor.Update(animatedModel->GraphInstance, dt);
|
||||
|
||||
// Update gameplay
|
||||
animatedModel->OnAnimationUpdated_Async();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationsSystem::Execute(TaskGraph* graph)
|
||||
{
|
||||
if (UpdateList.Count() == 0)
|
||||
return;
|
||||
|
||||
// Setup data for async update
|
||||
const auto& tickData = Time::Update;
|
||||
DeltaTime = tickData.DeltaTime.GetTotalSeconds();
|
||||
UnscaledDeltaTime = tickData.UnscaledDeltaTime.GetTotalSeconds();
|
||||
Time = tickData.Time.GetTotalSeconds();
|
||||
UnscaledTime = tickData.UnscaledTime.GetTotalSeconds();
|
||||
|
||||
// Schedule work to update all animated models in async
|
||||
Function<void(int32)> job;
|
||||
job.Bind<AnimationsSystem, &AnimationsSystem::Job>(this);
|
||||
graph->DispatchJob(job, UpdateList.Count());
|
||||
}
|
||||
|
||||
void AnimationsSystem::PostExecute(TaskGraph* graph)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Animations.PostExecute");
|
||||
|
||||
// Update gameplay
|
||||
for (int32 index = 0; index < UpdateList.Count(); index++)
|
||||
{
|
||||
auto animatedModel = UpdateList[index];
|
||||
auto skinnedModel = animatedModel->SkinnedModel.Get();
|
||||
auto animGraph = animatedModel->AnimationGraph.Get();
|
||||
if (animGraph && animGraph->IsLoaded() && animGraph->Graph.CanUseWithSkeleton(skinnedModel)
|
||||
#if USE_EDITOR
|
||||
&& animGraph->Graph.Parameters.Count() == animatedModel->GraphInstance.Parameters.Count() // It may happen in editor so just add safe check to prevent any crashes
|
||||
#endif
|
||||
)
|
||||
{
|
||||
animatedModel->OnAnimationUpdated_Sync();
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
UpdateList.Clear();
|
||||
}
|
||||
|
||||
void Animations::AddToUpdate(AnimatedModel* obj)
|
||||
|
||||
Reference in New Issue
Block a user