Files
UnrealEngineUWP/Engine/Source/Runtime/MovieScene/Private/EntitySystem/MovieSceneSequenceInstance.cpp
andrew rodham cf754edf08 Sequencer: Fix stale track templates not being purged on evaluation
Previously stale tracks were always purged inside FSequencer::EvaluateInternal, but this code was lost during the ECS migration and the segragation of the legacy evaluator from the main evaluation templates. Rather than put the code back in there, I'm opting to put it in the sequence instance's post-evaluate function, meaning it will apply to volatile sequences inside cooked builds too, rather than just the editor UI.

Ideally this code would exist as part of the FMovieSceneTrackEvaluator, but in order for it to only be applied for volatile sequences (and thus not affect runtime performance) we'd have to call through from the FSequenceInstance which would involve changing headers. I'd like to get this change into a patch release if possible, so I've opted for the hotfixable change - we can tidy it up in main.

#jira MH-5629
#rb max.chen
#preflight 625ee5addb15ac92db9d06c0

#ROBOMERGE-AUTHOR: andrew.rodham
#ROBOMERGE-SOURCE: CL 19811627 in //UE5/Release-5.0/... via CL 19818090
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v939-19570697)

[CL 19821323 by andrew rodham in ue5-main branch]
2022-04-19 19:12:05 -04:00

367 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EntitySystem/MovieSceneSequenceInstance.h"
#include "EntitySystem/MovieSceneEntitySystemLinker.h"
#include "EntitySystem/MovieSceneEntitySystem.h"
#include "EntitySystem/MovieSceneSequenceUpdaters.h"
#include "Evaluation/PreAnimatedState/MovieScenePreAnimatedStateExtension.h"
#include "Compilation/MovieSceneCompiledVolatilityManager.h"
#include "Compilation/MovieSceneCompiledDataManager.h"
#include "Evaluation/MovieSceneEvaluationTemplateInstance.h"
#include "Evaluation/Instances/MovieSceneTrackEvaluator.h"
#include "Evaluation/MovieSceneRootOverridePath.h"
#include "IMovieScenePlayer.h"
#include "MovieSceneSequencePlayer.h"
#include "MovieSceneTimeHelpers.h"
#include "Algo/IndexOf.h"
namespace UE
{
namespace MovieScene
{
DECLARE_CYCLE_STAT(TEXT("Sequence Instance Update"), MovieSceneEval_SequenceInstanceUpdate, STATGROUP_MovieSceneEval);
DECLARE_CYCLE_STAT(TEXT("[External] Sequence Instance Post-Update"), MovieSceneEval_SequenceInstancePostUpdate, STATGROUP_MovieSceneEval);
FSequenceInstance::FSequenceInstance(UMovieSceneEntitySystemLinker* Linker, IMovieScenePlayer* Player, FRootInstanceHandle InInstanceHandle)
: SequenceID(MovieSceneSequenceID::Root)
, RootOverrideSequenceID(MovieSceneSequenceID::Root)
, PlayerIndex(Player->GetUniqueIndex())
, InstanceHandle(InInstanceHandle)
, RootInstanceHandle(InInstanceHandle)
{
// Root instances always start in a finished state in order to ensure that 'Start'
// is called correctly for the top level instance. This is subtly different from
// bHasEverUpdated since a sequence instance can be Finished and restarted multiple times
bFinished = true;
bHasEverUpdated = false;
CompiledDataID = Player->GetEvaluationTemplate().GetCompiledDataID();
FMovieSceneObjectCache& ObjectCache = Player->State.GetObjectCache(SequenceID);
OnInvalidateObjectBindingHandle = ObjectCache.OnBindingInvalidated.AddUObject(Linker, &UMovieSceneEntitySystemLinker::InvalidateObjectBinding, InstanceHandle);
InvalidateCachedData(Linker);
}
FSequenceInstance::FSequenceInstance(UMovieSceneEntitySystemLinker* Linker, IMovieScenePlayer* Player, FInstanceHandle InInstanceHandle, FRootInstanceHandle InRootInstanceHandle, FMovieSceneSequenceID InSequenceID, FMovieSceneCompiledDataID InCompiledDataID)
: CompiledDataID(InCompiledDataID)
, SequenceID(InSequenceID)
, RootOverrideSequenceID(MovieSceneSequenceID::Invalid)
, PlayerIndex(Player->GetUniqueIndex())
, InstanceHandle(InInstanceHandle)
, RootInstanceHandle(InRootInstanceHandle)
{
// Sub Sequence instances always start in a non-finished state because they will only ever
// be created if they are active, and the Start/Update/Finish loop does not apply to sub-instances
bFinished = false;
bHasEverUpdated = false;
FMovieSceneObjectCache& ObjectCache = Player->State.GetObjectCache(SequenceID);
OnInvalidateObjectBindingHandle = ObjectCache.OnBindingInvalidated.AddUObject(Linker, &UMovieSceneEntitySystemLinker::InvalidateObjectBinding, InstanceHandle);
InvalidateCachedData(Linker);
}
FSequenceInstance::~FSequenceInstance()
{}
FSequenceInstance::FSequenceInstance(FSequenceInstance&&) = default;
FSequenceInstance& FSequenceInstance::operator=(FSequenceInstance&&) = default;
IMovieScenePlayer* FSequenceInstance::GetPlayer() const
{
return IMovieScenePlayer::Get(PlayerIndex);
}
void FSequenceInstance::InitializeLegacyEvaluator(UMovieSceneEntitySystemLinker* Linker)
{
IMovieScenePlayer* Player = GetPlayer();
check(Player);
UMovieSceneCompiledDataManager* CompiledDataManager = Player->GetEvaluationTemplate().GetCompiledDataManager();
const FMovieSceneCompiledDataEntry& CompiledEntry = CompiledDataManager->GetEntryRef(CompiledDataID);
if (EnumHasAnyFlags(CompiledEntry.AccumulatedMask, EMovieSceneSequenceCompilerMask::EvaluationTemplate))
{
if (!LegacyEvaluator)
{
LegacyEvaluator = MakeUnique<FMovieSceneTrackEvaluator>(CompiledEntry.GetSequence(), CompiledDataID, CompiledDataManager);
}
}
else if (LegacyEvaluator)
{
LegacyEvaluator->Finish(*Player);
LegacyEvaluator = nullptr;
}
}
void FSequenceInstance::InvalidateCachedData(UMovieSceneEntitySystemLinker* Linker)
{
Ledger.Invalidate();
IMovieScenePlayer* Player = GetPlayer();
check(Player);
UMovieSceneCompiledDataManager* CompiledDataManager = Player->GetEvaluationTemplate().GetCompiledDataManager();
UMovieSceneSequence* Sequence = CompiledDataManager->GetEntryRef(CompiledDataID).GetSequence();
Player->State.AssignSequence(SequenceID, *Sequence, *Player);
if (SequenceID == MovieSceneSequenceID::Root)
{
// Try and recreate the volatility manager if this sequence is now volatile
if (!VolatilityManager)
{
VolatilityManager = FCompiledDataVolatilityManager::Construct(*Player, CompiledDataID, CompiledDataManager);
if (VolatilityManager)
{
VolatilityManager->ConditionalRecompile(*Player, CompiledDataID, CompiledDataManager);
}
}
ISequenceUpdater::FactoryInstance(SequenceUpdater, CompiledDataManager, CompiledDataID);
SequenceUpdater->InvalidateCachedData(Linker);
if (LegacyEvaluator)
{
LegacyEvaluator->InvalidateCachedData();
}
InitializeLegacyEvaluator(Linker);
}
}
void FSequenceInstance::DissectContext(UMovieSceneEntitySystemLinker* Linker, const FMovieSceneContext& InContext, TArray<TRange<FFrameTime>>& OutDissections)
{
check(SequenceID == MovieSceneSequenceID::Root);
IMovieScenePlayer* Player = GetPlayer();
if (VolatilityManager)
{
UMovieSceneCompiledDataManager* CompiledDataManager = Player->GetEvaluationTemplate().GetCompiledDataManager();
if (VolatilityManager->ConditionalRecompile(*Player, CompiledDataID, CompiledDataManager))
{
InvalidateCachedData(Linker);
}
}
SequenceUpdater->DissectContext(Linker, Player, InContext, OutDissections);
}
void FSequenceInstance::Start(UMovieSceneEntitySystemLinker* Linker, const FMovieSceneContext& InContext)
{
check(SequenceID == MovieSceneSequenceID::Root);
bFinished = false;
bHasEverUpdated = true;
check(RootInstanceHandle == InstanceHandle);
IMovieScenePlayer* Player = GetPlayer();
SequenceUpdater->Start(Linker, RootInstanceHandle, Player, InContext);
}
void FSequenceInstance::Update(UMovieSceneEntitySystemLinker* Linker, const FMovieSceneContext& InContext)
{
SCOPE_CYCLE_COUNTER(MovieSceneEval_SequenceInstanceUpdate);
#if STATS || ENABLE_STATNAMEDEVENTS
const bool bShouldTrackObject = Stats::IsThreadCollectingData();
FScopeCycleCounterUObject ContextScope(bShouldTrackObject ? GetPlayer()->AsUObject() : nullptr);
#endif
bHasEverUpdated = true;
if (bFinished)
{
Start(Linker, InContext);
}
check(RootInstanceHandle == InstanceHandle);
Context = InContext;
SequenceUpdater->Update(Linker, RootInstanceHandle, GetPlayer(), InContext);
}
void FSequenceInstance::Finish(UMovieSceneEntitySystemLinker* Linker)
{
if (IsRootSequence() && !bHasEverUpdated)
{
return;
}
Linker->EntityManager.IncrementSystemSerial();
bFinished = true;
Ledger.UnlinkEverything(Linker);
Ledger = FEntityLedger();
IMovieScenePlayer* Player = IMovieScenePlayer::Get(PlayerIndex);
if (!ensure(Player))
{
return;
}
if (SequenceUpdater)
{
check(RootInstanceHandle == InstanceHandle);
SequenceUpdater->Finish(Linker, RootInstanceHandle, Player);
}
if (LegacyEvaluator)
{
LegacyEvaluator->Finish(*Player);
}
if (IsRootSequence())
{
FMovieSceneSpawnRegister& SpawnRegister = Player->GetSpawnRegister();
SpawnRegister.ForgetExternallyOwnedSpawnedObjects(Player->State, *Player);
SpawnRegister.CleanUp(*Player);
if (Player->PreAnimatedState.IsCapturingGlobalPreAnimatedState())
{
Linker->PreAnimatedState.RestoreGlobalState(FRestoreStateParams{ Linker, RootInstanceHandle });
}
}
}
void FSequenceInstance::PreEvaluation(UMovieSceneEntitySystemLinker* Linker)
{
if (IsRootSequence())
{
IMovieScenePlayer* Player = GetPlayer();
if (ensure(Player))
{
Player->PreEvaluation(Context);
}
}
}
void FSequenceInstance::RunLegacyTrackTemplates()
{
if (LegacyEvaluator)
{
IMovieScenePlayer* Player = IMovieScenePlayer::Get(PlayerIndex);
if (ensure(Player))
{
if (bFinished)
{
LegacyEvaluator->Finish(*Player);
}
else
{
LegacyEvaluator->Evaluate(Context, *Player, RootOverrideSequenceID);
}
}
}
}
void FSequenceInstance::PostEvaluation(UMovieSceneEntitySystemLinker* Linker)
{
Ledger.UnlinkOneShots(Linker);
if (IsRootSequence())
{
IMovieScenePlayer* Player = GetPlayer();
if (ensure(Player))
{
SCOPE_CYCLE_COUNTER(MovieSceneEval_SequenceInstancePostUpdate);
Player->PostEvaluation(Context);
if (VolatilityManager && LegacyEvaluator)
{
FMovieSceneRootEvaluationTemplateInstance& RootTemplateInstance = Player->GetEvaluationTemplate();
UMovieSceneCompiledDataManager* CompiledDataManager = RootTemplateInstance.GetCompiledDataManager();
// If this sequence is volatile, purge any stale track templates from the compiled data
FMovieSceneEvaluationTemplate* EvalTemplate = const_cast<FMovieSceneEvaluationTemplate*>(CompiledDataManager->FindTrackTemplate(CompiledDataID));
if (EvalTemplate)
{
EvalTemplate->PurgeStaleTracks();
}
// Do the same for all subsequences
const FMovieSceneSequenceHierarchy* Hierarchy = CompiledDataManager->FindHierarchy(CompiledDataID);
if (Hierarchy)
{
for (const TTuple<FMovieSceneSequenceID, FMovieSceneSubSequenceData>& Pair : Hierarchy->AllSubSequenceData())
{
UMovieSceneSequence* SubSequence = Pair.Value.GetLoadedSequence();
if (!SubSequence)
{
continue;
}
FMovieSceneCompiledDataID SubCompiledDataID = CompiledDataManager->FindDataID(SubSequence);
if (!SubCompiledDataID.IsValid())
{
continue;
}
FMovieSceneEvaluationTemplate* SubEvalTemplate = const_cast<FMovieSceneEvaluationTemplate*>(CompiledDataManager->FindTrackTemplate(SubCompiledDataID));
if (SubEvalTemplate)
{
SubEvalTemplate->PurgeStaleTracks();
}
}
}
}
}
}
}
void FSequenceInstance::DestroyImmediately(UMovieSceneEntitySystemLinker* Linker)
{
if (!Ledger.IsEmpty())
{
UE_LOG(LogMovieSceneECS, Verbose, TEXT("Instance being destroyed without first having been finished by calling Finish()"));
Ledger.UnlinkEverything(Linker);
}
if (SequenceUpdater)
{
SequenceUpdater->Destroy(Linker);
}
}
void FSequenceInstance::OverrideRootSequence(UMovieSceneEntitySystemLinker* Linker, FMovieSceneSequenceID NewRootSequenceID)
{
if (SequenceUpdater)
{
check(RootInstanceHandle == InstanceHandle);
SequenceUpdater->OverrideRootSequence(Linker, RootInstanceHandle, NewRootSequenceID);
}
RootOverrideSequenceID = NewRootSequenceID;
}
FInstanceHandle FSequenceInstance::FindSubInstance(FMovieSceneSequenceID SubSequenceID) const
{
return SequenceUpdater ? SequenceUpdater->FindSubInstance(SubSequenceID) : FInstanceHandle();
}
FMovieSceneEntityID FSequenceInstance::FindEntity(UObject* Owner, uint32 EntityID) const
{
return Ledger.FindImportedEntity(FMovieSceneEvaluationFieldEntityKey{ decltype(FMovieSceneEvaluationFieldEntityKey::EntityOwner)(Owner), EntityID });
}
FSubSequencePath FSequenceInstance::GetSubSequencePath() const
{
return FSubSequencePath(SequenceID, *GetPlayer());
}
} // namespace MovieScene
} // namespace UE