You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
MovieScene was targeted as its headers were at the top of profiling ShooterGame builds. Tested with -disableunity and -nopch. Current timings take ShooterGameEditor rebuild from 480s to 440s #jira none #rb josh.adams #preflight 624c97a63661c8f04a191889 [CL 19634779 by christopher waters in ue5-main branch]
1675 lines
60 KiB
C++
1675 lines
60 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Compilation/MovieSceneCompiledDataManager.h"
|
|
#include "Compilation/IMovieSceneTemplateGenerator.h"
|
|
#include "Compilation/IMovieSceneTrackTemplateProducer.h"
|
|
#include "Compilation/IMovieSceneDeterminismSource.h"
|
|
#include "EntitySystem/IMovieSceneEntityProvider.h"
|
|
#include "Evaluation/MovieSceneEvaluationCustomVersion.h"
|
|
#include "Evaluation/MovieSceneRootOverridePath.h"
|
|
#include "MovieScene.h"
|
|
#include "MovieSceneSequence.h"
|
|
#include "Sections/MovieSceneSubSection.h"
|
|
#include "Tracks/MovieSceneSubTrack.h"
|
|
#include "IMovieSceneModule.h"
|
|
#include "MovieSceneTimeHelpers.h"
|
|
|
|
#include "Algo/Sort.h"
|
|
#include "Algo/Unique.h"
|
|
|
|
#include "Containers/SortedMap.h"
|
|
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "UObject/Package.h"
|
|
#include "UObject/PackageReload.h"
|
|
|
|
|
|
FString GMovieSceneCompilerVersion = TEXT("7D4B98092FAC4A6B964ECF72D8279EF8");
|
|
FAutoConsoleVariableRef CVarMovieSceneCompilerVersion(
|
|
TEXT("Sequencer.CompilerVersion"),
|
|
GMovieSceneCompilerVersion,
|
|
TEXT("Defines a global identifer for moviescene compiler logic.\n"),
|
|
ECVF_Default
|
|
);
|
|
|
|
|
|
TAutoConsoleVariable<bool> CVarAddKeepStateDeterminismFences(
|
|
TEXT("Sequencer.AddKeepStateDeterminismFences"),
|
|
true,
|
|
TEXT("Whether the Sequencer compiler should auto-add determinism fences for the last frame of KeepState sections. "
|
|
"This ensures that the last possible value of the section is consistently evaluated regardless of framerate, "
|
|
"at the cost of an extra evaluation on frames that cross over KeepState sections' end time.\n"),
|
|
ECVF_Default);
|
|
|
|
|
|
IMovieSceneModule& GetMovieSceneModule()
|
|
{
|
|
static TWeakPtr<IMovieSceneModule> WeakMovieSceneModule;
|
|
|
|
TSharedPtr<IMovieSceneModule> Shared = WeakMovieSceneModule.Pin();
|
|
if (!Shared.IsValid())
|
|
{
|
|
WeakMovieSceneModule = IMovieSceneModule::Get().GetWeakPtr();
|
|
Shared = WeakMovieSceneModule.Pin();
|
|
}
|
|
check(Shared.IsValid());
|
|
|
|
return *Shared;
|
|
}
|
|
|
|
|
|
struct FMovieSceneCompileDataManagerGenerator : public IMovieSceneTemplateGenerator
|
|
{
|
|
FMovieSceneCompileDataManagerGenerator(UMovieSceneCompiledDataManager* InCompiledDataManager)
|
|
{
|
|
CompiledDataManager = InCompiledDataManager;
|
|
Entry = nullptr;
|
|
Template = nullptr;
|
|
}
|
|
|
|
void Reset(FMovieSceneCompiledDataEntry* InEntry)
|
|
{
|
|
check(InEntry);
|
|
|
|
Entry = InEntry;
|
|
Template = CompiledDataManager->TrackTemplates.Find(Entry->DataID.Value);
|
|
}
|
|
|
|
virtual void AddOwnedTrack(FMovieSceneEvaluationTrack&& InTrackTemplate, const UMovieSceneTrack& SourceTrack) override
|
|
{
|
|
check(Entry);
|
|
|
|
if (!Template)
|
|
{
|
|
Template = &CompiledDataManager->TrackTemplates.FindOrAdd(Entry->DataID.Value);
|
|
}
|
|
|
|
Template->AddTrack(SourceTrack.GetSignature(), MoveTemp(InTrackTemplate));
|
|
}
|
|
|
|
private:
|
|
|
|
UMovieSceneCompiledDataManager* CompiledDataManager;
|
|
FMovieSceneCompiledDataEntry* Entry;
|
|
FMovieSceneEvaluationTemplate* Template;
|
|
};
|
|
|
|
|
|
struct FCompileOnTheFlyData
|
|
{
|
|
/** Primary sort - group */
|
|
uint16 GroupEvaluationPriority;
|
|
/** Secondary sort - Hierarchical bias */
|
|
int16 HierarchicalBias;
|
|
/** Tertiary sort - Eval priority */
|
|
int16 EvaluationPriority;
|
|
/** Quaternary sort - Child priority */
|
|
int16 ChildPriority;
|
|
/** */
|
|
FName EvaluationGroup;
|
|
/** Whether the track requires initialization or not */
|
|
bool bRequiresInit;
|
|
bool bPriorityTearDown;
|
|
|
|
FMovieSceneEvaluationFieldTrackPtr Track;
|
|
FMovieSceneFieldEntry_ChildTemplate Child;
|
|
};
|
|
|
|
|
|
/** Gathered data for a given time or range */
|
|
struct FMovieSceneGatheredCompilerData
|
|
{
|
|
/** Tree of tracks to evaluate */
|
|
TMovieSceneEvaluationTree<FCompileOnTheFlyData> TrackTemplates;
|
|
/** Tree of active sequences */
|
|
TMovieSceneEvaluationTree<FMovieSceneSequenceID> Sequences;
|
|
FMovieSceneEntityComponentField* EntityField = nullptr;
|
|
|
|
FMovieSceneDeterminismData DeterminismData;
|
|
|
|
EMovieSceneSequenceFlags InheritedFlags = EMovieSceneSequenceFlags::None;
|
|
EMovieSceneSequenceCompilerMask AccumulatedMask = EMovieSceneSequenceCompilerMask::None;
|
|
};
|
|
|
|
/** Parameter structure used for gathering entities for a given time or range */
|
|
struct FGatherParameters
|
|
{
|
|
FGatherParameters()
|
|
: SequenceID(MovieSceneSequenceID::Root)
|
|
, RootClampRange(TRange<FFrameNumber>::All())
|
|
, LocalClampRange(RootClampRange)
|
|
, Flags(ESectionEvaluationFlags::None)
|
|
, HierarchicalBias(0)
|
|
, bHasHierarchicalEasing(false)
|
|
{}
|
|
|
|
FGatherParameters CreateForSubData(const FMovieSceneSubSequenceData& SubData, FMovieSceneSequenceID InSubSequenceID) const
|
|
{
|
|
return CreateForSubData(SubData, InSubSequenceID, FMovieSceneWarpCounter());
|
|
}
|
|
|
|
FGatherParameters CreateForSubData(const FMovieSceneSubSequenceData& SubData, FMovieSceneSequenceID InSubSequenceID, FMovieSceneWarpCounter WarpCounter) const
|
|
{
|
|
FGatherParameters SubParams = *this;
|
|
|
|
SubParams.RootToSequenceTransform = SubData.RootToSequenceTransform;
|
|
SubParams.HierarchicalBias = SubData.HierarchicalBias;
|
|
SubParams.bHasHierarchicalEasing = SubData.bHasHierarchicalEasing;
|
|
SubParams.SequenceID = InSubSequenceID;
|
|
SubParams.RootToSequenceWarpCounter = WarpCounter;
|
|
|
|
SubParams.LocalClampRange = SubData.RootToSequenceTransform.TransformRangeUnwarped(SubParams.RootClampRange);
|
|
|
|
return SubParams;
|
|
}
|
|
|
|
void SetClampRange(TRange<FFrameNumber> InNewRootClampRange)
|
|
{
|
|
RootClampRange = InNewRootClampRange;
|
|
LocalClampRange = RootToSequenceTransform.TransformRangeUnwarped(InNewRootClampRange);
|
|
}
|
|
|
|
/** Clamp the specified range to the current clamp range (in root space) */
|
|
TRange<FFrameNumber> ClampRoot(const TRange<FFrameNumber>& InRootRange) const
|
|
{
|
|
return TRange<FFrameNumber>::Intersection(RootClampRange, InRootRange);
|
|
}
|
|
|
|
/** The ID of the sequence being compiled */
|
|
FMovieSceneSequenceID SequenceID;
|
|
|
|
/** A range to clamp compilation to in the root's time-space */
|
|
TRange<FFrameNumber> RootClampRange;
|
|
/** A range to clamp compilation to in the current sequence's time-space */
|
|
TRange<FFrameNumber> LocalClampRange;
|
|
|
|
/** Evaluation flags for the current sequence */
|
|
ESectionEvaluationFlags Flags;
|
|
|
|
/** Transform from the root time-space to the current sequence's time-space */
|
|
FMovieSceneSequenceTransform RootToSequenceTransform;
|
|
|
|
/** Loop counts from the root to the current sequence */
|
|
FMovieSceneWarpCounter RootToSequenceWarpCounter;
|
|
|
|
/** Current accumulated hierarchical bias */
|
|
int16 HierarchicalBias;
|
|
|
|
/** Whether the current sequence is receiving hierarchical easing from some parent sequence */
|
|
bool bHasHierarchicalEasing;
|
|
|
|
EMovieSceneServerClientMask NetworkMask;
|
|
};
|
|
|
|
/** Parameter structure used for gathering entities for a given time or range */
|
|
struct FTrackGatherParameters : FGatherParameters
|
|
{
|
|
FTrackGatherParameters(UMovieSceneCompiledDataManager* InCompiledDataManager)
|
|
: TemplateGenerator(InCompiledDataManager)
|
|
{}
|
|
|
|
FTrackGatherParameters CreateForSubData(const FMovieSceneSubSequenceData& SubData, FMovieSceneSequenceID InSubSequenceID, FMovieSceneWarpCounter WarpCounter) const
|
|
{
|
|
FTrackGatherParameters SubParams = *this;
|
|
static_cast<FGatherParameters&>(SubParams) = FGatherParameters::CreateForSubData(SubData, InSubSequenceID, WarpCounter);
|
|
|
|
return SubParams;
|
|
}
|
|
|
|
|
|
/** Store from which to retrieve templates */
|
|
mutable FMovieSceneCompileDataManagerGenerator TemplateGenerator;
|
|
};
|
|
|
|
|
|
bool SortPredicate(const FCompileOnTheFlyData& A, const FCompileOnTheFlyData& B)
|
|
{
|
|
if (A.GroupEvaluationPriority != B.GroupEvaluationPriority)
|
|
{
|
|
return A.GroupEvaluationPriority > B.GroupEvaluationPriority;
|
|
}
|
|
else if (A.HierarchicalBias != B.HierarchicalBias)
|
|
{
|
|
return A.HierarchicalBias < B.HierarchicalBias;
|
|
}
|
|
else if (A.EvaluationPriority != B.EvaluationPriority)
|
|
{
|
|
return A.EvaluationPriority > B.EvaluationPriority;
|
|
}
|
|
else
|
|
{
|
|
return A.ChildPriority > B.ChildPriority;
|
|
}
|
|
}
|
|
|
|
void AddPtrsToGroup(
|
|
FMovieSceneEvaluationGroup* OutGroup,
|
|
TArray<FMovieSceneFieldEntry_EvaluationTrack>& InitTrackLUT,
|
|
TArray<FMovieSceneFieldEntry_ChildTemplate>& InitSectionLUT,
|
|
TArray<FMovieSceneFieldEntry_EvaluationTrack>& EvalTrackLUT,
|
|
TArray<FMovieSceneFieldEntry_ChildTemplate>& EvalSectionLUT
|
|
)
|
|
{
|
|
if (!InitTrackLUT.Num() && !EvalTrackLUT.Num())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FMovieSceneEvaluationGroupLUTIndex Index;
|
|
Index.NumInitPtrs = InitTrackLUT.Num();
|
|
Index.NumEvalPtrs = EvalTrackLUT.Num();
|
|
|
|
OutGroup->LUTIndices.Add(Index);
|
|
OutGroup->TrackLUT.Append(InitTrackLUT);
|
|
OutGroup->TrackLUT.Append(EvalTrackLUT);
|
|
|
|
OutGroup->SectionLUT.Append(InitSectionLUT);
|
|
OutGroup->SectionLUT.Append(EvalSectionLUT);
|
|
|
|
InitTrackLUT.Reset();
|
|
InitSectionLUT.Reset();
|
|
EvalTrackLUT.Reset();
|
|
EvalSectionLUT.Reset();
|
|
}
|
|
|
|
FMovieSceneCompiledDataEntry::FMovieSceneCompiledDataEntry()
|
|
: AccumulatedFlags(EMovieSceneSequenceFlags::None)
|
|
, AccumulatedMask(EMovieSceneSequenceCompilerMask::None)
|
|
{}
|
|
|
|
UMovieSceneSequence* FMovieSceneCompiledDataEntry::GetSequence() const
|
|
{
|
|
return CastChecked<UMovieSceneSequence>(SequenceKey.ResolveObjectPtr(), ECastCheckedType::NullAllowed);
|
|
}
|
|
|
|
UMovieSceneCompiledData::UMovieSceneCompiledData()
|
|
{
|
|
AccumulatedMask = EMovieSceneSequenceCompilerMask::None;
|
|
AllocatedMask = EMovieSceneSequenceCompilerMask::None;
|
|
AccumulatedFlags = EMovieSceneSequenceFlags::None;
|
|
}
|
|
|
|
void UMovieSceneCompiledData::Reset()
|
|
{
|
|
EvaluationTemplate = FMovieSceneEvaluationTemplate();
|
|
Hierarchy = FMovieSceneSequenceHierarchy();
|
|
EntityComponentField = FMovieSceneEntityComponentField();
|
|
TrackTemplateField = FMovieSceneEvaluationField();
|
|
DeterminismFences.Reset();
|
|
CompiledSignature.Invalidate();
|
|
CompilerVersion.Invalidate();
|
|
AccumulatedMask = EMovieSceneSequenceCompilerMask::None;
|
|
AllocatedMask = EMovieSceneSequenceCompilerMask::None;
|
|
AccumulatedFlags = EMovieSceneSequenceFlags::None;
|
|
}
|
|
|
|
UMovieSceneCompiledDataManager::UMovieSceneCompiledDataManager()
|
|
{
|
|
const bool bParsed = FGuid::Parse(GMovieSceneCompilerVersion, CompilerVersion);
|
|
ensureMsgf(bParsed, TEXT("Invalid compiler version specific - this will break any persistent compiled data"));
|
|
|
|
IConsoleManager::Get().RegisterConsoleVariableSink_Handle(FConsoleCommandDelegate::CreateUObject(this, &UMovieSceneCompiledDataManager::ConsoleVariableSink));
|
|
|
|
ReallocationVersion = 0;
|
|
NetworkMask = EMovieSceneServerClientMask::All;
|
|
|
|
auto OnPackageReloaded = [this](const EPackageReloadPhase InPackageReloadPhase, FPackageReloadedEvent* InPackageReloadedEvent)
|
|
{
|
|
if (InPackageReloadPhase != EPackageReloadPhase::OnPackageFixup)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (const TPair<UObject*, UObject*>& Pair : InPackageReloadedEvent->GetRepointedObjects())
|
|
{
|
|
UMovieSceneSequence* OldSequence = Cast<UMovieSceneSequence>(Pair.Key);
|
|
UMovieSceneSequence* NewSequence = Cast<UMovieSceneSequence>(Pair.Value);
|
|
if (OldSequence && NewSequence)
|
|
{
|
|
FMovieSceneCompiledDataID DataID = this->SequenceToDataIDs.FindRef(OldSequence);
|
|
if (DataID.IsValid())
|
|
{
|
|
// Repoint the data ID for the old sequence to the new sequence
|
|
{
|
|
FMovieSceneCompiledDataEntry& Entry = CompiledDataEntries[DataID.Value];
|
|
this->SequenceToDataIDs.Remove(Entry.SequenceKey);
|
|
|
|
// Entry is a ref here, so care is taken to ensure we do not allocate CompiledDataEntries while the ref is around
|
|
Entry = FMovieSceneCompiledDataEntry();
|
|
Entry.SequenceKey = NewSequence;
|
|
Entry.DataID = DataID;
|
|
|
|
this->SequenceToDataIDs.Add(Entry.SequenceKey, DataID);
|
|
}
|
|
|
|
// Destroy all the old compiled data as it is no longer valid
|
|
this->Hierarchies.Remove(DataID.Value);
|
|
this->TrackTemplates.Remove(DataID.Value);
|
|
this->TrackTemplateFields.Remove(DataID.Value);
|
|
this->EntityComponentFields.Remove(DataID.Value);
|
|
|
|
++this->ReallocationVersion;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
if (!HasAnyFlags(RF_ClassDefaultObject))
|
|
{
|
|
FCoreUObjectDelegates::OnPackageReloaded.AddWeakLambda(this, OnPackageReloaded);
|
|
}
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::DestroyAllData()
|
|
{
|
|
// Eradicate all compiled data
|
|
for (int32 Index = 0; Index < CompiledDataEntries.GetMaxIndex(); ++Index)
|
|
{
|
|
if (CompiledDataEntries.IsAllocated(Index))
|
|
{
|
|
FMovieSceneCompiledDataEntry& Entry = CompiledDataEntries[Index];
|
|
Entry.CompiledSignature = FGuid();
|
|
Entry.AccumulatedFlags = EMovieSceneSequenceFlags::None;
|
|
Entry.AccumulatedMask = EMovieSceneSequenceCompilerMask::None;
|
|
}
|
|
}
|
|
|
|
Hierarchies.Empty();
|
|
TrackTemplates.Empty();
|
|
TrackTemplateFields.Empty();
|
|
EntityComponentFields.Empty();
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::ConsoleVariableSink()
|
|
{
|
|
FGuid NewCompilerVersion;
|
|
const bool bParsed = FGuid::Parse(GMovieSceneCompilerVersion, NewCompilerVersion);
|
|
ensureMsgf(bParsed, TEXT("Invalid compiler version specific - this will break any persistent compiled data"));
|
|
|
|
if (CompilerVersion != NewCompilerVersion)
|
|
{
|
|
DestroyAllData();
|
|
}
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::CopyCompiledData(UMovieSceneSequence* Sequence)
|
|
{
|
|
UMovieSceneCompiledData* CompiledData = Sequence->GetOrCreateCompiledData();
|
|
CompiledData->Reset();
|
|
|
|
FMovieSceneCompiledDataID DataID = GetDataID(Sequence);
|
|
Compile(DataID, Sequence);
|
|
|
|
if (const FMovieSceneSequenceHierarchy* Hierarchy = FindHierarchy(DataID))
|
|
{
|
|
CompiledData->Hierarchy = *Hierarchy;
|
|
CompiledData->AllocatedMask.bHierarchy = true;
|
|
}
|
|
if (const FMovieSceneEvaluationTemplate* TrackTemplate = FindTrackTemplate(DataID))
|
|
{
|
|
CompiledData->EvaluationTemplate = *TrackTemplate;
|
|
CompiledData->AllocatedMask.bEvaluationTemplate = true;
|
|
}
|
|
if (const FMovieSceneEvaluationField* TrackTemplateField = FindTrackTemplateField(DataID))
|
|
{
|
|
if (Sequence->IsPlayableDirectly())
|
|
{
|
|
CompiledData->TrackTemplateField = *TrackTemplateField;
|
|
CompiledData->AllocatedMask.bEvaluationTemplateField = true;
|
|
}
|
|
}
|
|
if (const FMovieSceneEntityComponentField* EntityComponentField = FindEntityComponentField(DataID))
|
|
{
|
|
CompiledData->EntityComponentField = *EntityComponentField;
|
|
CompiledData->AllocatedMask.bEntityComponentField = true;
|
|
}
|
|
|
|
const FMovieSceneCompiledDataEntry& DataEntry = CompiledDataEntries[DataID.Value];
|
|
CompiledData->DeterminismFences = DataEntry.DeterminismFences;
|
|
CompiledData->CompiledSignature = Sequence->GetSignature();
|
|
CompiledData->CompilerVersion = CompilerVersion;
|
|
CompiledData->AccumulatedMask = DataEntry.AccumulatedMask;
|
|
CompiledData->AccumulatedFlags = DataEntry.AccumulatedFlags;
|
|
CompiledData->CompiledFlags = DataEntry.CompiledFlags;
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::LoadCompiledData(UMovieSceneSequence* Sequence)
|
|
{
|
|
// This can be called during Async Loads
|
|
FScopeLock AsyncLoadLock(&AsyncLoadCriticalSection);
|
|
|
|
UMovieSceneCompiledData* CompiledData = Sequence->GetCompiledData();
|
|
if (CompiledData)
|
|
{
|
|
FMovieSceneCompiledDataID DataID = GetDataID(Sequence);
|
|
|
|
if (CompiledData->CompilerVersion != CompilerVersion)
|
|
{
|
|
CompiledDataEntries[DataID.Value].AccumulatedFlags |= EMovieSceneSequenceFlags::Volatile;
|
|
return;
|
|
}
|
|
|
|
if (CompiledData->AllocatedMask.bHierarchy)
|
|
{
|
|
Hierarchies.Add(DataID.Value, MoveTemp(CompiledData->Hierarchy));
|
|
}
|
|
if (CompiledData->AllocatedMask.bEvaluationTemplate)
|
|
{
|
|
TrackTemplates.Add(DataID.Value, MoveTemp(CompiledData->EvaluationTemplate));
|
|
}
|
|
if (CompiledData->AllocatedMask.bEvaluationTemplateField)
|
|
{
|
|
TrackTemplateFields.Add(DataID.Value, MoveTemp(CompiledData->TrackTemplateField));
|
|
}
|
|
if (CompiledData->AllocatedMask.bEntityComponentField)
|
|
{
|
|
EntityComponentFields.Add(DataID.Value, MoveTemp(CompiledData->EntityComponentField));
|
|
}
|
|
|
|
FMovieSceneCompiledDataEntry* EntryPtr = GetEntryPtr(DataID);
|
|
|
|
EntryPtr->DeterminismFences = MoveTemp(CompiledData->DeterminismFences);
|
|
EntryPtr->CompiledSignature = CompiledData->CompiledSignature;
|
|
EntryPtr->AccumulatedMask = CompiledData->AccumulatedMask.AsEnum();
|
|
EntryPtr->AccumulatedFlags = CompiledData->AccumulatedFlags;
|
|
EntryPtr->CompiledFlags = CompiledData->CompiledFlags;
|
|
|
|
++ReallocationVersion;
|
|
}
|
|
else
|
|
{
|
|
Reset(Sequence);
|
|
}
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::SetEmulatedNetworkMask(EMovieSceneServerClientMask NewMask)
|
|
{
|
|
DestroyAllData();
|
|
NetworkMask = NewMask;
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::Reset(UMovieSceneSequence* Sequence)
|
|
{
|
|
// Care is taken here not to use GetDataID which _creates_ a new data ID if
|
|
// one is not available. This ensures that calling Reset() does not create
|
|
// new data for sequences that have not yet been encountered
|
|
FMovieSceneCompiledDataID DataID = SequenceToDataIDs.FindRef(Sequence);
|
|
if (DataID.IsValid())
|
|
{
|
|
DestroyData(DataID);
|
|
SequenceToDataIDs.Remove(Sequence);
|
|
}
|
|
}
|
|
|
|
FMovieSceneCompiledDataID UMovieSceneCompiledDataManager::FindDataID(UMovieSceneSequence* Sequence) const
|
|
{
|
|
return SequenceToDataIDs.FindRef(Sequence);
|
|
}
|
|
|
|
FMovieSceneCompiledDataID UMovieSceneCompiledDataManager::GetDataID(UMovieSceneSequence* Sequence)
|
|
{
|
|
check(Sequence);
|
|
|
|
FMovieSceneCompiledDataID ExistingDataID = FindDataID(Sequence);
|
|
if (ExistingDataID.IsValid())
|
|
{
|
|
return ExistingDataID;
|
|
}
|
|
|
|
const int32 Index = CompiledDataEntries.Add(FMovieSceneCompiledDataEntry());
|
|
|
|
ExistingDataID = FMovieSceneCompiledDataID { Index };
|
|
FMovieSceneCompiledDataEntry& NewEntry = CompiledDataEntries[Index];
|
|
|
|
NewEntry.SequenceKey = Sequence;
|
|
NewEntry.DataID = ExistingDataID;
|
|
NewEntry.AccumulatedFlags = Sequence->GetFlags();
|
|
|
|
SequenceToDataIDs.Add(Sequence, ExistingDataID);
|
|
return ExistingDataID;
|
|
}
|
|
|
|
FMovieSceneCompiledDataID UMovieSceneCompiledDataManager::GetSubDataID(FMovieSceneCompiledDataID DataID, FMovieSceneSequenceID SubSequenceID)
|
|
{
|
|
if (SubSequenceID == MovieSceneSequenceID::Root)
|
|
{
|
|
return DataID;
|
|
}
|
|
|
|
const FMovieSceneSequenceHierarchy* Hierarchy = FindHierarchy(DataID);
|
|
if (Hierarchy)
|
|
{
|
|
const FMovieSceneSubSequenceData* SubData = Hierarchy->FindSubData(SubSequenceID);
|
|
UMovieSceneSequence* SubSequence = SubData ? SubData->GetSequence() : nullptr;
|
|
|
|
if (SubSequence)
|
|
{
|
|
return GetDataID(SubSequence);
|
|
}
|
|
}
|
|
|
|
return FMovieSceneCompiledDataID();
|
|
}
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
UMovieSceneCompiledDataManager* UMovieSceneCompiledDataManager::GetPrecompiledData(EMovieSceneServerClientMask EmulatedMask)
|
|
{
|
|
ensureMsgf(!GExitPurge, TEXT("Attempting to access precompiled data manager during shutdown - this is undefined behavior since the manager may have already been destroyed, or could be unconstrictible"));
|
|
|
|
if (EmulatedMask == EMovieSceneServerClientMask::Client)
|
|
{
|
|
static UMovieSceneCompiledDataManager* GEmulatedClientDataManager = NewObject<UMovieSceneCompiledDataManager>(GetTransientPackage(), "EmulatedClientDataManager", RF_MarkAsRootSet);
|
|
GEmulatedClientDataManager->NetworkMask = EMovieSceneServerClientMask::Client;
|
|
return GEmulatedClientDataManager;
|
|
}
|
|
|
|
if (EmulatedMask == EMovieSceneServerClientMask::Server)
|
|
{
|
|
static UMovieSceneCompiledDataManager* GEmulatedServerDataManager = NewObject<UMovieSceneCompiledDataManager>(GetTransientPackage(), "EmulatedServerDataManager", RF_MarkAsRootSet);
|
|
GEmulatedServerDataManager->NetworkMask = EMovieSceneServerClientMask::Server;
|
|
return GEmulatedServerDataManager;
|
|
}
|
|
|
|
static UMovieSceneCompiledDataManager* GPrecompiledDataManager = NewObject<UMovieSceneCompiledDataManager>(GetTransientPackage(), "PrecompiledDataManager", RF_MarkAsRootSet);
|
|
return GPrecompiledDataManager;
|
|
}
|
|
|
|
#else // WITH_EDITOR
|
|
|
|
UMovieSceneCompiledDataManager* UMovieSceneCompiledDataManager::GetPrecompiledData()
|
|
{
|
|
ensureMsgf(!GExitPurge, TEXT("Attempting to access precompiled data manager during shutdown - this is undefined behavior since the manager may have already been destroyed, or could be unconstrictible"));
|
|
|
|
static UMovieSceneCompiledDataManager* GPrecompiledDataManager = NewObject<UMovieSceneCompiledDataManager>(GetTransientPackage(), "PrecompiledDataManager", RF_MarkAsRootSet);
|
|
return GPrecompiledDataManager;
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|
|
|
|
void UMovieSceneCompiledDataManager::DestroyData(FMovieSceneCompiledDataID DataID)
|
|
{
|
|
check(DataID.IsValid() && CompiledDataEntries.IsValidIndex(DataID.Value));
|
|
|
|
Hierarchies.Remove(DataID.Value);
|
|
TrackTemplates.Remove(DataID.Value);
|
|
TrackTemplateFields.Remove(DataID.Value);
|
|
EntityComponentFields.Remove(DataID.Value);
|
|
|
|
CompiledDataEntries.RemoveAt(DataID.Value);
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::DestroyTemplate(FMovieSceneCompiledDataID DataID)
|
|
{
|
|
check(DataID.IsValid() && CompiledDataEntries.IsValidIndex(DataID.Value));
|
|
|
|
// Remove the lookup entry for this sequence/network mask combination
|
|
const FMovieSceneCompiledDataEntry& Entry = CompiledDataEntries[DataID.Value];
|
|
SequenceToDataIDs.Remove(Entry.SequenceKey);
|
|
|
|
DestroyData(DataID);
|
|
}
|
|
|
|
bool UMovieSceneCompiledDataManager::IsDirty(const FMovieSceneCompiledDataEntry& Entry) const
|
|
{
|
|
if (!Entry.GetSequence())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (Entry.CompiledSignature != Entry.GetSequence()->GetSignature())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (const FMovieSceneSequenceHierarchy* Hierarchy = FindHierarchy(Entry.DataID))
|
|
{
|
|
for (const TTuple<FMovieSceneSequenceID, FMovieSceneSubSequenceData>& Pair : Hierarchy->AllSubSequenceData())
|
|
{
|
|
if (UMovieSceneSequence* SubSequence = Pair.Value.GetSequence())
|
|
{
|
|
FMovieSceneCompiledDataID SubDataID = FindDataID(SubSequence);
|
|
if (!SubDataID.IsValid() || CompiledDataEntries[SubDataID.Value].CompiledSignature != SubSequence->GetSignature())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool UMovieSceneCompiledDataManager::IsDirty(FMovieSceneCompiledDataID CompiledDataID) const
|
|
{
|
|
check(CompiledDataID.IsValid() && CompiledDataEntries.IsValidIndex(CompiledDataID.Value));
|
|
return IsDirty(CompiledDataEntries[CompiledDataID.Value]);
|
|
}
|
|
|
|
bool UMovieSceneCompiledDataManager::IsDirty(UMovieSceneSequence* Sequence) const
|
|
{
|
|
FMovieSceneCompiledDataID ExistingDataID = FindDataID(Sequence);
|
|
if (ExistingDataID.IsValid())
|
|
{
|
|
check(CompiledDataEntries.IsValidIndex(ExistingDataID.Value));
|
|
FMovieSceneCompiledDataEntry Entry = CompiledDataEntries[ExistingDataID.Value];
|
|
return IsDirty(Entry);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void UMovieSceneCompiledDataManager::Compile(FMovieSceneCompiledDataID DataID)
|
|
{
|
|
check(DataID.IsValid() && CompiledDataEntries.IsValidIndex(DataID.Value));
|
|
UMovieSceneSequence* Sequence = CompiledDataEntries[DataID.Value].GetSequence();
|
|
check(Sequence);
|
|
Compile(DataID, Sequence);
|
|
}
|
|
|
|
FMovieSceneCompiledDataID UMovieSceneCompiledDataManager::Compile(UMovieSceneSequence* Sequence)
|
|
{
|
|
FMovieSceneCompiledDataID DataID = GetDataID(Sequence);
|
|
Compile(DataID, Sequence);
|
|
return DataID;
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::Compile(FMovieSceneCompiledDataID DataID, UMovieSceneSequence* Sequence)
|
|
{
|
|
check(DataID.IsValid() && CompiledDataEntries.IsValidIndex(DataID.Value));
|
|
FMovieSceneCompiledDataEntry Entry = CompiledDataEntries[DataID.Value];
|
|
if (!IsDirty(Entry))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FMovieSceneGatheredCompilerData GatheredData;
|
|
FTrackGatherParameters Params(this);
|
|
|
|
Entry.DeterminismFences.Empty();
|
|
Entry.AccumulatedFlags = Sequence->GetFlags();
|
|
Params.TemplateGenerator.Reset(&Entry);
|
|
Params.NetworkMask = NetworkMask;
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// Step 1 - Always ensure the hierarchy information is completely up to date first
|
|
FMovieSceneSequenceHierarchy NewHierarchy;
|
|
const bool bHasHierarchy = CompileHierarchy(Sequence, Params, &NewHierarchy);
|
|
|
|
if (IMovieSceneDeterminismSource* DeterminismSource = Cast<IMovieSceneDeterminismSource>(Sequence))
|
|
{
|
|
DeterminismSource->PopulateDeterminismData(GatheredData.DeterminismData, TRange<FFrameNumber>::All());
|
|
}
|
|
|
|
TSet<FGuid> GatheredSignatures;
|
|
|
|
{
|
|
UMovieScene* MovieScene = Sequence->GetMovieScene();
|
|
|
|
if (ensure(MovieScene))
|
|
{
|
|
for (const FMovieSceneMarkedFrame& Mark : MovieScene->GetMarkedFrames())
|
|
{
|
|
if (Mark.bIsDeterminismFence)
|
|
{
|
|
GatheredData.DeterminismData.Fences.Add(Mark.FrameNumber);
|
|
}
|
|
}
|
|
|
|
if (UMovieSceneTrack* Track = MovieScene->GetCameraCutTrack())
|
|
{
|
|
CompileTrack(&Entry, nullptr, Track, Params, &GatheredSignatures, &GatheredData);
|
|
}
|
|
|
|
for (UMovieSceneTrack* Track : MovieScene->GetMasterTracks())
|
|
{
|
|
CompileTrack(&Entry, nullptr, Track, Params, &GatheredSignatures, &GatheredData);
|
|
}
|
|
|
|
for (const FMovieSceneBinding& ObjectBinding : MovieScene->GetBindings())
|
|
{
|
|
for (UMovieSceneTrack* Track : ObjectBinding.GetTracks())
|
|
{
|
|
CompileTrack(&Entry, &ObjectBinding, Track, Params, &GatheredSignatures, &GatheredData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// Step 2 - Gather compilation data
|
|
FMovieSceneEntityComponentField ThisSequenceEntityField;
|
|
|
|
{
|
|
GatheredData.EntityField = &ThisSequenceEntityField;
|
|
Gather(Entry, Sequence, Params, &GatheredData);
|
|
GatheredData.EntityField = nullptr;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// Step 3 - Assign entity field from data gathered for _this sequence only_
|
|
if (ThisSequenceEntityField.IsEmpty())
|
|
{
|
|
EntityComponentFields.Remove(DataID.Value);
|
|
}
|
|
else
|
|
{
|
|
// EntityComponent data is not flattened so we assign that now after the initial gather
|
|
EntityComponentFields.FindOrAdd(DataID.Value) = MoveTemp(ThisSequenceEntityField);
|
|
GatheredData.AccumulatedMask |= EMovieSceneSequenceCompilerMask::EntityComponentField;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// Step 4 - If we have a hierarchy, perform a gather for sub sequences
|
|
if (bHasHierarchy)
|
|
{
|
|
CompileSubSequences(NewHierarchy, Params, &GatheredData);
|
|
Entry.AccumulatedFlags |= GatheredData.InheritedFlags;
|
|
Entry.AccumulatedMask |= GatheredData.AccumulatedMask;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// Step 5 - Consolidate track template data from gathered data
|
|
if (FMovieSceneEvaluationTemplate* TrackTemplate = TrackTemplates.Find(Entry.DataID.Value))
|
|
{
|
|
TrackTemplate->RemoveStaleData(GatheredSignatures);
|
|
}
|
|
|
|
CompileTrackTemplateField(&Entry, NewHierarchy, &GatheredData);
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// Step 6 - Reassign or remove the new hierarchy
|
|
if (bHasHierarchy)
|
|
{
|
|
Hierarchies.FindOrAdd(DataID.Value) = MoveTemp(NewHierarchy);
|
|
}
|
|
else
|
|
{
|
|
Hierarchies.Remove(DataID.Value);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------------
|
|
// Step 7: Apply the final state to the entry
|
|
Entry.CompiledFlags.bParentSequenceRequiresLowerFence = GatheredData.DeterminismData.bParentSequenceRequiresLowerFence;
|
|
Entry.CompiledFlags.bParentSequenceRequiresUpperFence = GatheredData.DeterminismData.bParentSequenceRequiresUpperFence;
|
|
Entry.CompiledSignature = Sequence->GetSignature();
|
|
Entry.AccumulatedMask = GatheredData.AccumulatedMask;
|
|
Entry.DeterminismFences = MoveTemp(GatheredData.DeterminismData.Fences);
|
|
if (Entry.DeterminismFences.Num())
|
|
{
|
|
Algo::Sort(Entry.DeterminismFences);
|
|
const int32 NewNum = Algo::Unique(Entry.DeterminismFences);
|
|
if (NewNum != Entry.DeterminismFences.Num())
|
|
{
|
|
Entry.DeterminismFences.SetNum(NewNum);
|
|
}
|
|
}
|
|
|
|
CompiledDataEntries[DataID.Value] = Entry;
|
|
++ReallocationVersion;
|
|
|
|
#if 0
|
|
#if !NO_LOGGING
|
|
if (bHasHierarchy)
|
|
{
|
|
FMovieSceneSequenceHierarchy* HierarchyToLog = Hierarchies.Find(DataID.Value);
|
|
if (ensure(HierarchyToLog))
|
|
{
|
|
UE_LOG(LogMovieScene, Log, TEXT("Newly compiled sequence hierarchy:"));
|
|
HierarchyToLog->LogHierarchy();
|
|
HierarchyToLog->LogSubSequenceTree();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogMovieScene, Log, TEXT("No sequence hierarchy"));
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
|
|
void UMovieSceneCompiledDataManager::Gather(const FMovieSceneCompiledDataEntry& Entry, UMovieSceneSequence* Sequence, const FTrackGatherParameters& Params, FMovieSceneGatheredCompilerData* OutCompilerData) const
|
|
{
|
|
const FMovieSceneEvaluationTemplate* TrackTemplate = FindTrackTemplate(Entry.DataID);
|
|
|
|
UMovieScene* MovieScene = Sequence->GetMovieScene();
|
|
|
|
if (ensure(MovieScene))
|
|
{
|
|
if (UMovieSceneTrack* Track = MovieScene->GetCameraCutTrack())
|
|
{
|
|
GatherTrack(nullptr, Track, Params, TrackTemplate, OutCompilerData);
|
|
}
|
|
|
|
for (UMovieSceneTrack* Track : MovieScene->GetMasterTracks())
|
|
{
|
|
GatherTrack(nullptr, Track, Params, TrackTemplate, OutCompilerData);
|
|
}
|
|
|
|
for (const FMovieSceneBinding& ObjectBinding : MovieScene->GetBindings())
|
|
{
|
|
for (UMovieSceneTrack* Track : ObjectBinding.GetTracks())
|
|
{
|
|
GatherTrack(&ObjectBinding, Track, Params, TrackTemplate, OutCompilerData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::CompileSubSequences(const FMovieSceneSequenceHierarchy& Hierarchy, const FTrackGatherParameters& Params, FMovieSceneGatheredCompilerData* OutCompilerData)
|
|
{
|
|
using namespace UE::MovieScene;
|
|
|
|
OutCompilerData->AccumulatedMask |= EMovieSceneSequenceCompilerMask::Hierarchy;
|
|
|
|
// Ensure all sub sequences are compiled
|
|
for (const TTuple<FMovieSceneSequenceID, FMovieSceneSubSequenceData>& Pair : Hierarchy.AllSubSequenceData())
|
|
{
|
|
if (UMovieSceneSequence* SubSequence = Pair.Value.GetSequence())
|
|
{
|
|
Compile(SubSequence);
|
|
}
|
|
}
|
|
|
|
const TMovieSceneEvaluationTree<FMovieSceneSubSequenceTreeEntry>& SubSequenceTree = Hierarchy.GetTree();
|
|
|
|
// When adding determinism fences for sub sequences, we track the iteration index for each sequence ID so that
|
|
// we only add a fence when the sub sequence truly ends or begins, not for every segmentation of the sub sequence tree
|
|
struct FSubSequenceItMetaData
|
|
{
|
|
int32 LastIterIndex = INDEX_NONE;
|
|
TOptional<FFrameNumber> TrailingFence;
|
|
|
|
};
|
|
TSortedMap<FMovieSceneSequenceID, FSubSequenceItMetaData> ItMetaData;
|
|
|
|
// Start iterating the field from the lower bound of the compile range
|
|
FMovieSceneEvaluationTreeRangeIterator SubSequenceIt = SubSequenceTree.IterateFromLowerBound(Params.RootClampRange.GetLowerBound());
|
|
for ( int32 ItIndex = 0; SubSequenceIt && SubSequenceIt.Range().Overlaps(Params.RootClampRange); ++SubSequenceIt, ++ItIndex)
|
|
{
|
|
// Iterate all sub sequences in the current range
|
|
for (const FMovieSceneSubSequenceTreeEntry& SubSequenceEntry : SubSequenceTree.GetAllData(SubSequenceIt.Node()))
|
|
{
|
|
FMovieSceneSequenceID SubSequenceID = SubSequenceEntry.SequenceID;
|
|
|
|
const FMovieSceneSubSequenceData* SubData = Hierarchy.FindSubData(SubSequenceID);
|
|
checkf(SubData, TEXT("Sub data could not be found for a sequence that exists in the sub sequence tree - this indicates an error while populating the sub sequence hierarchy tree."));
|
|
|
|
UMovieSceneSequence* SubSequence = SubData->GetSequence();
|
|
if (SubSequence)
|
|
{
|
|
FTrackGatherParameters SubSectionGatherParams = Params.CreateForSubData(*SubData, SubSequenceID, SubSequenceEntry.RootToSequenceWarpCounter);
|
|
SubSectionGatherParams.Flags |= SubSequenceEntry.Flags;
|
|
SubSectionGatherParams.SetClampRange(SubSequenceIt.Range());
|
|
|
|
// Access the sub entry data after compilation
|
|
FMovieSceneCompiledDataID SubDataID = GetDataID(SubSequence);
|
|
check(SubDataID.IsValid());
|
|
|
|
// Gather track template data for the sub sequence
|
|
FMovieSceneCompiledDataEntry SubEntry = CompiledDataEntries[SubDataID.Value];
|
|
if (TrackTemplates.Contains(SubDataID.Value))
|
|
{
|
|
Gather(SubEntry, SubSequence, SubSectionGatherParams, OutCompilerData);
|
|
}
|
|
|
|
// Inherit flags from sub sequences (if a sub sequence is volatile, so must this be)
|
|
OutCompilerData->InheritedFlags |= (CompiledDataEntries[SubDataID.Value].AccumulatedFlags & EMovieSceneSequenceFlags::InheritedFlags);
|
|
OutCompilerData->AccumulatedMask |= SubEntry.AccumulatedMask;
|
|
|
|
FSubSequenceItMetaData* MetaData = &ItMetaData.FindOrAdd(SubSequenceID);
|
|
|
|
const bool bWasEvaluatedLastFrame = MetaData->LastIterIndex != INDEX_NONE && MetaData->LastIterIndex == ItIndex-1;
|
|
if (SubEntry.CompiledFlags.bParentSequenceRequiresLowerFence && bWasEvaluatedLastFrame == false)
|
|
{
|
|
OutCompilerData->DeterminismData.Fences.Add(DiscreteInclusiveLower(SubSequenceIt.Range()));
|
|
}
|
|
if (SubEntry.CompiledFlags.bParentSequenceRequiresUpperFence)
|
|
{
|
|
MetaData->TrailingFence = DiscreteExclusiveUpper(SubSequenceIt.Range());
|
|
}
|
|
|
|
MetaData->LastIterIndex = ItIndex;
|
|
}
|
|
}
|
|
|
|
for (TPair<FMovieSceneSequenceID, FSubSequenceItMetaData>& Pair : ItMetaData)
|
|
{
|
|
if (Pair.Value.LastIterIndex == ItIndex-1 && Pair.Value.TrailingFence.IsSet())
|
|
{
|
|
OutCompilerData->DeterminismData.Fences.Add(Pair.Value.TrailingFence.GetValue());
|
|
Pair.Value.TrailingFence.Reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UMovieSceneCompiledDataManager::CompileTrackTemplateField(FMovieSceneCompiledDataEntry* OutEntry, const FMovieSceneSequenceHierarchy& Hierarchy, FMovieSceneGatheredCompilerData* InCompilerData)
|
|
{
|
|
if (!EnumHasAnyFlags(InCompilerData->AccumulatedMask, EMovieSceneSequenceCompilerMask::EvaluationTemplate))
|
|
{
|
|
TrackTemplateFields.Remove(OutEntry->DataID.Value);
|
|
return;
|
|
}
|
|
|
|
|
|
FMovieSceneEvaluationField* TrackTemplateField = &TrackTemplateFields.FindOrAdd(OutEntry->DataID.Value);
|
|
|
|
// Wipe the current evaluation field for the template
|
|
*TrackTemplateField = FMovieSceneEvaluationField();
|
|
|
|
InCompilerData->AccumulatedMask |= EMovieSceneSequenceCompilerMask::EvaluationTemplateField;
|
|
|
|
TArray<FCompileOnTheFlyData> CompileData;
|
|
for (FMovieSceneEvaluationTreeRangeIterator It(InCompilerData->TrackTemplates); It; ++It)
|
|
{
|
|
CompileData.Reset();
|
|
|
|
TRange<FFrameNumber> FieldRange = It.Range();
|
|
for (const FCompileOnTheFlyData& TrackData : InCompilerData->TrackTemplates.GetAllData(It.Node()))
|
|
{
|
|
CompileData.Add(TrackData);
|
|
}
|
|
|
|
// Sort the compilation data based on (in order):
|
|
// 1. Group
|
|
// 2. Hierarchical bias
|
|
// 3. Evaluation priority
|
|
CompileData.Sort(SortPredicate);
|
|
|
|
// Generate the evaluation group by gathering initialization and evaluation ptrs for each unique group
|
|
FMovieSceneEvaluationGroup EvaluationGroup;
|
|
PopulateEvaluationGroup(CompileData, &EvaluationGroup);
|
|
|
|
// Compute meta data for this segment
|
|
TMovieSceneEvaluationTreeDataIterator<FMovieSceneSubSequenceTreeEntry> SubSequences = Hierarchy.GetTree().GetAllData(Hierarchy.GetTree().IterateFromLowerBound(FieldRange.GetLowerBound()).Node());
|
|
|
|
FMovieSceneEvaluationMetaData MetaData;
|
|
PopulateMetaData(Hierarchy, CompileData, SubSequences, &MetaData);
|
|
|
|
TrackTemplateField->Add(FieldRange, MoveTemp(EvaluationGroup), MoveTemp(MetaData));
|
|
}
|
|
}
|
|
|
|
|
|
void UMovieSceneCompiledDataManager::PopulateEvaluationGroup(const TArray<FCompileOnTheFlyData>& SortedCompileData, FMovieSceneEvaluationGroup* OutGroup)
|
|
{
|
|
check(OutGroup);
|
|
if (SortedCompileData.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static TArray<FMovieSceneFieldEntry_EvaluationTrack> InitTrackLUT;
|
|
static TArray<FMovieSceneFieldEntry_ChildTemplate> InitSectionLUT;
|
|
|
|
static TArray<FMovieSceneFieldEntry_EvaluationTrack> EvalTrackLUT;
|
|
static TArray<FMovieSceneFieldEntry_ChildTemplate> EvalSectionLUT;
|
|
|
|
InitTrackLUT.Reset();
|
|
InitSectionLUT.Reset();
|
|
EvalTrackLUT.Reset();
|
|
EvalSectionLUT.Reset();
|
|
|
|
// Now iterate the tracks and insert indices for initialization and evaluation
|
|
FName LastEvaluationGroup = SortedCompileData[0].EvaluationGroup;
|
|
|
|
int32 Index = 0;
|
|
while (Index < SortedCompileData.Num())
|
|
{
|
|
const FCompileOnTheFlyData& Data = SortedCompileData[Index];
|
|
|
|
// Check for different evaluation groups
|
|
if (Data.EvaluationGroup != LastEvaluationGroup)
|
|
{
|
|
// If we're now in a different flush group, add the ptrs to the group
|
|
AddPtrsToGroup(OutGroup, InitTrackLUT, InitSectionLUT, EvalTrackLUT, EvalSectionLUT);
|
|
}
|
|
LastEvaluationGroup = Data.EvaluationGroup;
|
|
|
|
// Add all subsequent entries that relate to the same track
|
|
FMovieSceneEvaluationFieldTrackPtr MatchTrack = Data.Track;
|
|
|
|
uint16 NumChildren = 0;
|
|
for ( ; Index < SortedCompileData.Num() && SortedCompileData[Index].Track == MatchTrack; ++Index)
|
|
{
|
|
if (SortedCompileData[Index].Child.ChildIndex != uint16(-1))
|
|
{
|
|
++NumChildren;
|
|
// If this track requires initialization, add it to the init array
|
|
if (Data.bRequiresInit)
|
|
{
|
|
InitSectionLUT.Add(SortedCompileData[Index].Child);
|
|
}
|
|
EvalSectionLUT.Add(SortedCompileData[Index].Child);
|
|
}
|
|
}
|
|
|
|
FMovieSceneFieldEntry_EvaluationTrack Entry{ Data.Track, NumChildren };
|
|
if (Data.bRequiresInit)
|
|
{
|
|
InitTrackLUT.Add(Entry);
|
|
}
|
|
EvalTrackLUT.Add(Entry);
|
|
}
|
|
|
|
AddPtrsToGroup(OutGroup, InitTrackLUT, InitSectionLUT, EvalTrackLUT, EvalSectionLUT);
|
|
}
|
|
|
|
|
|
void UMovieSceneCompiledDataManager::PopulateMetaData(const FMovieSceneSequenceHierarchy& RootHierarchy, const TArray<FCompileOnTheFlyData>& SortedCompileData, TMovieSceneEvaluationTreeDataIterator<FMovieSceneSubSequenceTreeEntry> SubSequences, FMovieSceneEvaluationMetaData* OutMetaData)
|
|
{
|
|
check(OutMetaData);
|
|
OutMetaData->Reset();
|
|
|
|
uint16 SetupIndex = 0;
|
|
uint16 TearDownIndex = 0;
|
|
for (const FCompileOnTheFlyData& CompileData : SortedCompileData)
|
|
{
|
|
if (CompileData.bRequiresInit)
|
|
{
|
|
uint32 ChildIndex = CompileData.Child.ChildIndex == uint16(-1) ? uint32(-1) : CompileData.Child.ChildIndex;
|
|
|
|
FMovieSceneEvaluationKey TrackKey(CompileData.Track.SequenceID, CompileData.Track.TrackIdentifier, ChildIndex);
|
|
OutMetaData->ActiveEntities.Add(FMovieSceneOrderedEvaluationKey{ TrackKey, SetupIndex++, (CompileData.bPriorityTearDown ? TearDownIndex : uint16(MAX_uint16-TearDownIndex)) });
|
|
++TearDownIndex;
|
|
}
|
|
}
|
|
|
|
// Then all the eval tracks
|
|
for (const FCompileOnTheFlyData& CompileData : SortedCompileData)
|
|
{
|
|
if (!CompileData.bRequiresInit)
|
|
{
|
|
uint32 ChildIndex = CompileData.Child.ChildIndex == uint16(-1) ? uint32(-1) : CompileData.Child.ChildIndex;
|
|
|
|
FMovieSceneEvaluationKey TrackKey(CompileData.Track.SequenceID, CompileData.Track.TrackIdentifier, ChildIndex);
|
|
OutMetaData->ActiveEntities.Add(FMovieSceneOrderedEvaluationKey{ TrackKey, SetupIndex++, (CompileData.bPriorityTearDown ? TearDownIndex : uint16(MAX_uint16-TearDownIndex)) });
|
|
++TearDownIndex;
|
|
}
|
|
}
|
|
|
|
Algo::SortBy(OutMetaData->ActiveEntities, &FMovieSceneOrderedEvaluationKey::Key);
|
|
|
|
{
|
|
OutMetaData->ActiveSequences.Reset();
|
|
OutMetaData->ActiveSequences.Add(MovieSceneSequenceID::Root);
|
|
|
|
for (const FMovieSceneSubSequenceTreeEntry& SubSequenceEntry : SubSequences)
|
|
{
|
|
OutMetaData->ActiveSequences.Add(SubSequenceEntry.SequenceID);
|
|
}
|
|
|
|
OutMetaData->ActiveSequences.Sort();
|
|
}
|
|
}
|
|
|
|
|
|
void UMovieSceneCompiledDataManager::CompileTrack(FMovieSceneCompiledDataEntry* OutEntry, const FMovieSceneBinding* ObjectBinding, UMovieSceneTrack* Track, const FTrackGatherParameters& Params, TSet<FGuid>* OutCompiledSignatures, FMovieSceneGatheredCompilerData* OutCompilerData)
|
|
{
|
|
using namespace UE::MovieScene;
|
|
|
|
check(Track);
|
|
check(OutCompiledSignatures);
|
|
|
|
const bool bTrackMatchesFlags = ( Params.Flags == ESectionEvaluationFlags::None )
|
|
|| ( EnumHasAnyFlags(Params.Flags, ESectionEvaluationFlags::PreRoll) && Track->EvalOptions.bEvaluateInPreroll )
|
|
|| ( EnumHasAnyFlags(Params.Flags, ESectionEvaluationFlags::PostRoll) && Track->EvalOptions.bEvaluateInPostroll );
|
|
|
|
if (!bTrackMatchesFlags)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Track->IsEvalDisabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
UMovieSceneSequence* Sequence = OutEntry->GetSequence();
|
|
check(Sequence);
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------------------
|
|
// Step 1 - ensure that track templates exist for any track that implements IMovieSceneTrackTemplateProducer
|
|
FMovieSceneTrackIdentifier TrackIdentifier;
|
|
FMovieSceneEvaluationTemplate* TrackTemplate = nullptr;
|
|
if (const IMovieSceneTrackTemplateProducer* TrackTemplateProducer = Cast<const IMovieSceneTrackTemplateProducer>(Track))
|
|
{
|
|
TrackTemplate = &TrackTemplates.FindOrAdd(OutEntry->DataID.Value);
|
|
|
|
check(TrackTemplate);
|
|
|
|
TrackIdentifier = TrackTemplate->GetLedger().FindTrackIdentifier(Track->GetSignature());
|
|
|
|
if (!TrackIdentifier)
|
|
{
|
|
// If the track doesn't exist - we need to generate it from scratch
|
|
FMovieSceneTrackCompilerArgs Args(Track, &Params.TemplateGenerator);
|
|
if (ObjectBinding)
|
|
{
|
|
Args.ObjectBindingId = ObjectBinding->GetObjectGuid();
|
|
}
|
|
|
|
Args.DefaultCompletionMode = Sequence->DefaultCompletionMode;
|
|
|
|
TrackTemplateProducer->GenerateTemplate(Args);
|
|
|
|
TrackIdentifier = TrackTemplate->GetLedger().FindTrackIdentifier(Track->GetSignature());
|
|
}
|
|
|
|
if (TrackIdentifier)
|
|
{
|
|
OutCompiledSignatures->Add(Track->GetSignature());
|
|
}
|
|
|
|
OutCompilerData->AccumulatedMask |= EMovieSceneSequenceCompilerMask::EvaluationTemplate;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------------------
|
|
// Step 2 - let the track or its sections add determinism fences
|
|
if (IMovieSceneDeterminismSource* DeterminismSource = Cast<IMovieSceneDeterminismSource>(Track))
|
|
{
|
|
DeterminismSource->PopulateDeterminismData(OutCompilerData->DeterminismData, TRange<FFrameNumber>::All());
|
|
}
|
|
|
|
const FMovieSceneTrackEvaluationField& EvaluationField = Track->GetEvaluationField();
|
|
const EMovieSceneCompletionMode DefaultCompletionMode = Sequence->DefaultCompletionMode;
|
|
const bool bAddKeepStateDeterminismFences = CVarAddKeepStateDeterminismFences.GetValueOnGameThread();
|
|
for (const FMovieSceneTrackEvaluationFieldEntry& Entry : EvaluationField.Entries)
|
|
{
|
|
if (bAddKeepStateDeterminismFences && Entry.Section)
|
|
{
|
|
// If a section is KeepState, we need to make sure to evaluate it on its last frame so that the value that "sticks" is correct.
|
|
const TRange<FFrameNumber> SectionRange = Entry.Section->GetRange();
|
|
const EMovieSceneCompletionMode SectionCompletionMode = Entry.Section->GetCompletionMode();
|
|
if (SectionRange.HasUpperBound() &&
|
|
(SectionCompletionMode == EMovieSceneCompletionMode::KeepState ||
|
|
(SectionCompletionMode == EMovieSceneCompletionMode::ProjectDefault && DefaultCompletionMode == EMovieSceneCompletionMode::KeepState)))
|
|
{
|
|
// We simply use the end time of the section for the fence, regardless of whether it's inclusive or exclusive.
|
|
// When exclusive, the ECS system will query entities just before that time, but still pass that time for
|
|
// evaluation purposes, so we will get the correct evaluated values.
|
|
const FFrameNumber FenceTime(SectionRange.GetUpperBoundValue());
|
|
OutCompilerData->DeterminismData.Fences.Add(FenceTime);
|
|
}
|
|
}
|
|
|
|
IMovieSceneDeterminismSource* DeterminismSource = Cast<IMovieSceneDeterminismSource>(Entry.Section);
|
|
if (DeterminismSource)
|
|
{
|
|
DeterminismSource->PopulateDeterminismData(OutCompilerData->DeterminismData, Entry.Range);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::GatherTrack(const FMovieSceneBinding* ObjectBinding, UMovieSceneTrack* Track, const FTrackGatherParameters& Params, const FMovieSceneEvaluationTemplate* TrackTemplate, FMovieSceneGatheredCompilerData* OutCompilerData) const
|
|
{
|
|
using namespace UE::MovieScene;
|
|
|
|
check(Track);
|
|
|
|
const bool bTrackMatchesFlags = ( Params.Flags == ESectionEvaluationFlags::None )
|
|
|| ( EnumHasAnyFlags(Params.Flags, ESectionEvaluationFlags::PreRoll) && Track->EvalOptions.bEvaluateInPreroll )
|
|
|| ( EnumHasAnyFlags(Params.Flags, ESectionEvaluationFlags::PostRoll) && Track->EvalOptions.bEvaluateInPostroll );
|
|
|
|
if (!bTrackMatchesFlags)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Track->IsEvalDisabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Some tracks could want to do some custom pre-compilation things.
|
|
FMovieSceneTrackPreCompileResult PreCompileResult;
|
|
Track->PreCompile(PreCompileResult);
|
|
|
|
const FMovieSceneTrackEvaluationField& EvaluationField = Track->GetEvaluationField();
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------------------
|
|
// Step 1 - Handle any entity producers that exist within the field
|
|
if (OutCompilerData->EntityField)
|
|
{
|
|
FMovieSceneEntityComponentFieldBuilder FieldBuilder(OutCompilerData->EntityField);
|
|
|
|
if (ObjectBinding)
|
|
{
|
|
FieldBuilder.GetSharedMetaData().ObjectBindingID = ObjectBinding->GetObjectGuid();
|
|
}
|
|
|
|
for (const FMovieSceneTrackEvaluationFieldEntry& Entry : EvaluationField.Entries)
|
|
{
|
|
if (Entry.Section && Track->IsRowEvalDisabled(Entry.Section->GetRowIndex()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IMovieSceneEntityProvider* EntityProvider = Cast<IMovieSceneEntityProvider>(Entry.Section);
|
|
if (!EntityProvider)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// This codepath should only ever execute for the highest level so we do not need to do any transformations
|
|
TRange<FFrameNumber> EffectiveRange = TRange<FFrameNumber>::Intersection(Params.LocalClampRange, Entry.Range);
|
|
if (!EffectiveRange.IsEmpty())
|
|
{
|
|
FMovieSceneEvaluationFieldEntityMetaData MetaData(PreCompileResult.DefaultMetaData);
|
|
|
|
MetaData.ForcedTime = Entry.ForcedTime;
|
|
MetaData.Flags = Entry.Flags;
|
|
MetaData.bEvaluateInSequencePreRoll = Track->EvalOptions.bEvaluateInPreroll;
|
|
MetaData.bEvaluateInSequencePostRoll = Track->EvalOptions.bEvaluateInPostroll;
|
|
|
|
if (!EntityProvider->PopulateEvaluationField(EffectiveRange, MetaData, &FieldBuilder))
|
|
{
|
|
const int32 EntityIndex = FieldBuilder.FindOrAddEntity(Entry.Section, 0);
|
|
const int32 MetaDataIndex = FieldBuilder.AddMetaData(MetaData);
|
|
|
|
FieldBuilder.AddPersistentEntity(EffectiveRange, EntityIndex, MetaDataIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------------------
|
|
// Step 2 - Handle the track being a template producer
|
|
FMovieSceneTrackIdentifier TrackIdentifier = TrackTemplate ? TrackTemplate->GetLedger().FindTrackIdentifier(Track->GetSignature()) : FMovieSceneTrackIdentifier();
|
|
if (TrackIdentifier)
|
|
{
|
|
// Iterate everything in the field
|
|
for (const FMovieSceneTrackEvaluationFieldEntry& Entry : EvaluationField.Entries)
|
|
{
|
|
FMovieSceneSequenceTransform SequenceToRootTransform = Params.RootToSequenceTransform.InverseFromWarp(Params.RootToSequenceWarpCounter);
|
|
TRange<FFrameNumber> ClampedRangeRoot = Params.ClampRoot(SequenceToRootTransform.TransformRangeUnwarped(Entry.Range));
|
|
UMovieSceneSection* Section = Entry.Section;
|
|
|
|
if (Section && Track->IsRowEvalDisabled(Section->GetRowIndex()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (ClampedRangeRoot.IsEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
check(TrackTemplate);
|
|
const FMovieSceneEvaluationTrack* EvaluationTrack = TrackTemplate->FindTrack(TrackIdentifier);
|
|
check(EvaluationTrack);
|
|
|
|
// Get the correct template for the sub sequence
|
|
FCompileOnTheFlyData CompileData;
|
|
|
|
CompileData.Track = FMovieSceneEvaluationFieldTrackPtr(Params.SequenceID, TrackIdentifier);
|
|
CompileData.EvaluationPriority = EvaluationTrack->GetEvaluationPriority();
|
|
CompileData.EvaluationGroup = EvaluationTrack->GetEvaluationGroup();
|
|
CompileData.GroupEvaluationPriority = GetMovieSceneModule().GetEvaluationGroupParameters(CompileData.EvaluationGroup).EvaluationPriority;
|
|
CompileData.HierarchicalBias = Params.HierarchicalBias;
|
|
CompileData.bPriorityTearDown = EvaluationTrack->HasTearDownPriority();
|
|
|
|
auto FindChildWithSection = [Section](const FMovieSceneEvalTemplatePtr& ChildTemplate)
|
|
{
|
|
return ChildTemplate.IsValid() && ChildTemplate->GetSourceSection() == Section;
|
|
};
|
|
|
|
const int32 ChildTemplateIndex = Section ? EvaluationTrack->GetChildTemplates().IndexOfByPredicate(FindChildWithSection) : INDEX_NONE;
|
|
if (ChildTemplateIndex != INDEX_NONE)
|
|
{
|
|
check(ChildTemplateIndex >= 0 && ChildTemplateIndex < TNumericLimits<uint16>::Max());
|
|
|
|
ESectionEvaluationFlags Flags = Params.Flags == ESectionEvaluationFlags::None ? Entry.Flags : Params.Flags;
|
|
|
|
CompileData.ChildPriority = Entry.LegacySortOrder;
|
|
CompileData.Child = FMovieSceneFieldEntry_ChildTemplate((uint16)ChildTemplateIndex, Flags, Entry.ForcedTime);
|
|
CompileData.bRequiresInit = EvaluationTrack->GetChildTemplate(ChildTemplateIndex).RequiresInitialization();
|
|
}
|
|
else
|
|
{
|
|
CompileData.ChildPriority = 0;
|
|
CompileData.Child = FMovieSceneFieldEntry_ChildTemplate{};
|
|
CompileData.bRequiresInit = false;
|
|
}
|
|
|
|
OutCompilerData->TrackTemplates.Add(ClampedRangeRoot, CompileData);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UMovieSceneCompiledDataManager::CompileHierarchy(UMovieSceneSequence* Sequence, FMovieSceneSequenceHierarchy* InOutHierarchy, EMovieSceneServerClientMask InNetworkMask)
|
|
{
|
|
FGatherParameters Params;
|
|
Params.NetworkMask = InNetworkMask;
|
|
return CompileHierarchy(Sequence, Params, InOutHierarchy);
|
|
}
|
|
|
|
bool UMovieSceneCompiledDataManager::CompileHierarchy(UMovieSceneSequence* Sequence, const FGatherParameters& Params, FMovieSceneSequenceHierarchy* InOutHierarchy)
|
|
{
|
|
UE::MovieScene::FSubSequencePath RootPath;
|
|
|
|
// Compile all the sub data for every part of the hierarchy
|
|
const bool bContainsSubSequences = GenerateSubSequenceData(Sequence, Params, FMovieSceneEvaluationOperand(), &RootPath, InOutHierarchy);
|
|
|
|
// Populate the sub sequence tree that defines which sub sequences happen at a given time
|
|
PopulateSubSequenceTree(Sequence, Params, &RootPath, InOutHierarchy);
|
|
|
|
return bContainsSubSequences;
|
|
}
|
|
|
|
bool UMovieSceneCompiledDataManager::GenerateSubSequenceData(UMovieSceneSequence* SubSequence, const FGatherParameters& Params, const FMovieSceneEvaluationOperand& Operand, UE::MovieScene::FSubSequencePath* RootPath, FMovieSceneSequenceHierarchy* InOutHierarchy)
|
|
{
|
|
UMovieScene* MovieScene = SubSequence ? SubSequence->GetMovieScene() : nullptr;
|
|
if (!MovieScene)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
check(RootPath && InOutHierarchy);
|
|
|
|
bool bContainsSubSequences = false;
|
|
|
|
for (UMovieSceneTrack* Track : MovieScene->GetMasterTracks())
|
|
{
|
|
if (UMovieSceneSubTrack* SubTrack = Cast<UMovieSceneSubTrack>(Track))
|
|
{
|
|
bContainsSubSequences |= GenerateSubSequenceData(SubTrack, Params, Operand, RootPath, InOutHierarchy);
|
|
}
|
|
}
|
|
|
|
for (const FMovieSceneBinding& ObjectBinding : MovieScene->GetBindings())
|
|
{
|
|
for (UMovieSceneTrack* Track : ObjectBinding.GetTracks())
|
|
{
|
|
if (UMovieSceneSubTrack* SubTrack = Cast<UMovieSceneSubTrack>(Track))
|
|
{
|
|
const FMovieSceneEvaluationOperand ChildOperand(Params.SequenceID, ObjectBinding.GetObjectGuid());
|
|
|
|
bContainsSubSequences |= GenerateSubSequenceData(SubTrack, Params, ChildOperand, RootPath, InOutHierarchy);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bContainsSubSequences;
|
|
}
|
|
|
|
bool UMovieSceneCompiledDataManager::GenerateSubSequenceData(UMovieSceneSubTrack* SubTrack, const FGatherParameters& Params, const FMovieSceneEvaluationOperand& Operand, UE::MovieScene::FSubSequencePath* RootPath, FMovieSceneSequenceHierarchy* InOutHierarchy)
|
|
{
|
|
bool bContainsSubSequences = false;
|
|
|
|
check(SubTrack && RootPath);
|
|
|
|
const FMovieSceneSequenceID ParentSequenceID = Params.SequenceID;
|
|
|
|
for (UMovieSceneSection* Section : SubTrack->GetAllSections())
|
|
{
|
|
if (SubTrack->IsRowEvalDisabled(Section->GetRowIndex()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UMovieSceneSubSection* SubSection = Cast<UMovieSceneSubSection>(Section);
|
|
if (!SubSection)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Note: we always compile FMovieSceneSubSequenceData for all entries of a hierarchy, even if excluded from the network mask
|
|
// to ensure that hierarchical information is still available when emulating different network masks
|
|
|
|
UMovieSceneSequence* SubSequence = SubSection->GetSequence();
|
|
if (!SubSequence)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UMovieScene* MovieScene = SubSequence->GetMovieScene();
|
|
if (!MovieScene)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FMovieSceneSequenceID InnerSequenceID = RootPath->ResolveChildSequenceID(SubSection->GetSequenceID());
|
|
|
|
FSubSequenceInstanceDataParams InstanceParams{ InnerSequenceID, Operand };
|
|
FMovieSceneSubSequenceData NewSubData = SubSection->GenerateSubSequenceData(InstanceParams);
|
|
|
|
// LocalClampRange here is in SubTrack's space, so we need to multiply that by the OuterToInnerTransform
|
|
// (which is the same as RootToSequenceTransform here before we transform it)
|
|
TRange<FFrameNumber> InnerClampRange = NewSubData.RootToSequenceTransform.TransformRangeUnwarped(Params.LocalClampRange);
|
|
|
|
NewSubData.PlayRange = TRange<FFrameNumber>::Intersection(InnerClampRange, NewSubData.PlayRange.Value);
|
|
NewSubData.RootToSequenceTransform = NewSubData.RootToSequenceTransform * Params.RootToSequenceTransform;
|
|
NewSubData.HierarchicalBias = Params.HierarchicalBias + NewSubData.HierarchicalBias;
|
|
NewSubData.bHasHierarchicalEasing = Params.bHasHierarchicalEasing || NewSubData.bHasHierarchicalEasing;
|
|
|
|
// Add the sub data to the root hierarchy
|
|
InOutHierarchy->Add(NewSubData, InnerSequenceID, ParentSequenceID);
|
|
|
|
// Iterate into the sub sequence
|
|
FGatherParameters SubParams = Params.CreateForSubData(NewSubData, InnerSequenceID);
|
|
|
|
// This is a bit of hack to make sure that LocalClampRange gets sent through to the next GenerateSubSequenceData call,
|
|
// but we do not set RootClampRange because it would be ambiguous to do so w.r.t looping sub sequences
|
|
SubParams.LocalClampRange = NewSubData.PlayRange.Value;
|
|
|
|
RootPath->PushGeneration(InnerSequenceID, NewSubData.DeterministicSequenceID);
|
|
GenerateSubSequenceData(SubSequence, SubParams, Operand, RootPath, InOutHierarchy);
|
|
RootPath->PopGenerations(1);
|
|
|
|
bContainsSubSequences = true;
|
|
}
|
|
|
|
return bContainsSubSequences;
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::PopulateSubSequenceTree(UMovieSceneSequence* SubSequence, const FGatherParameters& Params, UE::MovieScene::FSubSequencePath* RootPath, FMovieSceneSequenceHierarchy* InOutHierarchy)
|
|
{
|
|
UMovieScene* MovieScene = SubSequence ? SubSequence->GetMovieScene() : nullptr;
|
|
if (!MovieScene)
|
|
{
|
|
return;
|
|
}
|
|
|
|
check(RootPath && InOutHierarchy);
|
|
|
|
for (UMovieSceneTrack* Track : MovieScene->GetMasterTracks())
|
|
{
|
|
if (UMovieSceneSubTrack* SubTrack = Cast<UMovieSceneSubTrack>(Track))
|
|
{
|
|
PopulateSubSequenceTree(SubTrack, Params, RootPath, InOutHierarchy);
|
|
}
|
|
}
|
|
|
|
for (const FMovieSceneBinding& ObjectBinding : MovieScene->GetBindings())
|
|
{
|
|
for (UMovieSceneTrack* Track : ObjectBinding.GetTracks())
|
|
{
|
|
if (UMovieSceneSubTrack* SubTrack = Cast<UMovieSceneSubTrack>(Track))
|
|
{
|
|
PopulateSubSequenceTree(SubTrack, Params, RootPath, InOutHierarchy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMovieSceneCompiledDataManager::PopulateSubSequenceTree(UMovieSceneSubTrack* SubTrack, const FGatherParameters& Params, UE::MovieScene::FSubSequencePath* RootPath, FMovieSceneSequenceHierarchy* InOutHierarchy)
|
|
{
|
|
using namespace UE::MovieScene;
|
|
|
|
check(SubTrack && RootPath);
|
|
|
|
const bool bTrackMatchesFlags = ( Params.Flags == ESectionEvaluationFlags::None )
|
|
|| ( EnumHasAnyFlags(Params.Flags, ESectionEvaluationFlags::PreRoll) && SubTrack->EvalOptions.bEvaluateInPreroll )
|
|
|| ( EnumHasAnyFlags(Params.Flags, ESectionEvaluationFlags::PostRoll) && SubTrack->EvalOptions.bEvaluateInPostroll );
|
|
|
|
if (!bTrackMatchesFlags)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (SubTrack->IsEvalDisabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
UMovieSceneSequence* OuterSequence = SubTrack->GetTypedOuter<UMovieSceneSequence>();
|
|
if (!OuterSequence)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (const FMovieSceneTrackEvaluationFieldEntry& Entry : SubTrack->GetEvaluationField().Entries)
|
|
{
|
|
UMovieSceneSubSection* SubSection = Cast<UMovieSceneSubSection>(Entry.Section);
|
|
if (!SubSection || SubSection->GetSequence() == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (SubTrack->IsRowEvalDisabled(SubSection->GetRowIndex()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EMovieSceneServerClientMask NewMask = Params.NetworkMask & SubSection->GetNetworkMask();
|
|
if (NewMask == EMovieSceneServerClientMask::None)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FMovieSceneTimeTransform SequenceToRootTransform = Params.RootToSequenceTransform.InverseFromWarp(Params.RootToSequenceWarpCounter);
|
|
TRange<FFrameNumber> EffectiveRange = Params.ClampRoot(Entry.Range * SequenceToRootTransform);
|
|
if (EffectiveRange.IsEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FMovieSceneSequenceID SubSequenceID = RootPath->ResolveChildSequenceID(SubSection->GetSequenceID());
|
|
const FMovieSceneSubSequenceData* SubData = InOutHierarchy->FindSubData(SubSequenceID);
|
|
|
|
checkf(SubData, TEXT("Unable to locate sub-data for a sub section that appears in the track's evaluation field - this indicates that the section is being evaluated even though it is not active"));
|
|
|
|
const FMovieSceneFrameRange& SubSectionRange = SubSection->SectionRange;
|
|
const FMovieSceneSectionParameters& SubSectionParams = SubSection->Parameters;
|
|
|
|
const ESectionEvaluationFlags SubEntryFlags = Entry.Flags | Params.Flags;
|
|
|
|
if (!SubSectionParams.bCanLoop)
|
|
{
|
|
FGatherParameters SubParams = Params.CreateForSubData(*SubData, SubSequenceID, Params.RootToSequenceWarpCounter);
|
|
SubParams.SetClampRange(EffectiveRange);
|
|
SubParams.Flags |= Entry.Flags;
|
|
SubParams.NetworkMask = NewMask;
|
|
SubParams.RootToSequenceWarpCounter.AddNonWarpingLevel();
|
|
|
|
// The section isn't looping, so we can just add it to the tree.
|
|
InOutHierarchy->AddRange(EffectiveRange, SubSequenceID, SubEntryFlags, SubParams.RootToSequenceWarpCounter);
|
|
|
|
// Recurse into the sub sequence
|
|
RootPath->PushGeneration(SubSequenceID, SubData->DeterministicSequenceID);
|
|
{
|
|
PopulateSubSequenceTree(SubData->GetSequence(), SubParams, RootPath, InOutHierarchy);
|
|
}
|
|
RootPath->PopGenerations(1);
|
|
}
|
|
else
|
|
{
|
|
// The section is looping so we need to add its contents to the tree as many times as it has loops.
|
|
const float RootToSubSequenceTimeScale = SubData->RootToSequenceTransform.GetTimeScale();
|
|
const float SubSequenceToRootTimeScale = (RootToSubSequenceTimeScale != 0.f) ? 1.0f / RootToSubSequenceTimeScale : 1.f;
|
|
|
|
UMovieSceneSequence* SubSequence = SubData->GetSequence();
|
|
|
|
const TRange<FFrameNumber> SubSequencePlaybackRange = SubSequence->GetMovieScene()->GetPlaybackRange();
|
|
const FFrameNumber SubSequenceLength = DiscreteSize(SubSequencePlaybackRange);
|
|
const FFrameTime RootLoopLength = FMath::Max(
|
|
FFrameTime(SubSequenceLength - SubSectionParams.StartFrameOffset - SubSectionParams.EndFrameOffset) * SubSequenceToRootTimeScale,
|
|
FFrameTime(1));
|
|
const FFrameTime FirstRootLoopLength = FMath::Max(
|
|
RootLoopLength - SubSectionParams.FirstLoopStartFrameOffset * SubSequenceToRootTimeScale,
|
|
FFrameTime(1));
|
|
const TOptional<FFrameNumber> LocalSectionExclusiveEndTime = GetLoopingSubSectionEndTime(OuterSequence, SubSection, Params);
|
|
|
|
if (LocalSectionExclusiveEndTime.IsSet() && !SubSectionRange.GetLowerBound().IsOpen())
|
|
{
|
|
uint32 LoopCount = 0;
|
|
FFrameTime CurRootRangeStart = DiscreteInclusiveLower(SubSectionRange.GetLowerBound()) * SequenceToRootTransform;
|
|
TRange<FFrameNumber> CurRootRange(CurRootRangeStart.FloorToFrame(), (CurRootRangeStart + FirstRootLoopLength).CeilToFrame());
|
|
const FFrameNumber RootSectionEndTime = (LocalSectionExclusiveEndTime.GetValue() * SequenceToRootTransform).CeilToFrame();
|
|
|
|
while (CurRootRange.GetLowerBoundValue() < RootSectionEndTime)
|
|
{
|
|
if (CurRootRange.Overlaps(Params.RootClampRange))
|
|
{
|
|
// Clamp the sub-sequence's range by the containing section's range and the current compilation range.
|
|
const TRange<FFrameNumber> ClampedCurRootRange = TRange<FFrameNumber>::Intersection(CurRootRange, EffectiveRange);
|
|
|
|
FGatherParameters CurLoopParams = Params.CreateForSubData(*SubData, SubSequenceID, Params.RootToSequenceWarpCounter);
|
|
CurLoopParams.SetClampRange(ClampedCurRootRange);
|
|
CurLoopParams.Flags |= Entry.Flags;
|
|
CurLoopParams.NetworkMask = NewMask;
|
|
CurLoopParams.RootToSequenceWarpCounter.AddWarpingLevel(LoopCount);
|
|
|
|
// Add the section to the tree for the current loop.
|
|
InOutHierarchy->AddRange(ClampedCurRootRange, SubSequenceID, SubEntryFlags, CurLoopParams.RootToSequenceWarpCounter);
|
|
|
|
// Recurse into this loop's sub sequence.
|
|
RootPath->PushGeneration(SubSequenceID, SubData->DeterministicSequenceID);
|
|
{
|
|
PopulateSubSequenceTree(SubData->GetSequence(), CurLoopParams, RootPath, InOutHierarchy);
|
|
}
|
|
RootPath->PopGenerations(1);
|
|
}
|
|
|
|
// Move on to the next loop.
|
|
CurRootRangeStart = CurRootRange.GetUpperBoundValue();
|
|
CurRootRange = TRange<FFrameNumber>(CurRootRangeStart.FloorToFrame(), (CurRootRangeStart + RootLoopLength).FloorToFrame());
|
|
if (CurRootRange.GetUpperBoundValue() > RootSectionEndTime)
|
|
CurRootRange.SetUpperBoundValue(RootSectionEndTime);
|
|
++LoopCount;
|
|
}
|
|
}
|
|
// Faced with the cosmic horror of infinites, we choose to shield our sanity and skip this sub-section.
|
|
// (it either has an open-ended start time, which means we needed to loop since before time began, which means
|
|
// we don't know where loops are in the present... or it means the section and root sequence have open-ended
|
|
// end times, which means we would need to compile loops forever)
|
|
}
|
|
}
|
|
}
|
|
|
|
TOptional<FFrameNumber> UMovieSceneCompiledDataManager::GetLoopingSubSectionEndTime(const UMovieSceneSequence* InRootSequence, const UMovieSceneSubSection* SubSection, const FGatherParameters& Params)
|
|
{
|
|
using namespace UE::MovieScene;
|
|
|
|
TRangeBound<FFrameNumber> SectionRangeEnd = SubSection->SectionRange.GetUpperBound();
|
|
if (!SectionRangeEnd.IsOpen())
|
|
{
|
|
return DiscreteExclusiveUpper(SectionRangeEnd);
|
|
}
|
|
|
|
// This section is open ended... we don't want to compile its sub-sequence in an infinite loop so we'll bound
|
|
// that by the playback end of is own sequence.
|
|
if (const UMovieScene* MovieScene = InRootSequence->GetMovieScene())
|
|
{
|
|
const TRange<FFrameNumber> PlaybackRange = MovieScene->GetPlaybackRange();
|
|
if (!PlaybackRange.GetUpperBound().IsOpen())
|
|
{
|
|
return DiscreteExclusiveUpper(PlaybackRange.GetUpperBound());
|
|
}
|
|
}
|
|
|
|
// Sadly, the root sequence is also open ended, so we effectively would need to loop the sub-sequence
|
|
// indefinitely... we don't support that yet.
|
|
return TOptional<FFrameNumber>();
|
|
}
|