Files
UnrealEngineUWP/Engine/Source/Runtime/MovieScene/Private/EntitySystem/TrackInstance/MovieSceneTrackInstanceSystem.cpp
andrew rodham f5ec397d1b Sequencer: Relinked track instance inputs are now have OnInputRemoved/OnInputAdded called on them
- When track instance input is re-linked (ie, something modified a section causing it to be invalidated in the track instance) we track whether the input previously existed in the inputs, and remove + re-add it if so. This ensures that OnInputAdded is always called when a section is modified. If OnInputAdded is not called then logic that operates on the section's data (which probably changed during the Modify that invalidated the input) will get missed.

In the case of the CVar track, all its pre-animated state capture happens in OnInputAdded - when CVars were added or removed from an existing section, pre-animated state would never be captured for the new data.

#jira UE-140559
#rb Mike.Zyracki, Matt.Hoffman
#preflight 61f84b28114ec25fe0b107c8

#ROBOMERGE-AUTHOR: andrew.rodham
#ROBOMERGE-SOURCE: CL 18796811 in //UE5/Release-5.0/... via CL 18797813 via CL 18798519
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v908-18788545)

[CL 18798631 by andrew rodham in ue5-main branch]
2022-01-31 17:04:23 -05:00

290 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EntitySystem/TrackInstance/MovieSceneTrackInstanceSystem.h"
#include "EntitySystem/TrackInstance/MovieSceneTrackInstance.h"
#include "EntitySystem/MovieSceneBoundObjectInstantiator.h"
#include "EntitySystem/MovieSceneBoundSceneComponentInstantiator.h"
#include "EntitySystem/MovieSceneMasterInstantiatorSystem.h"
#include "EntitySystem/MovieSceneEntitySystemTask.h"
#include "EntitySystem/MovieSceneEntityManager.h"
#include "EntitySystem/BuiltInComponentTypes.h"
#include "EntitySystem/MovieSceneInstanceRegistry.h"
#include "EntitySystem/MovieSceneEntitySystemLinker.h"
#include "EntitySystem/MovieSceneEntityFactoryTemplates.h"
#include "MovieSceneSection.h"
DECLARE_CYCLE_STAT(TEXT("Generic Track Instances"), MovieSceneEval_GenericTrackInstances, STATGROUP_MovieSceneECS);
DECLARE_CYCLE_STAT(TEXT("Generic Track Instances Task"), MovieSceneEval_GenericTrackInstanceTask, STATGROUP_MovieSceneECS);
namespace UE
{
namespace MovieScene
{
struct FTrackInstanceInputComponentInitializer : TChildEntityInitializer<FMovieSceneTrackInstanceComponent, FTrackInstanceInputComponent>
{
UMovieSceneTrackInstanceInstantiator* Instantiator;
explicit FTrackInstanceInputComponentInitializer(UMovieSceneTrackInstanceInstantiator* InInstantiator)
: TChildEntityInitializer<FMovieSceneTrackInstanceComponent, FTrackInstanceInputComponent>(FBuiltInComponentTypes::Get()->TrackInstance, FBuiltInComponentTypes::Get()->TrackInstanceInput)
, Instantiator(InInstantiator)
{}
virtual void Run(const FEntityRange& ChildRange, const FEntityAllocation* ParentAllocation, TArrayView<const int32> ParentAllocationOffsets) override
{
check(ParentAllocationOffsets.Num() == ChildRange.Num);
FEntityAllocationWriteContext FreshWriteContext = FEntityAllocationWriteContext::NewAllocation();
TComponentReader<FMovieSceneTrackInstanceComponent> TrackInstances = ParentAllocation->ReadComponents(GetParentComponent());
TComponentWriter<FTrackInstanceInputComponent> Inputs = ChildRange.Allocation->WriteComponents(GetChildComponent(), FreshWriteContext);
TOptionalComponentReader<UObject*> BoundObjects = ChildRange.Allocation->TryReadComponents(FBuiltInComponentTypes::Get()->BoundObject);
if (BoundObjects)
{
for (int32 Index = 0; Index < ChildRange.Num; ++Index)
{
const int32 ParentIndex = ParentAllocationOffsets[Index];
const int32 ChildIndex = ChildRange.ComponentStartOffset + Index;
// Initialize the output index
Inputs[ChildIndex].OutputIndex = Instantiator->MakeOutput(BoundObjects[ChildIndex], TrackInstances[ParentIndex].TrackInstanceClass);
}
}
else for (int32 Index = 0; Index < ChildRange.Num; ++Index)
{
const int32 ParentIndex = ParentAllocationOffsets[Index];
const int32 ChildIndex = ChildRange.ComponentStartOffset + Index;
// Initialize the output index
Inputs[ChildIndex].OutputIndex = Instantiator->MakeOutput(nullptr, TrackInstances[ParentIndex].TrackInstanceClass);
}
}
};
} // MovieScene
} // UE
UMovieSceneTrackInstanceInstantiator::UMovieSceneTrackInstanceInstantiator(const FObjectInitializer& ObjInit)
: Super(ObjInit)
{
using namespace UE::MovieScene;
if (HasAnyFlags(RF_ClassDefaultObject))
{
DefineImplicitPrerequisite(UMovieSceneMasterInstantiatorSystem::StaticClass(), GetClass());
DefineComponentConsumer(GetClass(), FBuiltInComponentTypes::Get()->BoundObject);
}
}
void UMovieSceneTrackInstanceInstantiator::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
if (!Ar.IsLoading())
{
for (FMovieSceneTrackInstanceEntry& Entry : TrackInstances)
{
FMovieSceneTrackInstanceEntry::StaticStruct()->SerializeItem(Ar, &Entry, nullptr);
}
Ar << BoundObjectToInstances;
}
}
void UMovieSceneTrackInstanceInstantiator::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
Super::AddReferencedObjects(InThis, Collector);
UMovieSceneTrackInstanceInstantiator* This = CastChecked<UMovieSceneTrackInstanceInstantiator>(InThis);
for (FMovieSceneTrackInstanceEntry& Entry : This->TrackInstances)
{
Collector.AddReferencedObject(Entry.BoundObject, This);
Collector.AddReferencedObject(Entry.TrackInstance, This);
}
for (TTuple<UObject*, int32>& Pair : This->BoundObjectToInstances)
{
Collector.AddReferencedObject(Pair.Key, This);
}
}
int32 UMovieSceneTrackInstanceInstantiator::MakeOutput(UObject* BoundObject, UClass* TrackInstanceClass)
{
for (auto It = BoundObjectToInstances.CreateConstKeyIterator(BoundObject); It; ++It)
{
const int32 AnimatorIndex = It.Value();
if (TrackInstances[AnimatorIndex].TrackInstance->GetClass() == TrackInstanceClass)
{
InvalidatedOutputs.PadToNum(AnimatorIndex + 1, false);
InvalidatedOutputs[AnimatorIndex] = true;
return AnimatorIndex;
}
}
FMovieSceneTrackInstanceEntry NewEntry;
NewEntry.BoundObject = BoundObject;
NewEntry.TrackInstance = NewObject<UMovieSceneTrackInstance>(this, TrackInstanceClass);
NewEntry.TrackInstance->Initialize(BoundObject, Linker);
const int32 NewAnimatorIndex = TrackInstances.Add(NewEntry);
BoundObjectToInstances.Add(BoundObject, NewAnimatorIndex);
InvalidatedOutputs.PadToNum(NewAnimatorIndex + 1, false);
InvalidatedOutputs[NewAnimatorIndex] = true;
return NewAnimatorIndex;
}
void UMovieSceneTrackInstanceInstantiator::OnLink()
{
using namespace UE::MovieScene;
ChildInitializerIndex = Linker->EntityManager.DefineInstancedChildInitializer(FTrackInstanceInputComponentInitializer(this));
}
void UMovieSceneTrackInstanceInstantiator::OnUnlink()
{
Linker->EntityManager.DestroyInstancedChildInitializer(ChildInitializerIndex);
}
void UMovieSceneTrackInstanceInstantiator::OnTagGarbage()
{
using namespace UE::MovieScene;
TArray<FMovieSceneEntityID> Garbage;
auto FindGarbage = [this, &Garbage](FMovieSceneEntityID EntityID, FTrackInstanceInputComponent InputComponent)
{
if (FBuiltInComponentTypes::IsBoundObjectGarbage(InputComponent.Section))
{
Garbage.Add(EntityID);
this->InvalidatedOutputs.PadToNum(InputComponent.OutputIndex + 1, false);
this->InvalidatedOutputs[InputComponent.OutputIndex] = true;
}
};
FEntityTaskBuilder()
.ReadEntityIDs()
.Read(FBuiltInComponentTypes::Get()->TrackInstanceInput)
.Iterate_PerEntity(&Linker->EntityManager, FindGarbage);
FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get();
for (FMovieSceneEntityID ID : Garbage)
{
Linker->EntityManager.AddComponent(ID, BuiltInComponents->Tags.NeedsUnlink, EEntityRecursion::Full);
}
}
void UMovieSceneTrackInstanceInstantiator::OnRun(FSystemTaskPrerequisites& InPrerequisites, FSystemSubsequentTasks& Subsequents)
{
using namespace UE::MovieScene;
FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get();
auto InvalidateOutputs = [this](FTrackInstanceInputComponent InputComponent)
{
this->InvalidatedOutputs.PadToNum(InputComponent.OutputIndex + 1, false);
this->InvalidatedOutputs[InputComponent.OutputIndex] = true;
};
FEntityTaskBuilder().Read(BuiltInComponents->TrackInstanceInput).FilterAny({ BuiltInComponents->Tags.NeedsUnlink, BuiltInComponents->Tags.NeedsLink }).Iterate_PerEntity(&Linker->EntityManager, InvalidateOutputs);
// If we've nothing else to do, don't do anything else...
if (InvalidatedOutputs.Find(true) == INDEX_NONE)
{
return;
}
// Gather all the inputs for any invalidated output indices
TSortedMap<int32, TArray<FMovieSceneTrackInstanceInput> > NewInputs;
{
auto ReLinkInputs = [this, &NewInputs, BuiltInComponents](FEntityAllocationIteratorItem Item, const FInstanceHandle* SourceInstances, const FTrackInstanceInputComponent* InputComponents)
{
const int32 Num = Item.GetAllocation()->Num();
// If the input does not have the NeedsLink tag, it has already been processed, so doesn't need removing and re-adding
const bool bInputHasBeenProcessed = !Item.GetAllocationType().Contains(BuiltInComponents->Tags.NeedsLink);
for (int32 Index = 0; Index < Num; ++Index)
{
const int32 OutputIndex = InputComponents[Index].OutputIndex;
FMovieSceneTrackInstanceInput NewInput{ InputComponents[Index].Section, SourceInstances[Index], bInputHasBeenProcessed };
if (this->InvalidatedOutputs.IsValidIndex(OutputIndex) && this->InvalidatedOutputs[OutputIndex] == true)
{
NewInputs.FindOrAdd(OutputIndex).Add(NewInput);
}
}
};
FEntityTaskBuilder()
.Read(BuiltInComponents->InstanceHandle)
.Read(BuiltInComponents->TrackInstanceInput)
.FilterNone({ BuiltInComponents->Tags.NeedsUnlink })
.Iterate_PerAllocation(&Linker->EntityManager, ReLinkInputs);
}
// Update the inputs for each of the invalidated indices
for (TTuple< int32, TArray<FMovieSceneTrackInstanceInput> >& NewInput : NewInputs)
{
const int32 ThisIndex = NewInput.Key;
// Clear the bit so it doesn't get destroyed in the next loop
InvalidatedOutputs[ThisIndex] = false;
// This check should never fire - the inputs should always have been populated above if the output index was added
check(NewInput.Value.Num() != 0);
TrackInstances[ThisIndex].TrackInstance->UpdateInputs(MoveTemp(NewInput.Value));
}
for (TConstSetBitIterator<> SetBits(InvalidatedOutputs); SetBits; ++SetBits)
{
// Destroy any outputs before this output that were invalidated and now have no inputs
const int32 DestroyIndex = SetBits.GetIndex();
FMovieSceneTrackInstanceEntry& Entry = TrackInstances[DestroyIndex];
Entry.TrackInstance->Destroy();
// Remove the entry from our LUTs
BoundObjectToInstances.Remove(Entry.BoundObject, DestroyIndex);
TrackInstances.RemoveAt(DestroyIndex);
}
InvalidatedOutputs.Reset();
}
UMovieSceneTrackInstanceSystem::UMovieSceneTrackInstanceSystem(const FObjectInitializer& ObjInit)
: Super(ObjInit)
{
RelevantComponent = UE::MovieScene::FBuiltInComponentTypes::Get()->TrackInstance;
}
void UMovieSceneTrackInstanceSystem::OnLink()
{
Instantiator = Linker->LinkSystem<UMovieSceneTrackInstanceInstantiator>();
Linker->SystemGraph.AddReference(this, Instantiator);
}
void UMovieSceneTrackInstanceSystem::OnRun(FSystemTaskPrerequisites& InPrerequisites, FSystemSubsequentTasks& Subsequents)
{
SCOPE_CYCLE_COUNTER(MovieSceneEval_GenericTrackInstances)
if (this->Instantiator->GetTrackInstances().Num() != 0)
{
auto Run = [this]
{
for (const FMovieSceneTrackInstanceEntry& Entry : this->Instantiator->GetTrackInstances())
{
if (ensure(Entry.TrackInstance))
{
Entry.TrackInstance->Animate();
}
}
};
FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady(MoveTemp(Run), GET_STATID(MovieSceneEval_GenericTrackInstanceTask), InPrerequisites.All(), Linker->EntityManager.GetGatherThread());
Subsequents.AddMasterTask(Task);
}
}