diff --git a/Source/Engine/AI/Behavior.cpp b/Source/Engine/AI/Behavior.cpp index 94f525541..4f4d9b94d 100644 --- a/Source/Engine/AI/Behavior.cpp +++ b/Source/Engine/AI/Behavior.cpp @@ -3,8 +3,68 @@ #include "Behavior.h" #include "BehaviorKnowledge.h" #include "BehaviorTreeNodes.h" +#include "Engine/Engine/Engine.h" #include "Engine/Engine/Time.h" +#include "Engine/Engine/EngineService.h" #include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Threading/TaskGraph.h" + +class BehaviorSystem : public TaskGraphSystem +{ +public: + Array Behaviors; + void Job(int32 index); + void Execute(TaskGraph* graph) override; +}; + +class BehaviorService : public EngineService +{ +public: + BehaviorService() + : EngineService(TEXT("Behaviors"), 0) + { + } + + bool Init() override; + void Dispose() override; +}; + +BehaviorService BehaviorServiceInstance; +Array UpdateList; +TaskGraphSystem* Behavior::System = nullptr; + +void BehaviorSystem::Job(int32 index) +{ + PROFILE_CPU_NAMED("Behavior.Job"); + Behaviors[index]->UpdateAsync(); +} + +void BehaviorSystem::Execute(TaskGraph* graph) +{ + // Copy list of behaviors to update (in case one of them gets disabled during async jobs) + if (UpdateList.Count() == 0) + return; + Behaviors.Clear(); + Behaviors.Add(UpdateList); + + // Schedule work to update all behaviors in async + Function job; + job.Bind(this); + graph->DispatchJob(job, Behaviors.Count()); +} + +bool BehaviorService::Init() +{ + Behavior::System = New(); + Engine::UpdateGraph->AddSystem(Behavior::System); + return false; +} + +void BehaviorService::Dispose() +{ + UpdateList.Resize(0); + SAFE_DELETE(Behavior::System); +} Behavior::Behavior(const SpawnParams& params) : Script(params) @@ -14,6 +74,43 @@ Behavior::Behavior(const SpawnParams& params) Tree.Changed.Bind(this); } +void Behavior::UpdateAsync() +{ + if (_result != BehaviorUpdateResult::Running) + return; + const BehaviorTree* tree = Tree.Get(); + if (!tree || !tree->Graph.Root) + { + _result = BehaviorUpdateResult::Failed; + Finished(); + return; + } + + // Update timer + _accumulatedTime += Time::Update.DeltaTime.GetTotalSeconds(); + const float updateDeltaTime = 1.0f / Math::Max(tree->Graph.Root->UpdateFPS * UpdateRateScale, ZeroTolerance); + if (_accumulatedTime < updateDeltaTime) + return; + _accumulatedTime -= updateDeltaTime; + _totalTime += updateDeltaTime; + + // Update tree + BehaviorUpdateContext context; + context.Behavior = this; + context.Knowledge = &_knowledge; + context.Memory = _knowledge.Memory; + context.RelevantNodes = &_knowledge.RelevantNodes; + context.DeltaTime = updateDeltaTime; + context.Time = _totalTime; + const BehaviorUpdateResult result = tree->Graph.Root->InvokeUpdate(context); + if (result != BehaviorUpdateResult::Running) + _result = result; + if (_result != BehaviorUpdateResult::Running) + { + Finished(); + } +} + void Behavior::StartLogic() { PROFILE_CPU(); @@ -58,44 +155,12 @@ void Behavior::ResetLogic() void Behavior::OnEnable() { + UpdateList.Add(this); if (AutoStart) StartLogic(); } -void Behavior::OnLateUpdate() +void Behavior::OnDisable() { - if (_result != BehaviorUpdateResult::Running) - return; - PROFILE_CPU(); - const BehaviorTree* tree = Tree.Get(); - if (!tree || !tree->Graph.Root) - { - _result = BehaviorUpdateResult::Failed; - Finished(); - return; - } - - // Update timer - _accumulatedTime += Time::Update.DeltaTime.GetTotalSeconds(); - const float updateDeltaTime = 1.0f / Math::Max(tree->Graph.Root->UpdateFPS * UpdateRateScale, ZeroTolerance); - if (_accumulatedTime < updateDeltaTime) - return; - _accumulatedTime -= updateDeltaTime; - _totalTime += updateDeltaTime; - - // Update tree - BehaviorUpdateContext context; - context.Behavior = this; - context.Knowledge = &_knowledge; - context.Memory = _knowledge.Memory; - context.RelevantNodes = &_knowledge.RelevantNodes; - context.DeltaTime = updateDeltaTime; - context.Time = _totalTime; - const BehaviorUpdateResult result = tree->Graph.Root->InvokeUpdate(context); - if (result != BehaviorUpdateResult::Running) - _result = result; - if (_result != BehaviorUpdateResult::Running) - { - Finished(); - } + UpdateList.Remove(this); } diff --git a/Source/Engine/AI/Behavior.h b/Source/Engine/AI/Behavior.h index 7aad11c99..37b9c1c44 100644 --- a/Source/Engine/AI/Behavior.h +++ b/Source/Engine/AI/Behavior.h @@ -15,6 +15,12 @@ API_CLASS() class FLAXENGINE_API Behavior : public Script { API_AUTO_SERIALIZATION(); DECLARE_SCRIPTING_TYPE(Behavior); + friend class BehaviorSystem; + + /// + /// The system for behaviors update. + /// + API_FIELD(ReadOnly) static class TaskGraphSystem* System; private: BehaviorKnowledge _knowledge; @@ -23,6 +29,8 @@ private: BehaviorUpdateResult _result = BehaviorUpdateResult::Success; void* _memory = nullptr; + void UpdateAsync(); + public: /// /// Behavior Tree asset to use for logic execution. @@ -82,5 +90,5 @@ public: // [Script] void OnEnable() override; - void OnLateUpdate() override; + void OnDisable() override; };