// Copyright Epic Games, Inc. All Rights Reserved. #include "MetasoundFrontendSearchEngine.h" #include "Algo/Transform.h" #include "HAL/IConsoleManager.h" #include "MetasoundFrontendArchetypeRegistry.h" #include "MetasoundFrontendQuery.h" #include "MetasoundFrontendQuerySteps.h" #include "MetasoundFrontendRegistryTransaction.h" static int32 ClearMetaSoundFrontendSearchEngineCacheCVar = 0; FAutoConsoleVariableRef CVarClearMetaSoundFrontendSearchEngineCache( TEXT("au.Debug.MetaSounds.SearchEngine.ClearCache"), ClearMetaSoundFrontendSearchEngineCacheCVar, TEXT("If true, flags the MetaSound registry SearchEngine cache to be cleared on subsequent query.\n") TEXT("0: Do not clear, !0: Flag to clear"), ECVF_Default); namespace Metasound { namespace Frontend { namespace SearchEngineQuerySteps { class FArchetypeRegistryTransactionSource : public IFrontendQuerySource { public: FArchetypeRegistryTransactionSource() : CurrentTransactionID(GetOriginRegistryTransactionID()) { } void Stream(TArray& OutEntries) override { auto AddEntry = [&OutEntries](const FArchetypeRegistryTransaction& InTransaction) { OutEntries.Emplace(FFrontendQueryEntry::FValue(TInPlaceType(), InTransaction)); }; IArchetypeRegistry::Get().ForEachRegistryTransactionSince(CurrentTransactionID, &CurrentTransactionID, AddEntry); } void Reset() override { CurrentTransactionID = GetOriginRegistryTransactionID(); } private: FRegistryTransactionID CurrentTransactionID; }; class FMapArchetypeRegistryTransactionsToArchetypeRegistryKeys : public IFrontendQueryMapStep { public: FFrontendQueryEntry::FKey Map(const FFrontendQueryEntry& InEntry) const override { FArchetypeRegistryKey RegistryKey; if (ensure(InEntry.Value.IsType())) { RegistryKey = InEntry.Value.Get().GetArchetypeRegistryKey(); } return FFrontendQueryEntry::FKey{RegistryKey}; } }; class FReduceArchetypeRegistryTransactionsToCurrentStatus : public IFrontendQueryReduceStep { public: void Reduce(FFrontendQueryEntry::FKey InKey, TArrayView& InEntries, FReduceOutputView& OutResult) const override { int32 State = 0; FFrontendQueryEntry* FinalEntry = nullptr; for (FFrontendQueryEntry* Entry : InEntries) { if (ensure(Entry->Value.IsType())) { const FArchetypeRegistryTransaction& Transaction = Entry->Value.Get(); switch (Transaction.GetTransactionType()) { case FArchetypeRegistryTransaction::ETransactionType::ArchetypeRegistration: State++; FinalEntry = Entry; break; case FArchetypeRegistryTransaction::ETransactionType::ArchetypeUnregistration: State--; break; default: break; } } } if ((nullptr != FinalEntry) && (State > 0)) { OutResult.Add(*FinalEntry); } } }; class FTransformArchetypeRegistryTransactionToArchetype : public IFrontendQueryTransformStep { public: virtual void Transform(FFrontendQueryEntry::FValue& InValue) const override { FMetasoundFrontendArchetype Archetype; if (ensure(InValue.IsType())) { FArchetypeRegistryKey RegistryKey = InValue.Get().GetArchetypeRegistryKey(); IArchetypeRegistry::Get().FindArchetype(RegistryKey, Archetype); } InValue.Set(MoveTemp(Archetype)); } }; class FMapArchetypeToArchetypeNameAndMajorVersion : public IFrontendQueryMapStep { public: FFrontendQueryEntry::FKey Map(const FFrontendQueryEntry& InEntry) const override { FString Key; if (ensure(InEntry.Value.IsType())) { const FMetasoundFrontendArchetype& Arch = InEntry.Value.Get(); Key = FString::Format(TEXT("{0}_{1}"), { Arch.Version.Name.ToString(), Arch.Version.Number.Major }); } return FFrontendQueryEntry::FKey{Key}; } }; class FReduceArchetypesToHighestVersion : public IFrontendQueryReduceStep { public: void Reduce(FFrontendQueryEntry::FKey InKey, TArrayView& InEntries, FReduceOutputView& OutResult) const override { FFrontendQueryEntry* HighestVersionEntry = nullptr; FMetasoundFrontendVersionNumber HighestVersion = FMetasoundFrontendVersionNumber::GetInvalid(); for (FFrontendQueryEntry* Entry : InEntries) { if (ensure(Entry->Value.IsType())) { const FMetasoundFrontendVersionNumber& VersionNumber = Entry->Value.Get().Version.Number; if (VersionNumber > HighestVersion) { HighestVersionEntry = Entry; HighestVersion = VersionNumber; } } } if (HighestVersionEntry) { OutResult.Add(*HighestVersionEntry); } } }; class FFilterArchetypeRegistryTransactionsByName : public IFrontendQueryFilterStep { public: FFilterArchetypeRegistryTransactionsByName(const FName& InName) : Name(InName) { } bool Filter(const FFrontendQueryEntry& InEntry) const override { if (ensure(InEntry.Value.IsType())) { return Name == InEntry.Value.Get().GetArchetypeVersion().Name; } return false; } private: FName Name; }; class FFilterArchetypeRegistryTransactionsByNameAndMajorVersion : public IFrontendQueryFilterStep { public: FFilterArchetypeRegistryTransactionsByNameAndMajorVersion(const FName& InName, int32 InMajorVersion) : Name(InName) , MajorVersion(InMajorVersion) { } bool Filter(const FFrontendQueryEntry& InEntry) const override { if (ensure(InEntry.Value.IsType())) { const FMetasoundFrontendVersion& Version = InEntry.Value.Get().GetArchetypeVersion(); return (Name == Version.Name) && (MajorVersion == Version.Number.Major); } return false; } private: FName Name; int32 MajorVersion; }; class FMapArchetypeToArchetypeName : public IFrontendQueryMapStep { public: FFrontendQueryEntry::FKey Map(const FFrontendQueryEntry& InEntry) const override { FString Key; if (ensure(InEntry.Value.IsType())) { Key = InEntry.Value.Get().Version.Name.ToString(); } return FFrontendQueryEntry::FKey{Key}; } }; class FFilterRegistryTransactionsByName : public IFrontendQueryFilterStep { public: FFilterRegistryTransactionsByName(const FName& InName) : Name(InName) { } bool Filter(const FFrontendQueryEntry& InEntry) const override { if (ensure(InEntry.Value.IsType())) { return Name == InEntry.Value.Get().GetArchetypeVersion().Name; } return false; } private: FName Name; }; class FTransformArchetypeRegistryTransactionToArchetypeVersion : public IFrontendQueryTransformStep { public: virtual void Transform(FFrontendQueryEntry::FValue& InValue) const override { FMetasoundFrontendVersion Version; if (ensure(InValue.IsType())) { Version = InValue.Get().GetArchetypeVersion(); } InValue.Set(Version); } }; } class FSearchEngine : public ISearchEngine { FSearchEngine(const FSearchEngine&) = delete; FSearchEngine(FSearchEngine&&) = delete; FSearchEngine& operator=(const FSearchEngine&) = delete; FSearchEngine& operator=(FSearchEngine&&) = delete; public: FSearchEngine() = default; virtual ~FSearchEngine() = default; TArray FindAllClasses(bool bInIncludeDeprecated) override; TArray FindClassesWithName(const FNodeClassName& InName, bool bInSortByVersion) override; bool FindClassWithHighestVersion(const FNodeClassName& InName, FMetasoundFrontendClass& OutClass) override; bool FindClassWithMajorVersion(const FNodeClassName& InName, int32 InMajorVersion, FMetasoundFrontendClass& OutClass) override; virtual TArray FindAllArchetypes(bool bInIncludeDeprecated) override; virtual TArray FindAllRegisteredArchetypesWithName(const FName& InArchetypeName) override; virtual bool FindArchetypeWithHighestVersion(const FName& InArchetypeName, FMetasoundFrontendArchetype& OutArchetype) override; virtual bool FindArchetypeWithMajorVersion(const FName& InArchetypeName, int32 InMajorVersion, FMetasoundFrontendArchetype& OutArchetype) override; private: FFrontendQuery* FindQuery(const FString& InQueryName); FFrontendQuery* AddQuery(const FString& InQueryName, TUniquePtr&& InQuery); // Store of queries indexed by unique strings. TMap> QueryCache; }; ISearchEngine& ISearchEngine::Get() { static FSearchEngine SearchEngine; return SearchEngine; } TArray FSearchEngine::FindAllClasses(bool bInIncludeDeprecated) { FString QueryName = FString(TEXT("FindAllClasses")); if (bInIncludeDeprecated) { QueryName += TEXT("IncludingDeprecated"); } FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); // TODO: this query will be slow to update when new nodes are registered. // Consider reworking call sites, or creating a reduce function // which performs the majority of the query logic. NewQuery->AddStep() .AddStep() .AddStep() .AddStep(); if (!bInIncludeDeprecated) { NewQuery->AddStep() .AddStep(); } Query = AddQuery(QueryName, MoveTemp(NewQuery)); } TArray Result; if (ensure(Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); for (const FFrontendQueryEntry* Entry : Selection.GetSelection()) { check(Entry->Value.IsType()); Result.Add(Entry->Value.Get()); } } return Result; } TArray FSearchEngine::FindClassesWithName(const FNodeClassName& InName, bool bInSortByVersion) { FString QueryName = FString(TEXT("FindClassesWithName")); if (bInSortByVersion) { QueryName += TEXT("SortedByVersion"); } QueryName += InName.GetFullName().ToString(); FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); NewQuery->AddStep() .AddStep() .AddStep() .AddStep() .AddStep(InName); if (bInSortByVersion) { NewQuery->AddStep(); } Query = AddQuery(QueryName, MoveTemp(NewQuery)); } TArray Result; if (ensure(nullptr != Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); for (const FFrontendQueryEntry* Entry : Selection.GetSelection()) { check(Entry->Value.IsType()); Result.Add(Entry->Value.Get()); } } return Result; } bool FSearchEngine::FindClassWithHighestVersion(const FNodeClassName& InName, FMetasoundFrontendClass& OutClass) { const FString QueryName = FString(TEXT("FindHighestVersion")) + InName.GetFullName().ToString(); FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); // TODO: Create index of class names and major version to avoid duplicating // this query for each class name. NewQuery->AddStep() .AddStep() .AddStep() .AddStep() .AddStep(InName) .AddStep() .AddStep(); Query = AddQuery(QueryName, MoveTemp(NewQuery)); } if (ensure(Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); if (Entries.IsEmpty()) { return false; } if (ensure(Entries.Num() == 1)) { OutClass = Entries[0]->Value.Get(); return true; } } return false; } bool FSearchEngine::FindClassWithMajorVersion(const FNodeClassName& InName, int32 InMajorVersion, FMetasoundFrontendClass& OutClass) { const FString QueryName = FString(TEXT("FindClassWithMajorVersion")) + InName.GetFullName().ToString() + FString::FromInt(InMajorVersion); FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); // TODO: Create index of class names to avoid duplicating // this query for each class name. NewQuery->AddStep() .AddStep() .AddStep() .AddStep() .AddStep(InName) .AddStep() .AddStep(InMajorVersion); Query = AddQuery(QueryName, MoveTemp(NewQuery)); } if (ensure(Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); if (Entries.IsEmpty()) { return false; } if (ensure(Entries.Num() == 1)) { OutClass = Entries[0]->Value.Get(); return true; } } return false; } TArray FSearchEngine::FindAllArchetypes(bool bInIncludeDeprecated) { using namespace SearchEngineQuerySteps; const FString QueryName = FString::Format(TEXT("FindAllArchetypes_IncludeDeprecated:{0}"), { bInIncludeDeprecated }); FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); NewQuery->AddStep() .AddStep() .AddStep() .AddStep(); if (!bInIncludeDeprecated) { NewQuery->AddStep() .AddStep(); } Query = AddQuery(QueryName, MoveTemp(NewQuery)); } TArray Result; if (ensure(Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); for (const FFrontendQueryEntry* Entry : Selection.GetSelection()) { if (ensure(Entry->Value.IsType())) { Result.Add(Entry->Value.Get()); } } } return Result; } TArray FSearchEngine::FindAllRegisteredArchetypesWithName(const FName& InArchetypeName) { using namespace SearchEngineQuerySteps; const FString QueryName = FString::Format(TEXT("FindAllRegisteredArchetypesWithName_Name:{0}"), { InArchetypeName.ToString() }); FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); NewQuery->AddStep() .AddStep(InArchetypeName) .AddStep() .AddStep() .AddStep(); Query = AddQuery(QueryName, MoveTemp(NewQuery)); } TArray Result; if (ensure(Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); for (const FFrontendQueryEntry* Entry : Selection.GetSelection()) { if (ensure(Entry->Value.IsType())) { Result.Add(Entry->Value.Get()); } } } return Result; } bool FSearchEngine::FindArchetypeWithHighestVersion(const FName& InArchetypeName, FMetasoundFrontendArchetype& OutArchetype) { using namespace SearchEngineQuerySteps; const FString QueryName = FString::Format(TEXT("FindArchetypeWithHighestVersion_Name:{0}"), { InArchetypeName.ToString() }); FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); NewQuery->AddStep() .AddStep(InArchetypeName) .AddStep() .AddStep() .AddStep() .AddStep() .AddStep(); Query = AddQuery(QueryName, MoveTemp(NewQuery)); } if (ensure(Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); if (Entries.IsEmpty()) { return false; } if (ensure(Entries.Num() == 1)) { OutArchetype = Entries[0]->Value.Get(); return true; } } return false; } bool FSearchEngine::FindArchetypeWithMajorVersion(const FName& InArchetypeName, int32 InMajorVersion, FMetasoundFrontendArchetype& OutArchetype) { using namespace SearchEngineQuerySteps; const FString QueryName = FString::Format(TEXT("FindArchetypeWithMajorVersion_Name:{0}_MajorVersion:{1}"), { InArchetypeName.ToString(), InMajorVersion }); FFrontendQuery* Query = FindQuery(QueryName); if (!Query) { TUniquePtr NewQuery = MakeUnique(); NewQuery->AddStep() .AddStep(InArchetypeName, InMajorVersion) .AddStep() .AddStep() .AddStep() .AddStep() .AddStep(); Query = AddQuery(QueryName, MoveTemp(NewQuery)); } if (ensure(Query)) { FFrontendQuerySelectionView Selection = Query->Execute(); TArrayView Entries = Selection.GetSelection(); if (Entries.IsEmpty()) { return false; } if (ensure(Entries.Num() == 1)) { OutArchetype = Entries[0]->Value.Get(); return true; } } return false; } FFrontendQuery* FSearchEngine::FindQuery(const FString& InQueryName) { if (ClearMetaSoundFrontendSearchEngineCacheCVar) { ClearMetaSoundFrontendSearchEngineCacheCVar = 0; QueryCache.Reset(); } if (TUniquePtr* Query = QueryCache.Find(InQueryName)) { return Query->Get(); } return nullptr; } FFrontendQuery* FSearchEngine::AddQuery(const FString& InQueryName, TUniquePtr&& InQuery) { return QueryCache.Add(InQueryName, MoveTemp(InQuery)).Get(); } } }