2020-08-11 01:36:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "EntitySystem/MovieSceneEntitySystemRunner.h"
# include "EntitySystem/MovieSceneEntitySystemLinker.h"
# include "EntitySystem/MovieSceneEntityMutations.h"
# include "EntitySystem/BuiltInComponentTypes.h"
2022-05-04 16:14:59 -04:00
# include "Evaluation/PreAnimatedState/MovieScenePreAnimatedCaptureSource.h"
2020-08-11 01:36:57 -04:00
# include "ProfilingDebugging/CountersTrace.h"
2021-12-03 14:24:33 -05:00
DECLARE_CYCLE_STAT ( TEXT ( " ECS System Cost " ) , MovieSceneEval_TotalGTCost , STATGROUP_MovieSceneEval ) ;
2020-08-11 01:36:57 -04:00
2021-12-03 14:24:33 -05:00
DECLARE_CYCLE_STAT ( TEXT ( " Spawn Phase " ) , MovieSceneEval_SpawnPhase , STATGROUP_MovieSceneECS ) ;
DECLARE_CYCLE_STAT ( TEXT ( " Instantiation Phase " ) , MovieSceneEval_InstantiationPhase , STATGROUP_MovieSceneECS ) ;
DECLARE_CYCLE_STAT ( TEXT ( " Instantiation Async Tasks " ) , MovieSceneEval_AsyncInstantiationTasks , STATGROUP_MovieSceneECS ) ;
DECLARE_CYCLE_STAT ( TEXT ( " Post Instantiation " ) , MovieSceneEval_PostInstantiation , STATGROUP_MovieSceneECS ) ;
DECLARE_CYCLE_STAT ( TEXT ( " Evaluation Phase " ) , MovieSceneEval_EvaluationPhase , STATGROUP_MovieSceneECS ) ;
DECLARE_CYCLE_STAT ( TEXT ( " Finalization Phase " ) , MovieSceneEval_FinalizationPhase , STATGROUP_MovieSceneECS ) ;
DECLARE_CYCLE_STAT ( TEXT ( " Post Evaluation Phase " ) , MovieSceneEval_PostEvaluationPhase , STATGROUP_MovieSceneECS ) ;
2020-08-11 01:36:57 -04:00
TRACE_DECLARE_INT_COUNTER ( MovieSceneEntitySystemFlushes , TEXT ( " MovieScene/ECSFlushes " ) ) ;
TRACE_DECLARE_INT_COUNTER ( MovieSceneEntitySystemEvaluations , TEXT ( " MovieScene/ECSEvaluations " ) ) ;
FMovieSceneEntitySystemRunner : : FMovieSceneEntitySystemRunner ( )
2021-08-03 11:56:47 -04:00
: CompletionTask ( nullptr )
2020-09-24 00:43:27 -04:00
, GameThread ( ENamedThreads : : GameThread_Local )
, CurrentPhase ( UE : : MovieScene : : ESystemPhase : : None )
2020-08-11 01:36:57 -04:00
{
}
FMovieSceneEntitySystemRunner : : ~ FMovieSceneEntitySystemRunner ( )
{
if ( IsAttachedToLinker ( ) )
{
DetachFromLinker ( ) ;
}
}
void FMovieSceneEntitySystemRunner : : AttachToLinker ( UMovieSceneEntitySystemLinker * InLinker )
{
if ( ! ensureMsgf ( InLinker , TEXT ( " Can't attach to a null linker! " ) ) )
{
return ;
}
2021-08-03 11:56:47 -04:00
if ( ! ensureMsgf ( WeakLinker . IsExplicitlyNull ( ) , TEXT ( " This runner is already attached to a linker " ) ) )
{
if ( ensureMsgf ( WeakLinker . IsValid ( ) , TEXT ( " Our previous linker isn't valid anymore! We will permit attaching to a new one. " ) ) )
2021-09-27 19:54:25 -04:00
{
return ;
}
2021-08-03 11:56:47 -04:00
}
2020-08-11 01:36:57 -04:00
2021-08-03 11:56:47 -04:00
WeakLinker = InLinker ;
InLinker - > Events . AbandonLinker . AddRaw ( this , & FMovieSceneEntitySystemRunner : : OnLinkerAbandon ) ;
}
bool FMovieSceneEntitySystemRunner : : IsAttachedToLinker ( ) const
{
return ! WeakLinker . IsExplicitlyNull ( ) ;
2020-08-11 01:36:57 -04:00
}
void FMovieSceneEntitySystemRunner : : DetachFromLinker ( )
{
2021-08-03 11:56:47 -04:00
if ( ensureMsgf ( ! WeakLinker . IsExplicitlyNull ( ) , TEXT ( " This runner is not attached to any linker " ) ) )
2020-08-11 01:36:57 -04:00
{
2021-08-03 11:56:47 -04:00
if ( ensureMsgf ( WeakLinker . IsValid ( ) , TEXT ( " This runner is attached to an invalid linker! " ) ) )
{
OnLinkerAbandon ( WeakLinker . Get ( ) ) ;
}
else
{
WeakLinker . Reset ( ) ;
2021-09-27 19:54:25 -04:00
}
2020-08-11 01:36:57 -04:00
}
}
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * FMovieSceneEntitySystemRunner : : GetLinker ( ) const
2020-08-11 01:36:57 -04:00
{
2021-08-03 11:56:47 -04:00
return WeakLinker . Get ( ) ;
}
UE : : MovieScene : : FEntityManager * FMovieSceneEntitySystemRunner : : GetEntityManager ( ) const
2021-09-27 19:54:25 -04:00
{
2021-08-03 11:56:47 -04:00
if ( UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) )
2020-08-11 01:36:57 -04:00
{
return & Linker - > EntityManager ;
}
return nullptr ;
}
2021-08-03 11:56:47 -04:00
UE : : MovieScene : : FInstanceRegistry * FMovieSceneEntitySystemRunner : : GetInstanceRegistry ( ) const
2020-08-11 01:36:57 -04:00
{
2021-08-03 11:56:47 -04:00
if ( UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) )
2020-08-11 01:36:57 -04:00
{
return Linker - > GetInstanceRegistry ( ) ;
}
return nullptr ;
}
bool FMovieSceneEntitySystemRunner : : HasQueuedUpdates ( ) const
{
2021-08-03 11:56:47 -04:00
if ( UpdateQueue . Num ( ) ! = 0 | | DissectedUpdates . Num ( ) ! = 0 )
{
2021-09-27 19:54:25 -04:00
return true ;
}
2021-12-03 02:53:51 -05:00
if ( const UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) )
2021-09-27 19:54:25 -04:00
{
2021-12-03 02:53:51 -05:00
return Linker - > HasStructureChangedSinceLastRun ( ) ;
2021-08-03 11:56:47 -04:00
}
return false ;
2020-08-11 01:36:57 -04:00
}
bool FMovieSceneEntitySystemRunner : : HasQueuedUpdates ( FInstanceHandle InInstanceHandle ) const
{
return Algo : : FindBy ( UpdateQueue , InInstanceHandle , & FMovieSceneUpdateRequest : : InstanceHandle ) ! = nullptr | |
Algo : : FindBy ( DissectedUpdates , InInstanceHandle , & FDissectedUpdate : : InstanceHandle ) ! = nullptr ;
}
2020-10-29 13:38:15 -04:00
void FMovieSceneEntitySystemRunner : : QueueUpdate ( const FMovieSceneContext & Context , FInstanceHandle InInstanceHandle )
2020-08-11 01:36:57 -04:00
{
UpdateQueue . Add ( FMovieSceneUpdateRequest { Context , InInstanceHandle } ) ;
}
2020-10-29 13:38:15 -04:00
void FMovieSceneEntitySystemRunner : : Update ( const FMovieSceneContext & Context , FInstanceHandle Instance )
2020-08-11 01:36:57 -04:00
{
2021-02-18 18:13:28 -04:00
if ( UpdateQueue . Num ( ) > 0 )
{
2021-08-18 21:15:01 -04:00
UE_LOG ( LogMovieSceneECS , Warning , TEXT ( " Updates are already queued! This will run those updates as well, which might not be what's intended. " ) ) ;
2021-02-18 18:13:28 -04:00
}
2020-08-11 01:36:57 -04:00
// Queue our one update and flush immediately.
2020-10-29 13:38:15 -04:00
QueueUpdate ( Context , Instance ) ;
2020-08-11 01:36:57 -04:00
Flush ( ) ;
}
void FMovieSceneEntitySystemRunner : : Flush ( )
{
using namespace UE : : MovieScene ;
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
2020-08-11 01:36:57 -04:00
// Check that we are attached to a linker that allows starting a new evaluation.
2021-08-03 11:56:47 -04:00
if ( ! ensureMsgf ( Linker , TEXT ( " Runner isn't attached to a valid linker " ) ) )
2020-08-11 01:36:57 -04:00
{
return ;
}
2021-08-03 11:56:47 -04:00
2021-01-21 16:22:06 -04:00
if ( ! Linker - > StartEvaluation ( * this ) )
2020-08-11 01:36:57 -04:00
{
return ;
}
SCOPE_CYCLE_COUNTER ( MovieSceneEval_TotalGTCost ) ;
TRACE_COUNTER_INCREMENT ( MovieSceneEntitySystemFlushes ) ;
// We need to run the system from the game thread so we know we can fire events and callbacks from here.
check ( IsInGameThread ( ) ) ;
// Our entity manager cannot be locked down for us to continue. Something must have left it locked if
// this check fails
FEntityManager & EntityManager = Linker - > EntityManager ;
check ( ! EntityManager . IsLockedDown ( ) ) ;
EntityManager . SetDispatchThread ( ENamedThreads : : GameThread_Local ) ;
EntityManager . SetGatherThread ( ENamedThreads : : GameThread_Local ) ;
2021-05-25 02:43:26 -04:00
// We specifically only check whether the entity manager has changed since the last instantation once
// to ensure that we are not vulnerable to infinite loops where components are added/removed in post-evaluation
2021-12-03 02:53:51 -05:00
bool bStructureHadChanged = Linker - > HasStructureChangedSinceLastRun ( ) ;
2021-05-25 02:43:26 -04:00
2020-08-11 01:36:57 -04:00
// Start flushing the update queue... keep flushing as long as we have work to do.
2021-12-03 01:40:08 -05:00
while ( UpdateQueue . Num ( ) > 0 | |
DissectedUpdates . Num ( ) > 0 | |
bStructureHadChanged )
2020-08-11 01:36:57 -04:00
{
DoFlushUpdateQueueOnce ( ) ;
2021-05-25 02:43:26 -04:00
bStructureHadChanged = false ;
2020-08-11 01:36:57 -04:00
}
Linker - > EndEvaluation ( * this ) ;
}
void FMovieSceneEntitySystemRunner : : DoFlushUpdateQueueOnce ( )
{
using namespace UE : : MovieScene ;
TRACE_COUNTER_INCREMENT ( MovieSceneEntitySystemEvaluations ) ;
2022-01-12 12:23:06 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FMovieSceneEntitySystemRunner : : DoFlushUpdateQueueOnce ) ;
2020-08-11 01:36:57 -04:00
// Setup the completion task that we can wait on.
CompletionTask = TGraphTask < FNullGraphTask > : : CreateTask ( nullptr , ENamedThreads : : GameThread )
. ConstructAndHold ( TStatId ( ) , ENamedThreads : : GameThread_Local ) ;
// Set the debug vizualizer's entity manager pointer, so all debugging happening here will show
// relevant information. We need to set it here instead of higher up because we could have, say,
// a blocking sequence triggering another blocking sequence via an event track. The nested call stack
// of the second sequence needs to show debug information relevant to its private linker, but when
// we return back up to the first sequence, which might still have another update round (such as
// the other side of the dissected update range around the event), we need to set the pointer back
// again.
TGuardValue < FEntityManager * > DebugVizGuard ( GEntityManagerForDebuggingVisualizers , GetEntityManager ( ) ) ;
2022-05-04 16:14:59 -04:00
// Also reset the capture source scope so that each group of sequences tied to a given linker starts
// with a clean slate.
TGuardValue < FScopedPreAnimatedCaptureSource * > CaptureSourceGuard ( FScopedPreAnimatedCaptureSource : : GetCaptureSourcePtr ( ) , nullptr ) ;
2020-08-11 01:36:57 -04:00
// Entry point to the whole ECS loop... this will either unroll in the current thread's call stack
// if there's not much to do, or it will start queuing up tasks on the task graph.
// We immediately wait for the completion task to be executed.
{
GameThread_ProcessQueue ( ) ;
}
2021-05-25 02:43:26 -04:00
2020-08-11 01:36:57 -04:00
FTaskGraphInterface : : Get ( ) . WaitUntilTaskCompletes ( CompletionTask - > GetCompletionEvent ( ) , ENamedThreads : : GameThread_Local ) ;
2021-05-25 02:43:26 -04:00
2020-08-11 01:36:57 -04:00
CompletionTask = nullptr ;
// Now run the post-evaluation logic, which contains stuff we don't want to run from inside a task
// graph call.
2021-05-25 02:43:26 -04:00
GameThread_EvaluationFinalizationPhase ( ) ;
2020-08-11 01:36:57 -04:00
GameThread_PostEvaluationPhase ( ) ;
}
void FMovieSceneEntitySystemRunner : : GameThread_ProcessQueue ( )
{
using namespace UE : : MovieScene ;
2021-12-03 01:40:08 -05:00
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-08-11 01:36:57 -04:00
FInstanceRegistry * InstanceRegistry = GetInstanceRegistry ( ) ;
if ( DissectedUpdates . Num ( ) = = 0 )
{
TArray < TRange < FFrameTime > > Dissections ;
TSet < FInstanceHandle , DefaultKeyFuncs < FInstanceHandle > , TInlineSetAllocator < 256 > > Updates ;
TArray < FMovieSceneUpdateRequest > TempUpdateQueue ;
Swap ( TempUpdateQueue , UpdateQueue ) ;
for ( int32 UpdateIndex = 0 ; UpdateIndex < TempUpdateQueue . Num ( ) ; + + UpdateIndex )
{
FMovieSceneUpdateRequest Request = TempUpdateQueue [ UpdateIndex ] ;
if ( ! InstanceRegistry - > IsHandleValid ( Request . InstanceHandle ) )
{
continue ;
}
// Already have an update for this
else if ( Updates . Contains ( Request . InstanceHandle ) )
{
UpdateQueue . Add ( Request ) ;
continue ;
}
Updates . Add ( Request . InstanceHandle ) ;
// Give the instance an opportunity to dissect the range into distinct evaluations
FSequenceInstance & Instance = InstanceRegistry - > MutateInstance ( Request . InstanceHandle ) ;
Instance . DissectContext ( Linker , Request . Context , Dissections ) ;
if ( Dissections . Num ( ) ! = 0 )
{
2021-12-03 01:40:08 -05:00
for ( int32 Index = 0 ; Index < Dissections . Num ( ) - 1 ; + + Index )
2020-08-11 01:36:57 -04:00
{
2021-12-03 01:40:08 -05:00
FDissectedUpdate Dissection {
2021-05-14 15:31:40 -04:00
FMovieSceneContext ( FMovieSceneEvaluationRange ( Dissections [ Index ] , Request . Context . GetFrameRate ( ) , Request . Context . GetDirection ( ) ) , Request . Context . GetStatus ( ) ) ,
2020-08-11 01:36:57 -04:00
Request . InstanceHandle ,
Index
} ;
DissectedUpdates . Add ( Dissection ) ;
}
// Add the last one with MAX_int32 so it gets evaluated with all the others in this flush
2021-12-03 01:40:08 -05:00
FDissectedUpdate Dissection {
2021-05-14 15:31:40 -04:00
FMovieSceneContext ( FMovieSceneEvaluationRange ( Dissections . Last ( ) , Request . Context . GetFrameRate ( ) , Request . Context . GetDirection ( ) ) , Request . Context . GetStatus ( ) ) ,
2020-08-11 01:36:57 -04:00
Request . InstanceHandle ,
MAX_int32
} ;
DissectedUpdates . Add ( Dissection ) ;
Dissections . Reset ( ) ;
}
else
{
DissectedUpdates . Add ( FDissectedUpdate { Request . Context , Request . InstanceHandle , MAX_int32 } ) ;
}
MarkForUpdate ( Request . InstanceHandle ) ;
}
}
2021-09-27 19:54:25 -04:00
else
{
// Look for the next batch of updates, and mark the respective sequence instances as currently updating.
const int32 PredicateOrder = DissectedUpdates [ 0 ] . Order ;
for ( int32 Index = 0 ; Index < DissectedUpdates . Num ( ) & & DissectedUpdates [ Index ] . Order = = PredicateOrder ; + + Index )
{
const FDissectedUpdate & Update = DissectedUpdates [ Index ] ;
MarkForUpdate ( Update . InstanceHandle ) ;
}
}
2020-08-11 01:36:57 -04:00
2021-12-03 02:55:51 -05:00
// If we have no instances marked for update, we are running an evaluation probably because some
// structural changes have occured in the entity manager (out of date instantiation serial number
// in the linker). So we mark everything for update, so that PreEvaluation/PostEvaluation callbacks
// and legacy templates are correctly executed.
if ( CurrentInstances . Num ( ) = = 0 )
{
for ( const FSequenceInstance & Instance : InstanceRegistry - > GetSparseInstances ( ) )
{
MarkForUpdate ( Instance . GetInstanceHandle ( ) ) ;
}
}
2020-08-11 01:36:57 -04:00
// Let sequence instances do any pre-evaluation work.
for ( FInstanceHandle UpdatedInstanceHandle : CurrentInstances )
{
InstanceRegistry - > MutateInstance ( UpdatedInstanceHandle ) . PreEvaluation ( Linker ) ;
}
// Process updates
GameThread_SpawnPhase ( ) ;
}
void FMovieSceneEntitySystemRunner : : GameThread_SpawnPhase ( )
{
using namespace UE : : MovieScene ;
check ( GameThread = = ENamedThreads : : GameThread | | GameThread = = ENamedThreads : : GameThread_Local ) ;
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-08-11 01:36:57 -04:00
Linker - > EntityManager . IncrementSystemSerial ( ) ;
2020-09-24 00:43:27 -04:00
CurrentPhase = ESystemPhase : : Spawn ;
2020-08-11 01:36:57 -04:00
FInstanceRegistry * InstanceRegistry = GetInstanceRegistry ( ) ;
// Update all systems
if ( DissectedUpdates . Num ( ) ! = 0 )
{
const int32 PredicateOrder = DissectedUpdates [ 0 ] . Order ;
int32 Index = 0 ;
for ( ; Index < DissectedUpdates . Num ( ) & & DissectedUpdates [ Index ] . Order = = PredicateOrder ; + + Index )
{
FDissectedUpdate Update = DissectedUpdates [ Index ] ;
if ( ensure ( InstanceRegistry - > IsHandleValid ( Update . InstanceHandle ) ) )
{
FSequenceInstance & Instance = InstanceRegistry - > MutateInstance ( Update . InstanceHandle ) ;
Instance . Update ( Linker , Update . Context ) ;
}
}
DissectedUpdates . RemoveAt ( 0 , Index ) ;
}
2021-12-03 02:53:51 -05:00
const bool bInstantiationDirty = Linker - > HasStructureChangedSinceLastRun ( ) | | InstanceRegistry - > HasInvalidatedBindings ( ) ;
2020-08-11 01:36:57 -04:00
FGraphEventArray AllTasks ;
2020-09-01 14:07:48 -04:00
Linker - > AutoLinkRelevantSystems ( ) ;
2020-08-11 01:36:57 -04:00
// --------------------------------------------------------------------------------------------------------------------------------------------
// Step 1: Run the spawn phase if there were any changes to the current entity instantiations
if ( bInstantiationDirty )
{
2021-11-07 23:43:01 -05:00
SCOPE_CYCLE_COUNTER ( MovieSceneEval_SpawnPhase ) ;
2020-08-11 01:36:57 -04:00
// The spawn phase can queue events to trigger from the event tracks.
bCanQueueEventTriggers = true ;
{
Linker - > SystemGraph . ExecutePhase ( ESystemPhase : : Spawn , Linker , AllTasks ) ;
}
bCanQueueEventTriggers = false ;
// We don't open a re-entrancy window, however, because there's no way we can recursively evaluate things at this point... too many
// things are in an intermediate state. So events triggered as PreSpawn/PostSpawn can't be wired to something that starts a sequence.
if ( EventTriggers . IsBound ( ) )
{
EventTriggers . Broadcast ( ) ;
EventTriggers . Clear ( ) ;
}
}
// --------------------------------------------------------------------------------------------------------------------------------------------
// Step 2: Run the instantiation phase if there is anything to instantiate. This must come after the spawn phase because new intatniations may
// be created during the spawn phase
2021-08-03 11:56:47 -04:00
auto NextStep = [ this , bInstantiationDirty , Linker ]
2020-08-11 01:36:57 -04:00
{
const bool bAnyPending = Linker - > EntityManager . ContainsComponent ( FBuiltInComponentTypes : : Get ( ) - > Tags . NeedsLink ) | | Linker - > GetInstanceRegistry ( ) - > HasInvalidatedBindings ( ) ;
if ( bInstantiationDirty | | bAnyPending )
{
this - > GameThread_InstantiationPhase ( ) ;
}
else
{
// Go straight to evaluation
this - > GameThread_EvaluationPhase ( ) ;
}
} ;
if ( AllTasks . Num ( ) ! = 0 )
{
TGraphTask < TFunctionGraphTaskImpl < void ( ) , ESubsequentsMode : : TrackSubsequents > > : : CreateTask ( & AllTasks , ENamedThreads : : GameThread )
. ConstructAndDispatchWhenReady ( MoveTemp ( NextStep ) , TStatId ( ) , GameThread ) ;
}
else
{
NextStep ( ) ;
}
}
void FMovieSceneEntitySystemRunner : : GameThread_InstantiationPhase ( )
{
using namespace UE : : MovieScene ;
check ( GameThread = = ENamedThreads : : GameThread | | GameThread = = ENamedThreads : : GameThread_Local ) ;
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-09-24 00:43:27 -04:00
CurrentPhase = ESystemPhase : : Instantiation ;
2020-08-11 01:36:57 -04:00
FGraphEventArray AllTasks ;
2021-11-07 23:43:01 -05:00
{
SCOPE_CYCLE_COUNTER ( MovieSceneEval_InstantiationPhase ) ;
Linker - > SystemGraph . ExecutePhase ( ESystemPhase : : Instantiation , Linker , AllTasks ) ;
}
2020-08-11 01:36:57 -04:00
if ( AllTasks . Num ( ) ! = 0 )
{
TGraphTask < TFunctionGraphTaskImpl < void ( ) , ESubsequentsMode : : TrackSubsequents > > : : CreateTask ( & AllTasks , ENamedThreads : : GameThread )
2021-12-03 01:40:08 -05:00
. ConstructAndDispatchWhenReady (
[ this ]
{
this - > GameThread_PostInstantiation ( ) ;
}
, TStatId ( ) , GameThread ) ;
2020-08-11 01:36:57 -04:00
}
else
{
this - > GameThread_PostInstantiation ( ) ;
}
}
void FMovieSceneEntitySystemRunner : : GameThread_PostInstantiation ( )
{
using namespace UE : : MovieScene ;
2021-11-07 23:43:01 -05:00
{
SCOPE_CYCLE_COUNTER ( MovieSceneEval_PostInstantiation ) ;
2021-08-03 11:56:47 -04:00
2021-11-07 23:43:01 -05:00
check ( GameThread = = ENamedThreads : : GameThread | | GameThread = = ENamedThreads : : GameThread_Local ) ;
2020-08-11 01:36:57 -04:00
2021-11-07 23:43:01 -05:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-08-11 01:36:57 -04:00
2021-12-03 02:53:51 -05:00
Linker - > PostInstantation ( * this ) ;
2020-08-11 01:36:57 -04:00
2021-11-07 23:43:01 -05:00
FEntityManager & EntityManager = Linker - > EntityManager ;
FBuiltInComponentTypes * BuiltInComponentTypes = FBuiltInComponentTypes : : Get ( ) ;
2020-08-11 01:36:57 -04:00
2021-11-07 23:43:01 -05:00
// Nothing needs linking, caching or restoring any more
FRemoveMultipleMutation Mutation ;
Mutation . RemoveComponent ( BuiltInComponentTypes - > Tags . NeedsLink ) ;
2020-08-11 01:36:57 -04:00
2021-11-07 23:43:01 -05:00
FEntityComponentFilter Filter = FEntityComponentFilter ( ) . Any ( { BuiltInComponentTypes - > Tags . NeedsLink } ) ;
EntityManager . MutateAll ( Filter , Mutation ) ;
2020-08-11 01:36:57 -04:00
2021-11-07 23:43:01 -05:00
// Free anything that has been unlinked
EntityManager . FreeEntities ( FEntityComponentFilter ( ) . All ( { BuiltInComponentTypes - > Tags . NeedsUnlink } ) ) ;
2020-08-11 01:36:57 -04:00
2021-11-07 23:43:01 -05:00
Linker - > SystemGraph . RemoveIrrelevantSystems ( Linker ) ;
2020-08-11 01:36:57 -04:00
2021-11-07 23:43:01 -05:00
EntityManager . Compact ( ) ;
}
2020-08-11 01:36:57 -04:00
GameThread_EvaluationPhase ( ) ;
}
void FMovieSceneEntitySystemRunner : : GameThread_EvaluationPhase ( )
{
using namespace UE : : MovieScene ;
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-09-24 00:43:27 -04:00
CurrentPhase = ESystemPhase : : Evaluation ;
2020-08-11 01:36:57 -04:00
FGraphEventArray AllTasks ;
2021-11-07 23:43:01 -05:00
{
SCOPE_CYCLE_COUNTER ( MovieSceneEval_EvaluationPhase ) ;
// --------------------------------------------------------------------------------------------------------------------------------------------
// Step 2: Run the evaluation phase. The entity manager is locked down for this phase, meaning no changes to entity-component structure is allowed
// This vastly simplifies the concurrent handling of entity component allocations
Linker - > EntityManager . LockDown ( ) ;
checkf ( ! Linker - > EntityManager . ContainsComponent ( FBuiltInComponentTypes : : Get ( ) - > Tags . NeedsUnlink ) , TEXT ( " Stale entities remain in the entity manager during evaluation - these should have been destroyed during the instantiation phase. Did it run? " ) ) ;
Linker - > SystemGraph . ExecutePhase ( ESystemPhase : : Evaluation , Linker , AllTasks ) ;
}
2020-08-11 01:36:57 -04:00
2021-05-25 02:43:26 -04:00
auto Finish = [ this ]
{
// We are now done with the current update batch. Let's unlock the completion task to unblock
// the main thread, which is waiting on it inside Flush().
check ( this - > CompletionTask ! = nullptr ) ;
this - > CompletionTask - > Unlock ( ) ;
} ;
2020-08-11 01:36:57 -04:00
if ( AllTasks . Num ( ) ! = 0 )
{
TGraphTask < TFunctionGraphTaskImpl < void ( ) , ESubsequentsMode : : TrackSubsequents > > : : CreateTask ( & AllTasks , ENamedThreads : : GameThread )
2021-12-03 01:40:08 -05:00
. ConstructAndDispatchWhenReady ( MoveTemp ( Finish ) , TStatId ( ) , GameThread ) ;
2020-08-11 01:36:57 -04:00
}
else
{
2021-05-25 02:43:26 -04:00
Finish ( ) ;
2020-08-11 01:36:57 -04:00
}
}
void FMovieSceneEntitySystemRunner : : GameThread_EvaluationFinalizationPhase ( )
{
using namespace UE : : MovieScene ;
check ( GameThread = = ENamedThreads : : GameThread | | GameThread = = ENamedThreads : : GameThread_Local ) ;
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-08-11 01:36:57 -04:00
Linker - > EntityManager . ReleaseLockDown ( ) ;
2020-09-24 00:43:27 -04:00
CurrentPhase = ESystemPhase : : Finalization ;
2020-08-11 01:36:57 -04:00
// Post-eval events can be queued during the finalization phase so let's open that up.
// The events are actually executed a bit later, in GameThread_PostEvaluationPhase.
bCanQueueEventTriggers = true ;
{
2021-11-07 23:43:01 -05:00
SCOPE_CYCLE_COUNTER ( MovieSceneEval_FinalizationPhase ) ;
2021-08-03 11:56:47 -04:00
FInstanceRegistry * InstanceRegistry = GetInstanceRegistry ( ) ;
2021-09-13 13:55:16 -04:00
// Iterate on a copy of our current instances, since LegacyEvaluator->Evaluate() could change the instance handle, which would affect PostEvaluationPhase
TArray < FInstanceHandle > CurrentInstancesCopy ( CurrentInstances ) ;
for ( FInstanceHandle UpdatedInstanceHandle : CurrentInstancesCopy )
2021-08-03 11:56:47 -04:00
{
2021-09-13 13:55:16 -04:00
if ( InstanceRegistry - > IsHandleValid ( UpdatedInstanceHandle ) )
2021-08-03 11:56:47 -04:00
{
2021-09-13 13:55:16 -04:00
FSequenceInstance & Instance = InstanceRegistry - > MutateInstance ( UpdatedInstanceHandle ) ;
if ( Instance . IsRootSequence ( ) )
{
Instance . RunLegacyTrackTemplates ( ) ;
}
2021-08-03 11:56:47 -04:00
}
}
2021-05-25 02:43:26 -04:00
2020-08-11 01:36:57 -04:00
FGraphEventArray Tasks ;
Linker - > SystemGraph . ExecutePhase ( ESystemPhase : : Finalization , Linker , Tasks ) ;
checkf ( Tasks . Num ( ) = = 0 , TEXT ( " Cannot dispatch new tasks during finalization " ) ) ;
}
bCanQueueEventTriggers = false ;
2020-09-24 00:43:27 -04:00
CurrentPhase = ESystemPhase : : None ;
2020-08-11 01:36:57 -04:00
}
void FMovieSceneEntitySystemRunner : : GameThread_PostEvaluationPhase ( )
{
using namespace UE : : MovieScene ;
2021-11-07 23:43:01 -05:00
SCOPE_CYCLE_COUNTER ( MovieSceneEval_PostEvaluationPhase ) ;
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-08-11 01:36:57 -04:00
// Execute any queued events from the evaluation finalization phase.
if ( EventTriggers . IsBound ( ) )
{
// Let's allow re-entrant evaluation at this point.
FMovieSceneEntitySystemEvaluationReentrancyWindow Window ( * Linker ) ;
EventTriggers . Broadcast ( ) ;
EventTriggers . Clear ( ) ;
}
// Now run the post-evaluation logic so that we can safely handle broadcast events (like OnFinished)
// that trigger some new evaluations (such as connecting it to another sequence's Play in Blueprint).
//
// If we are the global linker (and not a "private" linker, as is the case with "blocking" sequences),
// we may find ourselves in a re-entrant call, which means we need to save our state here and restore
// it afterwards. We also:
// - iterate on a copy of our current instances, since a re-entrant call would modify that array.
// - store our pending update result in a separate variable before assigning it to ourselves, for
// the same reasons as above.
//
FInstanceRegistry * InstanceRegistry = GetInstanceRegistry ( ) ;
TArray < FInstanceHandle > CurrentInstancesCopy ( CurrentInstances ) ;
CurrentInstances . Empty ( ) ;
{
FMovieSceneEntitySystemEvaluationReentrancyWindow Window ( * Linker ) ;
for ( FInstanceHandle UpdatedInstanceHandle : CurrentInstancesCopy )
{
2021-01-21 16:22:06 -04:00
// We must check for validity here because the cache handles may have become invalid
// during this iteration (since there is a re-entrancy window open)
if ( InstanceRegistry - > IsHandleValid ( UpdatedInstanceHandle ) )
{
InstanceRegistry - > MutateInstance ( UpdatedInstanceHandle ) . PostEvaluation ( Linker ) ;
}
2020-08-11 01:36:57 -04:00
}
}
}
void FMovieSceneEntitySystemRunner : : FinishInstance ( FInstanceHandle InInstanceHandle )
{
using namespace UE : : MovieScene ;
2021-08-03 11:56:47 -04:00
UMovieSceneEntitySystemLinker * Linker = GetLinker ( ) ;
check ( Linker ) ;
2020-08-11 01:36:57 -04:00
// If we've already got queued updates for this instance we need to flush the linker first so that those updates are reflected correctly
FInstanceRegistry * InstanceRegistry = GetInstanceRegistry ( ) ;
if ( InstanceRegistry - > GetInstance ( InInstanceHandle ) . HasEverUpdated ( ) & & HasQueuedUpdates ( InInstanceHandle ) )
{
Flush ( ) ;
}
InstanceRegistry - > MutateInstance ( InInstanceHandle ) . Finish ( Linker ) ;
2021-12-03 02:53:51 -05:00
if ( Linker - > HasStructureChangedSinceLastRun ( ) )
2020-08-11 01:36:57 -04:00
{
MarkForUpdate ( InInstanceHandle ) ;
Flush ( ) ;
}
else
{
InstanceRegistry - > MutateInstance ( InInstanceHandle ) . PostEvaluation ( Linker ) ;
}
}
void FMovieSceneEntitySystemRunner : : MarkForUpdate ( FInstanceHandle InInstanceHandle )
{
CurrentInstances . AddUnique ( InInstanceHandle ) ;
}
void FMovieSceneEntitySystemRunner : : OnLinkerAbandon ( UMovieSceneEntitySystemLinker * InLinker )
{
2021-08-03 11:56:47 -04:00
if ( ensure ( InLinker ) )
{
InLinker - > Events . AbandonLinker . RemoveAll ( this ) ;
}
WeakLinker . Reset ( ) ;
2020-08-11 01:36:57 -04:00
}
FMovieSceneEntitySystemEventTriggers & FMovieSceneEntitySystemRunner : : GetQueuedEventTriggers ( )
{
checkf ( bCanQueueEventTriggers , TEXT ( " Can't queue event triggers at this point in the update loop. " ) ) ;
return EventTriggers ;
}