// Copyright Epic Games, Inc. All Rights Reserved. #include "EntitySystem/MovieSceneEntitySystem.h" #include "EntitySystem/MovieSceneEntitySystemTypes.h" #include "EntitySystem/MovieSceneEntitySystemLinker.h" #include "EntitySystem/MovieSceneComponentRegistry.h" namespace UE { namespace MovieScene { struct FSystemDependencyGraph { uint16 GetGraphID(UClass* Class) { const uint16* Existing = GraphIDsByClass.Find(Class->GetFName()); if (Existing) { return *Existing; } const int32 NewIndex = Nodes.Add(FNode::FromClass(Class)); check(NewIndex >= 0 && NewIndex < TNumericLimits::Max()); const uint16 NewGraphID = static_cast(NewIndex); ImplicitPrerequisites.AllocateNode(NewGraphID); ImplicitSubsequents.AllocateNode(NewGraphID); GraphIDsByClass.Add(Class->GetFName(), NewGraphID); return NewGraphID; } uint16 GetGraphID(FComponentTypeID InComponentType) { const uint16* Existing = GraphIDsByComponent.Find(InComponentType); if (Existing) { return *Existing; } const int32 NewIndex = Nodes.Add(FNode::FromComponent(InComponentType)); check(NewIndex >= 0 && NewIndex < TNumericLimits::Max()); const uint16 NewGraphID = static_cast(NewIndex); ImplicitPrerequisites.AllocateNode(NewGraphID); ImplicitSubsequents.AllocateNode(NewGraphID); GraphIDsByComponent.Add(InComponentType, NewGraphID); return NewGraphID; } void SetupDependencies(UMovieSceneEntitySystem* ThisSystem, UMovieSceneEntitySystemLinker* Linker) { using FDirectionalEdge = FMovieSceneEntitySystemDirectedGraph::FDirectionalEdge; const uint16 FromNode = ThisSystem->GetGlobalDependencyGraphID(); // Set up prerequisites for (FDirectionalEdge Edge : ImplicitPrerequisites.GetEdgesFrom(FromNode)) { FNode Node = Nodes[Edge.ToNode]; // Follow edges from components if (Node.WriteComponentType) { for (FDirectionalEdge ComponentEdge : ImplicitPrerequisites.GetEdgesFrom(Edge.ToNode)) { // Components shouldn't be connected to other components SetupPrereq(Nodes[ComponentEdge.ToNode].Class.Get(), ThisSystem, Linker); } } else { SetupPrereq(Node.Class.Get(), ThisSystem, Linker); } } // Set up subsequents for (FDirectionalEdge Edge : ImplicitSubsequents.GetEdgesFrom(FromNode)) { FNode Node = Nodes[Edge.ToNode]; // Follow edges from components if (Node.WriteComponentType) { for (FDirectionalEdge ComponentEdge : ImplicitSubsequents.GetEdgesFrom(Edge.ToNode)) { // Components shouldn't be connected to other components SetupSubsequent(ThisSystem, Nodes[ComponentEdge.ToNode].Class.Get(), Linker); } } else { SetupSubsequent(ThisSystem, Node.Class.Get(), Linker); } } } void SetupPrereq(UClass* PrereqType, UMovieSceneEntitySystem* ThisSystem, UMovieSceneEntitySystemLinker* Linker) { if (PrereqType) { if (UMovieSceneEntitySystem* TargetSystem = Linker->FindSystem(PrereqType)) { Linker->SystemGraph.AddPrerequisite(TargetSystem, ThisSystem); } } } void SetupSubsequent(UMovieSceneEntitySystem* ThisSystem, UClass* SubsequentType, UMovieSceneEntitySystemLinker* Linker) { if (SubsequentType) { if (UMovieSceneEntitySystem* TargetSystem = Linker->FindSystem(SubsequentType)) { Linker->SystemGraph.AddPrerequisite(ThisSystem, TargetSystem); } } } void MakeRelationship(uint16 UpstreamGraphID, uint16 DownstreamGraphID) { ImplicitSubsequents.MakeEdge(UpstreamGraphID, DownstreamGraphID); ImplicitPrerequisites.MakeEdge(DownstreamGraphID, UpstreamGraphID); } UClass* ClassFromGraphID(uint16 GraphID) const { check(Nodes.IsValidIndex(GraphID)); return Nodes[GraphID].Class.Get(); } uint16 NumGraphIDs() const { return Nodes.Num(); } private: TMap GraphIDsByClass; TMap GraphIDsByComponent; struct FNode { static FNode FromClass(UClass* InClass) { return FNode{ InClass, FComponentTypeID::Invalid() }; } static FNode FromComponent(FComponentTypeID InComponent) { return FNode{ nullptr, InComponent }; } TWeakObjectPtr Class; FComponentTypeID WriteComponentType; }; TArray Nodes; FMovieSceneEntitySystemDirectedGraph ImplicitPrerequisites; FMovieSceneEntitySystemDirectedGraph ImplicitSubsequents; }; FSystemDependencyGraph GlobalDependencyGraph; TMap SystemStats; } // namespace MovieScene } // namespace UE UMovieSceneEntitySystem::UMovieSceneEntitySystem(const FObjectInitializer& ObjInit) : Super(ObjInit) { using namespace UE::MovieScene; bSystemIsEnabled = true; SystemExclusionContext = EEntitySystemContext::None; Phase = ESystemPhase::Evaluation; GraphID = TNumericLimits::Max(); if (!GetClass()->HasAnyClassFlags(CLASS_Abstract)) { GlobalDependencyGraphID = GlobalDependencyGraph.GetGraphID(GetClass()); } else { GlobalDependencyGraphID = MAX_uint16; } #if STATS || ENABLE_STATNAMEDEVENTS const TStatId* ExistingStat = SystemStats.Find(GetClass()->GetFName()); if (ExistingStat) { StatID = *ExistingStat; } else { #if STATS TStatId NewStatID = FDynamicStats::CreateStatId(GetClass()->GetName()); #else // Just use the base UObject stat ID if we only have named events TStatId NewStatID = GetStatID(true /* bForDeferredUse */); #endif StatID = NewStatID; SystemStats.Add(GetClass()->GetFName(), NewStatID); } #endif } UMovieSceneEntitySystem::~UMovieSceneEntitySystem() { } void UMovieSceneEntitySystem::DefineImplicitPrerequisite(TSubclassOf UpstreamSystemType, TSubclassOf DownstreamSystemType) { using namespace UE::MovieScene; const uint16 UpstreamGlobalDependencyGraphID = GlobalDependencyGraph.GetGraphID(UpstreamSystemType.Get()); const uint16 DownstreamGlobalDependencyGraphID = GlobalDependencyGraph.GetGraphID(DownstreamSystemType.Get()); GlobalDependencyGraph.MakeRelationship(UpstreamGlobalDependencyGraphID, DownstreamGlobalDependencyGraphID); } void UMovieSceneEntitySystem::DefineComponentProducer(TSubclassOf ThisClassType, FComponentTypeID ComponentType) { using namespace UE::MovieScene; const uint16 UpstreamGlobalDependencyGraphID = GlobalDependencyGraph.GetGraphID(ThisClassType.Get()); const uint16 DownstreamGlobalDependencyGraphID = GlobalDependencyGraph.GetGraphID(ComponentType); GlobalDependencyGraph.MakeRelationship(UpstreamGlobalDependencyGraphID, DownstreamGlobalDependencyGraphID); } void UMovieSceneEntitySystem::DefineComponentConsumer(TSubclassOf ThisClassType, FComponentTypeID ComponentType) { using namespace UE::MovieScene; const uint16 UpstreamGlobalDependencyGraphID = GlobalDependencyGraph.GetGraphID(ComponentType); const uint16 DownstreamGlobalDependencyGraphID = GlobalDependencyGraph.GetGraphID(ThisClassType.Get()); GlobalDependencyGraph.MakeRelationship(UpstreamGlobalDependencyGraphID, DownstreamGlobalDependencyGraphID); } void UMovieSceneEntitySystem::LinkRelevantSystems(UMovieSceneEntitySystemLinker* InLinker) { using namespace UE::MovieScene; EEntitySystemContext LinkerContext = InLinker->GetSystemContext(); for (uint16 GraphID = 0; GraphID < GlobalDependencyGraph.NumGraphIDs(); ++GraphID) { if (InLinker->HasLinkedSystem(GraphID)) { continue; } UClass* Class = GlobalDependencyGraph.ClassFromGraphID(GraphID); UMovieSceneEntitySystem* SystemCDO = Class ? Cast(Class->GetDefaultObject()) : nullptr; if (SystemCDO && !EnumHasAnyFlags(SystemCDO->SystemExclusionContext, LinkerContext)) { SystemCDO->ConditionalLinkSystem(InLinker); } } } bool UMovieSceneEntitySystem::IsRelevant(UMovieSceneEntitySystemLinker* InLinker) const { if (RelevantComponent && InLinker->EntityManager.ContainsComponent(RelevantComponent)) { return true; } return IsRelevantImpl(InLinker); } bool UMovieSceneEntitySystem::IsRelevantImpl(UMovieSceneEntitySystemLinker* InLinker) const { return false; } void UMovieSceneEntitySystem::ConditionalLinkSystem(UMovieSceneEntitySystemLinker* InLinker) const { check(HasAnyFlags(RF_ClassDefaultObject)); ConditionalLinkSystemImpl(InLinker); } void UMovieSceneEntitySystem::ConditionalLinkSystemImpl(UMovieSceneEntitySystemLinker* InLinker) const { if (IsRelevant(InLinker)) { InLinker->LinkSystem(GetClass()); } } void UMovieSceneEntitySystem::Enable() { bSystemIsEnabled = true; } void UMovieSceneEntitySystem::Disable() { bSystemIsEnabled = false; } void UMovieSceneEntitySystem::TagGarbage() { OnTagGarbage(); } void UMovieSceneEntitySystem::CleanTaggedGarbage() { OnCleanTaggedGarbage(); } bool UMovieSceneEntitySystem::IsReadyForFinishDestroy() { return Linker == nullptr; } void UMovieSceneEntitySystem::Abandon() { Linker = nullptr; GraphID = TNumericLimits::Max(); } void UMovieSceneEntitySystem::FinishDestroy() { checkf(!Linker, TEXT("System being destroyed without Unlink being called")); Super::FinishDestroy(); } void UMovieSceneEntitySystem::Run(FSystemTaskPrerequisites& InPrerequisites, FSystemSubsequentTasks& Subsequents) { if (!bSystemIsEnabled) { return; } #if STATS || ENABLE_STATNAMEDEVENTS FScopeCycleCounter Scope(StatID); #endif checkf(Linker != nullptr, TEXT("Attempting to evaluate a system that has been unlinked!")); // We may have erroneously linked a system we should have done, but we must not run it in this case if (EnumHasAnyFlags(SystemExclusionContext, Linker->GetSystemContext())) { return; } Linker->EntityManager.IncrementSystemSerial(); UE_LOG(LogMovieSceneECS, Verbose, TEXT("Running moviescene system for phase %d: %s"), (int32)Phase, *GetName()); OnRun(InPrerequisites, Subsequents); } void UMovieSceneEntitySystem::Link(UMovieSceneEntitySystemLinker* InLinker) { using namespace UE::MovieScene; check(GraphID != TNumericLimits::Max()); Linker = InLinker; OnLink(); GlobalDependencyGraph.SetupDependencies(this, InLinker); Linker->SystemLinked(this); } void UMovieSceneEntitySystem::Unlink() { if (GraphID != TNumericLimits::Max()) { Linker->SystemGraph.RemoveSystem(this); } OnUnlink(); Linker->SystemUnlinked(this); Linker = nullptr; }