// Copyright Epic Games, Inc. All Rights Reserved. #include "Compilation/MovieSceneCompiledVolatilityManager.h" #include "Compilation/MovieSceneCompiledDataManager.h" #include "EntitySystem/MovieSceneSharedPlaybackState.h" #include "MovieSceneFwd.h" namespace UE { namespace MovieScene { #if WITH_EDITOR int32 GVolatileSequencesInEditor = 1; FAutoConsoleVariableRef CVarVolatileSequencesInEditor( TEXT("Sequencer.VolatileSequencesInEditor"), GVolatileSequencesInEditor, TEXT("(Default: 1) When non-zero, all assets will be treated as volatile in editor. Can be disabled to bypass volatility checks in-editor for more representative runtime performance metrics.\n"), ECVF_Default ); #endif FORCEINLINE EMovieSceneSequenceFlags GetEditorVolatilityFlags() { #if WITH_EDITOR return GVolatileSequencesInEditor ? EMovieSceneSequenceFlags::Volatile : EMovieSceneSequenceFlags::None; #else return EMovieSceneSequenceFlags::None; #endif } TUniquePtr FCompiledDataVolatilityManager::Construct(TSharedRef SharedPlaybackState) { FMovieSceneCompiledDataID RootDataID = SharedPlaybackState->GetRootCompiledDataID(); UMovieSceneCompiledDataManager* CompiledDataManager = SharedPlaybackState->GetCompiledDataManager(); const FMovieSceneCompiledDataEntry& Entry = CompiledDataManager->GetEntryRef(RootDataID); EMovieSceneSequenceFlags SequenceFlags = Entry.AccumulatedFlags | GetEditorVolatilityFlags(); if (!EnumHasAnyFlags(SequenceFlags, EMovieSceneSequenceFlags::Volatile)) { // If the entry has a valid compiled signature, it is assumed to be non-volatile if it does // not explicitly have the volatile flag. Otherwise we assume this sequence was added to the // manager but never compiled, and as such must be volatile since it needs compiling. if (Entry.CompiledSignature.IsValid()) { return nullptr; } } TUniquePtr VolatilityManager = MakeUnique(SharedPlaybackState); VolatilityManager->ConditionalRecompile(); return VolatilityManager; } FCompiledDataVolatilityManager::FCompiledDataVolatilityManager(TSharedRef SharedPlaybackState) : WeakSharedPlaybackState(SharedPlaybackState) { } bool FCompiledDataVolatilityManager::HasBeenRecompiled() const { TSharedPtr SharedPlaybackState = WeakSharedPlaybackState.Pin(); FMovieSceneCompiledDataID RootDataID = SharedPlaybackState->GetRootCompiledDataID(); UMovieSceneCompiledDataManager* CompiledDataManager = SharedPlaybackState->GetCompiledDataManager(); if (HasSequenceBeenRecompiled(RootDataID, MovieSceneSequenceID::Root)) { return true; } if (const FMovieSceneSequenceHierarchy* Hierarchy = CompiledDataManager->FindHierarchy(RootDataID)) { for (const TTuple& Pair : Hierarchy->AllSubSequenceData()) { FMovieSceneCompiledDataID SubDataID = CompiledDataManager->GetSubDataID(RootDataID, Pair.Key); if (SubDataID.IsValid() && HasSequenceBeenRecompiled(SubDataID, Pair.Key)) { return true; } } } return false; } bool FCompiledDataVolatilityManager::HasSequenceBeenRecompiled(FMovieSceneCompiledDataID DataID, FMovieSceneSequenceID SequenceID) const { const FGuid* CachedSignature = CachedCompilationSignatures.Find(SequenceID); TSharedPtr SharedPlaybackState = WeakSharedPlaybackState.Pin(); UMovieSceneCompiledDataManager* CompiledDataManager = SharedPlaybackState->GetCompiledDataManager(); const FMovieSceneCompiledDataEntry& CompiledEntry = CompiledDataManager->GetEntryRef(DataID); return CachedSignature == nullptr || *CachedSignature != CompiledEntry.CompiledSignature; } bool FCompiledDataVolatilityManager::ConditionalRecompile() { bool bRecompiled = false; TSharedPtr SharedPlaybackState = WeakSharedPlaybackState.Pin(); FMovieSceneCompiledDataID RootDataID = SharedPlaybackState->GetRootCompiledDataID(); UMovieSceneCompiledDataManager* CompiledDataManager = SharedPlaybackState->GetCompiledDataManager(); if (CompiledDataManager->IsDirty(RootDataID)) { // We override the network mask from the compiled data manager here simply because it may not be correct. // In a non-editor/PIE executable, we have a single global compiled data manager, and in its current 'global static' implementation, // it may not have been created at a time with enough context to determine the net mode. // In certain edge cases, such as networked games where both server and client are compiled using a single target executable of TargetType.Game, // we may not have the context at Sequence cook/compile time to determine which subsections may be included/excluded at runtime based on NetworkMask, // and so these Sequences are marked as volatile on compile time. Therefore, it's here, upon conditional recompile, when we need to know the correct // network mask to use, which we override and apply here. EMovieSceneServerClientMask NetworkMask = CompiledDataManager->GetNetworkMask(); UObject* PlaybackContext = SharedPlaybackState->GetPlaybackContext(); UWorld* World = PlaybackContext ? PlaybackContext->GetWorld() : nullptr; if (World) { ENetMode NetMode = World->GetNetMode(); if (NetMode == ENetMode::NM_DedicatedServer) { NetworkMask = EMovieSceneServerClientMask::Server; } else if (NetMode == ENetMode::NM_Client) { NetworkMask = EMovieSceneServerClientMask::Client; } } CompiledDataManager->Compile(RootDataID, NetworkMask); bRecompiled = true; } else { bRecompiled = HasBeenRecompiled(); } if (bRecompiled) { UpdateCachedSignatures(); } return bRecompiled; } void FCompiledDataVolatilityManager::UpdateCachedSignatures() { CachedCompilationSignatures.Reset(); TSharedPtr SharedPlaybackState = WeakSharedPlaybackState.Pin(); FMovieSceneCompiledDataID RootDataID = SharedPlaybackState->GetRootCompiledDataID(); UMovieSceneCompiledDataManager* CompiledDataManager = SharedPlaybackState->GetCompiledDataManager(); FMovieSceneEvaluationState* State = SharedPlaybackState->FindCapability(); { const FMovieSceneCompiledDataEntry& RootEntry = CompiledDataManager->GetEntryRef(RootDataID); CachedCompilationSignatures.Add(MovieSceneSequenceID::Root, RootEntry.CompiledSignature); UMovieSceneSequence* RootSequence = RootEntry.GetSequence(); if (RootSequence && State) { State->AssignSequence(MovieSceneSequenceID::Root, *RootSequence, SharedPlaybackState.ToSharedRef()); } } if (const FMovieSceneSequenceHierarchy* Hierarchy = CompiledDataManager->FindHierarchy(RootDataID)) { for (const TTuple& SubData : Hierarchy->AllSubSequenceData()) { const FMovieSceneCompiledDataID SubDataID = CompiledDataManager->GetSubDataID(RootDataID, SubData.Key); if (!SubDataID.IsValid()) { UE_LOG(LogMovieScene, Error, TEXT("Invalid SubDataID for: %s"), *SubData.Value.Sequence.ToString()); continue; } const FMovieSceneCompiledDataEntry& SubEntry = CompiledDataManager->GetEntryRef(SubDataID); CachedCompilationSignatures.Add(SubData.Key, SubEntry.CompiledSignature); UMovieSceneSequence* Sequence = SubData.Value.GetSequence(); if (Sequence && State) { State->AssignSequence(SubData.Key, *Sequence, SharedPlaybackState.ToSharedRef()); } } } } } // namespace MovieScene } // namespace UE