Files
UnrealEngineUWP/Engine/Source/Runtime/MovieScene/Private/EntitySystem/MovieSceneEntitySystemLinker.cpp
max chen a20dc348ee Sequencer: Pre-Animated state changes to support track instance inputs
- Track Instance Inputs can now be used as capture sources to capture pre-animated state
  - PreAnimatedState extension now simply exists as a member of UMovieSceneEntitySystem. Previously the existence of the extension was used to determine whether any pre-animated state logic was required, but in practice this created more problems than it solved and led to some convoluted lifetime management code.
  - Added a templated group manager type from which the object group manager can inherit. This simplifies the introduction of new group managers
  - Removed the requirement that all pre-animated state must exist as part of a valid group. This forced state from 'master' tracks to be arbitrarily grouped together which put an unnecessary burden on the storage classes.

#jira UE-132512
#rb max.chen, matt.hoffman
[FYI] andrew.rodham
#preflight 61f2ca893765218cf01f290e

#ROBOMERGE-AUTHOR: max.chen
#ROBOMERGE-SOURCE: CL 18754810 in //UE5/Release-5.0/... via CL 18754825 via CL 18757520
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v903-18687472)

[CL 18758489 by max chen in ue5-main branch]
2022-01-27 14:35:51 -05:00

447 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EntitySystem/MovieSceneEntitySystemLinker.h"
#include "EntitySystem/MovieSceneEntitySystemTypes.h"
#include "EntitySystem/MovieSceneEntitySystem.h"
#include "EntitySystem/MovieSceneEntitySystemTask.h"
#include "EntitySystem/MovieSceneEntityMutations.h"
#include "EntitySystem/MovieSceneComponentRegistry.h"
#include "EntitySystem/BuiltInComponentTypes.h"
#include "EntitySystem/MovieScenePreAnimatedStateSystem.h"
#include "MovieSceneFwd.h"
#include "UObject/Package.h"
#include "UObject/UObjectGlobals.h"
#include "Templates/SubclassOf.h"
#include "Containers/Ticker.h"
#include "UObject/ObjectKey.h"
#include "Engine/World.h"
#include "Algo/Find.h"
#include "HAL/Event.h"
#include "HAL/PlatformProcess.h"
#include "ProfilingDebugging/CountersTrace.h"
DECLARE_CYCLE_STAT(TEXT("Link Relevant Systems"), MovieSceneEval_LinkRelevantSystems, STATGROUP_MovieSceneECS);
namespace UE
{
namespace MovieScene
{
struct FCustomEventDeleter
{
void operator()(FEvent* Event)
{
FPlatformProcess::ReturnSynchEventToPool(Event);
}
};
static FComponentRegistry GComponentRegistry;
} // namespace MovieScene
} // namespace UE
UMovieSceneEntitySystemLinker::UMovieSceneEntitySystemLinker(const FObjectInitializer& ObjInit)
: Super(ObjInit)
{
using namespace UE::MovieScene;
LastSystemLinkVersion = 0;
LastInstantiationVersion = 0;
AutoLinkMode = EAutoLinkRelevantSystems::Enabled;
SystemContext = EEntitySystemContext::Runtime;
if (!HasAnyFlags(RF_ClassDefaultObject))
{
PreAnimatedState.Initialize(this);
FCoreUObjectDelegates::GetPostGarbageCollect().AddUObject(this, &UMovieSceneEntitySystemLinker::HandlePostGarbageCollection);
EntityManager.SetDebugName(GetName() + TEXT("[Entity Manager]"));
EntityManager.SetComponentRegistry(&GComponentRegistry);
FWorldDelegates::OnWorldCleanup.AddUObject(this, &UMovieSceneEntitySystemLinker::OnWorldCleanup);
InstanceRegistry.Reset(new FInstanceRegistry(this));
#if WITH_EDITOR
FCoreUObjectDelegates::OnObjectsReplaced.AddUObject(this, &UMovieSceneEntitySystemLinker::OnObjectsReplaced);
#endif
}
}
UE::MovieScene::FEntitySystemLinkerExtensionID UMovieSceneEntitySystemLinker::RegisterExtension()
{
static int32 StaticID = 0;
return UE::MovieScene::FEntitySystemLinkerExtensionID{ StaticID++ };
}
void UMovieSceneEntitySystemLinker::Reset()
{
Events.AbandonLinker.Broadcast(this);
Events.TagGarbage.Clear();
Events.CleanTaggedGarbage.Clear();
Events.AddReferencedObjects.Clear();
Events.AbandonLinker.Clear();
Events.CleanUpWorld.Clear();
SystemGraph.Shutdown();
EntitySystemsByGlobalGraphID.Reset();
EntityManager.Destroy();
}
UMovieSceneEntitySystemLinker* UMovieSceneEntitySystemLinker::FindOrCreateLinker(UObject* PreferredOuter, const TCHAR* Name)
{
if (!PreferredOuter)
{
PreferredOuter = GetTransientPackage();
}
UMovieSceneEntitySystemLinker* Existing = FindObject<UMovieSceneEntitySystemLinker>(PreferredOuter, Name);
if (!Existing)
{
Existing = NewObject<UMovieSceneEntitySystemLinker>(PreferredOuter, Name);
}
return Existing;
}
UMovieSceneEntitySystemLinker* UMovieSceneEntitySystemLinker::CreateLinker(UObject* PreferredOuter)
{
if (!PreferredOuter)
{
PreferredOuter = GetTransientPackage();
}
return NewObject<UMovieSceneEntitySystemLinker>(PreferredOuter);
}
UE::MovieScene::FComponentRegistry* UMovieSceneEntitySystemLinker::GetComponents()
{
return &UE::MovieScene::GComponentRegistry;
}
void UMovieSceneEntitySystemLinker::InvalidateObjectBinding(const FGuid& ObjectBindingID, FInstanceHandle InInstanceHandle)
{
if (InstanceRegistry->IsHandleValid(InInstanceHandle))
{
InstanceRegistry->InvalidateObjectBinding(ObjectBindingID, InInstanceHandle);
}
}
void UMovieSceneEntitySystemLinker::SystemLinked(UMovieSceneEntitySystem* InSystem)
{
const uint16 GlobalID = InSystem->GetGlobalDependencyGraphID();
EntitySystemsByGlobalGraphID.Insert(GlobalID, InSystem);
}
void UMovieSceneEntitySystemLinker::SystemUnlinked(UMovieSceneEntitySystem* InSystem)
{
const uint16 GlobalID = InSystem->GetGlobalDependencyGraphID();
check(EntitySystemsByGlobalGraphID[GlobalID] == InSystem);
EntitySystemsByGlobalGraphID.RemoveAt(GlobalID);
Events.TagGarbage.RemoveAll(InSystem);
Events.CleanTaggedGarbage.RemoveAll(InSystem);
Events.AddReferencedObjects.RemoveAll(InSystem);
Events.AbandonLinker.RemoveAll(InSystem);
Events.CleanUpWorld.RemoveAll(InSystem);
// Add the system to the recycling pool.
ensure(EntitySystemsRecyclingPool.Contains(InSystem->GetClass()) == false);
EntitySystemsRecyclingPool.Add(InSystem->GetClass(), InSystem);
}
bool UMovieSceneEntitySystemLinker::HasLinkedSystem(const uint16 GlobalDependencyGraphID)
{
return EntitySystemsByGlobalGraphID.IsValidIndex(GlobalDependencyGraphID);
}
void UMovieSceneEntitySystemLinker::BeginDestroy()
{
Events.AbandonLinker.Broadcast(this);
SystemGraph.Shutdown();
FCoreUObjectDelegates::GetPostGarbageCollect().RemoveAll(this);
Super::BeginDestroy();
}
void UMovieSceneEntitySystemLinker::CleanupInvalidBoundObjects()
{
TagInvalidBoundObjects();
Events.TagGarbage.Broadcast(this);
CleanGarbage();
}
void UMovieSceneEntitySystemLinker::TagInvalidBoundObjects()
{
using namespace UE::MovieScene;
FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get();
// Tag any bound objects that are now invalid
TArray<FMovieSceneEntityID> ExpiredBoundObjects;
auto Iter = [&ExpiredBoundObjects](FMovieSceneEntityID EntityID, UObject* BoundObject)
{
if (FBuiltInComponentTypes::IsBoundObjectGarbage(BoundObject))
{
ExpiredBoundObjects.Add(EntityID);
}
};
FEntityTaskBuilder()
.ReadEntityIDs()
.Read(BuiltInComponents->BoundObject)
.Iterate_PerEntity(&EntityManager, Iter);
for (FMovieSceneEntityID Entity : ExpiredBoundObjects)
{
EntityManager.AddComponent(Entity, BuiltInComponents->Tags.NeedsUnlink, EEntityRecursion::Full);
}
}
bool UMovieSceneEntitySystemLinker::HasStructureChangedSinceLastRun() const
{
return EntityManager.HasStructureChangedSince(LastInstantiationVersion);
}
bool UMovieSceneEntitySystemLinker::StartEvaluation(FMovieSceneEntitySystemRunner& InRunner)
{
if (ActiveRunners.Num() == 0 || ActiveRunners.Last().bIsReentrancyAllowed)
{
// Default to re-entrancy being forbidden. The runner will allow re-entrancy at specific spots
// in the evaluation loop, via a "re-entrancy window".
ActiveRunners.Emplace(FActiveRunnerInfo{ &InRunner, false });
return true;
}
UE_LOG(LogMovieSceneECS, Warning, TEXT("Can't start a new evaluation: the active runner is not in a re-entrancy window."));
return false;
}
FMovieSceneEntitySystemRunner* UMovieSceneEntitySystemLinker::GetActiveRunner() const
{
if (ActiveRunners.Num() > 0)
{
return ActiveRunners.Last().Runner;
}
return nullptr;
}
void UMovieSceneEntitySystemLinker::PostInstantation(FMovieSceneEntitySystemRunner& InRunner)
{
LastInstantiationVersion = EntityManager.GetSystemSerial();
GetInstanceRegistry()->PostInstantation();
}
void UMovieSceneEntitySystemLinker::EndEvaluation(FMovieSceneEntitySystemRunner& InRunner)
{
if (ensureMsgf((ActiveRunners.Num() > 0 && ActiveRunners.Last().Runner == &InRunner),
TEXT("Trying end the evaluation of a runner that's not the latest one to run.")))
{
ActiveRunners.Pop();
}
}
void UMovieSceneEntitySystemLinker::HandlePostGarbageCollection()
{
using namespace UE::MovieScene;
// All the instance registry to unlink garbage first
InstanceRegistry->TagGarbage();
// Clean any garbage bound objects
TagInvalidBoundObjects();
// Allow any other system to tag garbage
Events.TagGarbage.Broadcast(this);
auto RouteTagGarbage = [](UMovieSceneEntitySystem* System){ System->TagGarbage(); };
SystemGraph.IteratePhase(ESystemPhase::Spawn, RouteTagGarbage);
SystemGraph.IteratePhase(ESystemPhase::Instantiation, RouteTagGarbage);
CleanGarbage();
}
void UMovieSceneEntitySystemLinker::CleanGarbage()
{
using namespace UE::MovieScene;
FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get();
FComponentTypeID NeedsUnlink = BuiltInComponents->Tags.NeedsUnlink;
if (!EntityManager.ContainsComponent(NeedsUnlink))
{
return;
}
// Clear the instantiation serial to indicate that we probably need to re-run the instantiation systems
// the next time a runner gets flushed
LastInstantiationVersion = 0;
// Allow any other system to tag garbage
Events.CleanTaggedGarbage.Broadcast(this);
auto RouteCleanTaggedGarbage = [](UMovieSceneEntitySystem* System){ System->CleanTaggedGarbage(); };
SystemGraph.IteratePhase(ESystemPhase::Spawn, RouteCleanTaggedGarbage);
SystemGraph.IteratePhase(ESystemPhase::Instantiation, RouteCleanTaggedGarbage);
TArray<FMovieSceneEntityID> UnresolvedEntities;
FEntityTaskBuilder()
.Read(BuiltInComponents->BoundObject)
.Read(BuiltInComponents->ParentEntity)
.FilterNone({ BuiltInComponents->Tags.NeedsUnlink, BuiltInComponents->Tags.Ignored, BuiltInComponents->Tags.Finished })
.Iterate_PerEntity(&EntityManager, [&UnresolvedEntities](UObject* Object, FMovieSceneEntityID ParentEntityID)
{
if (!Object)
{
UnresolvedEntities.Add(ParentEntityID);
}
});
for (FMovieSceneEntityID EntityID : UnresolvedEntities)
{
EntityManager.AddComponent(EntityID, BuiltInComponents->Tags.HasUnresolvedBinding);
}
// Free the entities
TSet<FMovieSceneEntityID> FreedEntities;
EntityManager.FreeEntities(FEntityComponentFilter().All({ NeedsUnlink }), &FreedEntities);
InstanceRegistry->CleanupLinkerEntities(FreedEntities);
}
void UMovieSceneEntitySystemLinker::OnObjectsReplaced(const TMap<UObject*, UObject*>& ReplacementMap)
{
#if WITH_EDITOR
using namespace UE::MovieScene;
FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get();
FEntityTaskBuilder()
.Write(BuiltInComponents->BoundObject)
.Iterate_PerEntity(&EntityManager, [&ReplacementMap](UObject*& Object)
{
if (UObject* Replacement = ReplacementMap.FindRef(Object))
{
Object = Replacement;
}
});
#endif
}
void UMovieSceneEntitySystemLinker::OnWorldCleanup(UWorld* InWorld, bool bSessionEnded, bool bCleanupResources)
{
Events.CleanUpWorld.Broadcast(this, InWorld);
InstanceRegistry->WorldCleanup(InWorld);
HandlePostGarbageCollection();
}
void UMovieSceneEntitySystemLinker::AddReferencedObjects(UObject* Object, FReferenceCollector& Collector)
{
Super::AddReferencedObjects(Object, Collector);
UMovieSceneEntitySystemLinker* This = CastChecked<UMovieSceneEntitySystemLinker>(Object);
This->EntityManager.AddReferencedObjects(Collector);
This->Events.AddReferencedObjects.Broadcast(This, Collector);
Collector.AddReferencedObjects(This->EntitySystemsRecyclingPool);
}
UMovieSceneEntitySystem* UMovieSceneEntitySystemLinker::LinkSystem(TSubclassOf<UMovieSceneEntitySystem> InClassType)
{
UMovieSceneEntitySystem* Existing = FindSystem(InClassType);
if (Existing)
{
return Existing;
}
// We always create systems with a fixed name (since there should only ever be one of that name)
// This means we can do our own recycling within the scope of this linker, to save on the cost of re-creating
// systems when the first instantiation phase kicks in after a period without any sequence playing.
UMovieSceneEntitySystem* NewSystem = nullptr;
UMovieSceneEntitySystem** Recycled = EntitySystemsRecyclingPool.Find(InClassType);
if (Recycled)
{
// Revive a recycled system.
NewSystem = *Recycled;
check(NewSystem);
EntitySystemsRecyclingPool.Remove(InClassType);
UE_LOG(LogMovieSceneECS, Verbose, TEXT("Recycling system: "), *InClassType->GetName());
}
else
{
// Unique names also mean we will recycle systems if they previously existed but are no longer used to avoid thrashing the GC
// Recycling will destruct + memzero + construct the object so we can be sure that previous state doesn't roll over
UClass* SystemClass = InClassType.Get();
FName SystemName = SystemClass->GetFName();
NewSystem = NewObject<UMovieSceneEntitySystem>(this, SystemClass, SystemName);
}
// If a system implements a hard depdency on another (through direct use of LinkSystem<>), we can't break the client code by returning null, but we can still warn that it should have checked whether it can call LinkSystem first
ensureMsgf(!EnumHasAnyFlags(NewSystem->GetExclusionContext(), SystemContext), TEXT("Attempting to link a system that should have been excluded - this is probably an explicit call to Link a system that should have been excluded."));
SystemGraph.AddSystem(NewSystem);
NewSystem->Link(this);
return NewSystem;
}
UMovieSceneEntitySystem* UMovieSceneEntitySystemLinker::FindSystem(TSubclassOf<UMovieSceneEntitySystem> InClassType) const
{
UClass* Class = InClassType.Get();
UMovieSceneEntitySystem* SystemCDO = Class ? Cast<UMovieSceneEntitySystem>(Class->GetDefaultObject()) : nullptr;
if (SystemCDO)
{
const uint16 GlobalID = SystemCDO->GetGlobalDependencyGraphID();
if (EntitySystemsByGlobalGraphID.IsValidIndex(GlobalID))
{
return EntitySystemsByGlobalGraphID[GlobalID];
}
}
return nullptr;
}
void UMovieSceneEntitySystemLinker::LinkRelevantSystems()
{
MOVIESCENE_DETAILED_SCOPE_CYCLE_COUNTER(MovieSceneEval_LinkRelevantSystems);
// If the structure has not changed there's no way that there are any other relevant systems still
if (EntityManager.HasStructureChangedSince(LastSystemLinkVersion))
{
UMovieSceneEntitySystem::LinkRelevantSystems(this);
LastSystemLinkVersion = EntityManager.GetSystemSerial();
}
}
void UMovieSceneEntitySystemLinker::AutoLinkRelevantSystems()
{
if (AutoLinkMode == UE::MovieScene::EAutoLinkRelevantSystems::Enabled)
{
LinkRelevantSystems();
}
}
FMovieSceneEntitySystemEvaluationReentrancyWindow::FMovieSceneEntitySystemEvaluationReentrancyWindow(UMovieSceneEntitySystemLinker& InLinker)
: Linker(InLinker)
{
CurrentLevel = Linker.ActiveRunners.Num() - 1;
Linker.ActiveRunners[CurrentLevel].bIsReentrancyAllowed = true;
}
FMovieSceneEntitySystemEvaluationReentrancyWindow::~FMovieSceneEntitySystemEvaluationReentrancyWindow()
{
if (ensure(Linker.ActiveRunners.IsValidIndex(CurrentLevel)))
{
Linker.ActiveRunners[CurrentLevel].bIsReentrancyAllowed = false;
}
}