2024-05-08 14:53:53 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundDocumentBuilderRegistry.h"
2024-06-14 13:05:35 -04:00
# include "MetasoundAssetManager.h"
2024-06-17 12:51:18 -04:00
# include "MetasoundGlobals.h"
2024-06-18 16:47:21 -04:00
# include "MetasoundSettings.h"
2024-05-08 14:53:53 -04:00
# include "MetasoundTrace.h"
# include "MetasoundUObjectRegistry.h"
namespace Metasound : : Engine
{
FDocumentBuilderRegistry : : ~ FDocumentBuilderRegistry ( )
{
2024-06-03 14:44:36 -04:00
TMultiMap < FMetasoundFrontendClassName , TWeakObjectPtr < UMetaSoundBuilderBase > > BuildersToFinish ;
2024-05-29 15:33:29 -04:00
FScopeLock Lock ( & BuildersCriticalSection ) ;
2024-06-03 14:44:36 -04:00
{
BuildersToFinish = MoveTemp ( Builders ) ;
Builders . Reset ( ) ;
}
UE_CLOG ( ! BuildersToFinish . IsEmpty ( ) , LogMetaSound , Display , TEXT ( " BuilderRegistry is shutting down with the following %i active builder entries. Forcefully shutting down: " ) , BuildersToFinish . Num ( ) ) ;
2024-05-29 15:33:29 -04:00
int32 NumStale = 0 ;
2024-06-03 14:44:36 -04:00
for ( const TPair < FMetasoundFrontendClassName , TWeakObjectPtr < UMetaSoundBuilderBase > > & Pair : BuildersToFinish )
2024-05-08 14:53:53 -04:00
{
2024-05-29 15:33:29 -04:00
const bool bIsValid = Pair . Value . IsValid ( ) ;
if ( bIsValid )
2024-05-14 20:20:01 -04:00
{
2024-05-29 15:33:29 -04:00
UE_CLOG ( bIsValid , LogMetaSound , Display , TEXT ( " - %s " ) , * Pair . Value - > GetFullName ( ) ) ;
2024-05-14 20:20:01 -04:00
constexpr bool bForceUnregister = true ;
2024-06-05 11:00:21 -04:00
FinishBuildingInternal ( * Pair . Value . Get ( ) , bForceUnregister ) ;
2024-05-29 15:33:29 -04:00
}
else
{
+ + NumStale ;
2024-05-14 20:20:01 -04:00
}
2024-05-08 14:53:53 -04:00
}
2024-05-29 15:33:29 -04:00
UE_CLOG ( NumStale > 0 , LogMetaSound , Display , TEXT ( " BuilderRegistry is shutting down with %i stale entries " ) , NumStale ) ;
2024-06-03 14:44:36 -04:00
}
void FDocumentBuilderRegistry : : AddBuilderInternal ( const FMetasoundFrontendClassName & InClassName , UMetaSoundBuilderBase * NewBuilder ) const
{
FScopeLock Lock ( & BuildersCriticalSection ) ;
2024-06-05 12:06:07 -04:00
// #if !NO_LOGGING
2024-06-10 13:52:48 -04:00
// bool bLogDuplicateEntries = CanPostEventLog(ELogEvent::DuplicateEntries, ELogVerbosity::Error);
// if (bLogDuplicateEntries)
// {
// bLogDuplicateEntries = Builders.Contains(InClassName);
// }
2024-06-05 12:06:07 -04:00
// #endif // !NO_LOGGING
2024-06-03 14:44:36 -04:00
Builders . Add ( InClassName , NewBuilder ) ;
2024-06-05 12:06:07 -04:00
// #if !NO_LOGGING
2024-06-10 13:52:48 -04:00
// if (bLogDuplicateEntries)
2024-06-05 12:06:07 -04:00
// {
// TArray<TWeakObjectPtr<UMetaSoundBuilderBase>> Entries;
// Builders.MultiFind(InClassName, Entries);
2024-06-10 13:52:48 -04:00
//
// // Don't print stale entries as during cook and some editor asset actions,
// // these may be removed after a new valid builder is created. If stale
// // entries leak, they will show up on registry logging upon destruction.
// Entries.RemoveAllSwap([](const TWeakObjectPtr<UMetaSoundBuilderBase>& Builder) { return !Builder.IsValid(); });
//
// if (!Entries.IsEmpty())
2024-06-05 12:06:07 -04:00
// {
2024-06-10 13:52:48 -04:00
// UE_LOG(LogMetaSound, Error, TEXT("More than one asset registered with class name '%s'. "
// "Look-up may return builder that is not associated with desired object! \n"
// "This can happen if asset was moved using revision control and original location was revived. \n"
// "Remove all but one of the following assets and relink a duplicate or copied replacement asset:"),
// *InClassName.ToString());
// for (const TWeakObjectPtr<UMetaSoundBuilderBase>& BuilderPtr : Entries)
2024-06-05 12:06:07 -04:00
// {
// UE_LOG(LogMetaSound, Error, TEXT("- %s"), *BuilderPtr->GetConstBuilder().CastDocumentObjectChecked<UObject>().GetPathName());
// }
// }
// }
// #endif // !NO_LOGGING
2024-05-08 14:53:53 -04:00
}
2024-06-10 13:52:48 -04:00
bool FDocumentBuilderRegistry : : CanPostEventLog ( ELogEvent Event , ELogVerbosity : : Type Verbosity ) const
{
# if NO_LOGGING
return false ;
# else // !NO_LOGGING
if ( const ELogVerbosity : : Type * SetVerbosity = EventLogVerbosity . Find ( Event ) )
{
return * SetVerbosity > = Verbosity ;
}
return true ;
# endif // !NO_LOGGING
}
2024-05-08 14:53:53 -04:00
# if WITH_EDITORONLY_DATA
FMetaSoundFrontendDocumentBuilder & FDocumentBuilderRegistry : : FindOrBeginBuilding ( TScriptInterface < IMetaSoundDocumentInterface > MetaSound )
{
UObject * Object = MetaSound . GetObject ( ) ;
check ( Object ) ;
return FindOrBeginBuilding ( * Object ) . GetBuilder ( ) ;
}
# endif // WITH_EDITORONLY_DATA
FMetaSoundFrontendDocumentBuilder * FDocumentBuilderRegistry : : FindBuilder ( TScriptInterface < IMetaSoundDocumentInterface > MetaSound ) const
{
2024-05-29 15:33:29 -04:00
if ( UMetaSoundBuilderBase * Builder = FindBuilderObject ( MetaSound ) )
2024-05-08 14:53:53 -04:00
{
2024-05-29 15:33:29 -04:00
return & Builder - > GetBuilder ( ) ;
2024-05-08 14:53:53 -04:00
}
return nullptr ;
}
2024-06-05 11:00:21 -04:00
FMetaSoundFrontendDocumentBuilder * FDocumentBuilderRegistry : : FindBuilder ( const FMetasoundFrontendClassName & InClassName , const FTopLevelAssetPath & AssetPath ) const
2024-05-08 14:53:53 -04:00
{
2024-06-05 11:00:21 -04:00
if ( UMetaSoundBuilderBase * Builder = FindBuilderObject ( InClassName , AssetPath ) )
2024-05-08 14:53:53 -04:00
{
return & Builder - > GetBuilder ( ) ;
}
return nullptr ;
}
UMetaSoundBuilderBase * FDocumentBuilderRegistry : : FindBuilderObject ( TScriptInterface < const IMetaSoundDocumentInterface > MetaSound ) const
{
2024-06-05 11:00:21 -04:00
UMetaSoundBuilderBase * FoundEntry = nullptr ;
2024-05-08 14:53:53 -04:00
if ( const UObject * MetaSoundObject = MetaSound . GetObject ( ) )
{
const FMetasoundFrontendDocument & Document = MetaSound - > GetConstDocument ( ) ;
const FMetasoundFrontendClassName & ClassName = Document . RootGraph . Metadata . GetClassName ( ) ;
2024-05-29 15:33:29 -04:00
TArray < TWeakObjectPtr < UMetaSoundBuilderBase > > Entries ;
{
FScopeLock Lock ( & BuildersCriticalSection ) ;
Builders . MultiFind ( ClassName , Entries ) ;
}
2024-06-05 11:00:21 -04:00
for ( const TWeakObjectPtr < UMetaSoundBuilderBase > & BuilderPtr : Entries )
2024-05-29 15:33:29 -04:00
{
2024-06-05 11:00:21 -04:00
if ( UMetaSoundBuilderBase * Builder = BuilderPtr . Get ( ) )
2024-05-29 15:33:29 -04:00
{
2024-06-05 11:00:21 -04:00
// Can be invalid if look-up is called during asset removal/destruction or the entry was
// prematurely "finished". Only return invalid entry if builder asset path cannot be
// matched as this is likely the destroyed entry associated with the provided AssetPath.
const FMetaSoundFrontendDocumentBuilder & DocBuilder = Builder - > GetConstBuilder ( ) ;
if ( DocBuilder . IsValid ( ) )
2024-05-29 15:33:29 -04:00
{
UObject & TestMetaSound = BuilderPtr - > GetConstBuilder ( ) . CastDocumentObjectChecked < UObject > ( ) ;
2024-06-05 11:00:21 -04:00
if ( & TestMetaSound = = MetaSoundObject )
{
FoundEntry = Builder ;
break ;
}
}
else
{
FoundEntry = Builder ;
2024-05-29 15:33:29 -04:00
}
}
}
2024-05-08 14:53:53 -04:00
}
2024-06-05 11:00:21 -04:00
return FoundEntry ;
2024-05-08 14:53:53 -04:00
}
2024-06-05 11:00:21 -04:00
UMetaSoundBuilderBase * FDocumentBuilderRegistry : : FindBuilderObject ( const FMetasoundFrontendClassName & InClassName , const FTopLevelAssetPath & AssetPath ) const
2024-05-08 14:53:53 -04:00
{
2024-05-29 15:33:29 -04:00
TArray < TWeakObjectPtr < UMetaSoundBuilderBase > > Entries ;
{
FScopeLock Lock ( & BuildersCriticalSection ) ;
2024-06-03 14:44:36 -04:00
Builders . MultiFind ( InClassName , Entries ) ;
2024-05-29 15:33:29 -04:00
}
2024-06-05 11:00:21 -04:00
UMetaSoundBuilderBase * FoundEntry = nullptr ;
for ( const TWeakObjectPtr < UMetaSoundBuilderBase > & BuilderPtr : Entries )
2024-05-29 15:33:29 -04:00
{
2024-06-05 11:00:21 -04:00
if ( UMetaSoundBuilderBase * Builder = BuilderPtr . Get ( ) )
{
const FMetaSoundFrontendDocumentBuilder & DocBuilder = Builder - > GetConstBuilder ( ) ;
// Can be invalid if look-up is called during asset removal/destruction or the entry was
// prematurely "finished". Only return invalid entry if builder asset path cannot be
// matched as this is likely the destroyed entry associated with the provided AssetPath.
if ( DocBuilder . IsValid ( ) )
{
const UObject & DocObject = DocBuilder . CastDocumentObjectChecked < UObject > ( ) ;
FTopLevelAssetPath ObjectPath ;
if ( ObjectPath . TrySetPath ( & DocObject ) )
{
if ( AssetPath = = ObjectPath )
{
FoundEntry = Builder ;
break ;
}
}
else
{
FoundEntry = Builder ;
}
}
else
{
FoundEntry = Builder ;
}
}
2024-05-29 15:33:29 -04:00
}
2024-06-05 11:00:21 -04:00
return FoundEntry ;
2024-05-29 15:33:29 -04:00
}
2024-06-03 14:44:36 -04:00
TArray < UMetaSoundBuilderBase * > FDocumentBuilderRegistry : : FindBuilderObjects ( const FMetasoundFrontendClassName & InClassName ) const
2024-05-29 15:33:29 -04:00
{
TArray < UMetaSoundBuilderBase * > FoundBuilders ;
TArray < TWeakObjectPtr < UMetaSoundBuilderBase > > Entries ;
{
FScopeLock Lock ( & BuildersCriticalSection ) ;
2024-06-03 14:44:36 -04:00
Builders . MultiFind ( InClassName , Entries ) ;
2024-05-29 15:33:29 -04:00
}
if ( ! Entries . IsEmpty ( ) )
{
Algo : : TransformIf ( Entries , FoundBuilders ,
[ ] ( const TWeakObjectPtr < UMetaSoundBuilderBase > & BuilderPtr ) { return BuilderPtr . IsValid ( ) ; } ,
[ ] ( const TWeakObjectPtr < UMetaSoundBuilderBase > & BuilderPtr ) { return BuilderPtr . Get ( ) ; }
) ;
}
return FoundBuilders ;
}
2024-06-18 16:47:21 -04:00
FGuid FDocumentBuilderRegistry : : ResolveExecutablePageID ( const TScriptInterface < IMetaSoundDocumentInterface > DocumentInterface ) const
{
// TODO: implement selection logic. For now default to 0 guid, which is always valid on an asset.
return FGuid ( ) ;
}
2024-05-29 15:33:29 -04:00
FMetaSoundFrontendDocumentBuilder * FDocumentBuilderRegistry : : FindOutermostBuilder ( const UObject & InSubObject ) const
{
using namespace Metasound : : Frontend ;
TScriptInterface < IMetaSoundDocumentInterface > DocumentInterface = InSubObject . GetOutermostObject ( ) ;
check ( DocumentInterface . GetObject ( ) ) ;
return FindBuilder ( DocumentInterface ) ;
2024-05-08 14:53:53 -04:00
}
2024-05-30 18:56:24 -04:00
bool FDocumentBuilderRegistry : : FinishBuilding ( const FMetasoundFrontendClassName & InClassName , bool bForceUnregisterNodeClass ) const
2024-05-08 14:53:53 -04:00
{
using namespace Metasound ;
using namespace Metasound : : Engine ;
2024-05-29 15:33:29 -04:00
TArray < UMetaSoundBuilderBase * > FoundBuilders = FindBuilderObjects ( InClassName ) ;
for ( UMetaSoundBuilderBase * Builder : FoundBuilders )
2024-05-08 14:53:53 -04:00
{
2024-06-05 11:00:21 -04:00
FinishBuildingInternal ( * Builder , bForceUnregisterNodeClass ) ;
}
2024-06-06 14:50:30 -04:00
FScopeLock Lock ( & BuildersCriticalSection ) ;
return Builders . Remove ( InClassName ) > 0 ;
2024-06-05 11:00:21 -04:00
}
bool FDocumentBuilderRegistry : : FinishBuilding ( const FMetasoundFrontendClassName & InClassName , const FTopLevelAssetPath & AssetPath , bool bForceUnregisterNodeClass ) const
{
using namespace Metasound ;
using namespace Metasound : : Engine ;
TWeakObjectPtr < UMetaSoundBuilderBase > BuilderPtr ;
if ( UMetaSoundBuilderBase * Builder = FindBuilderObject ( InClassName , AssetPath ) )
{
FinishBuildingInternal ( * Builder , bForceUnregisterNodeClass ) ;
BuilderPtr = TWeakObjectPtr < UMetaSoundBuilderBase > ( Builder ) ;
}
FScopeLock Lock ( & BuildersCriticalSection ) ;
return Builders . RemoveSingle ( InClassName , BuilderPtr ) > 0 ;
}
void FDocumentBuilderRegistry : : FinishBuildingInternal ( UMetaSoundBuilderBase & Builder , bool bForceUnregisterNodeClass ) const
{
2024-06-14 13:05:35 -04:00
using namespace Metasound ;
using namespace Metasound : : Frontend ;
2024-06-05 11:00:21 -04:00
// If the builder has applied transactions to its document object that are not mirrored in the frontend registry,
// unregister version in registry. This will ensure that future requests for the builder's associated asset will
// register a fresh version from the object as the transaction history is intrinsically lost once this builder
// is destroyed. It is also possible that the DocBuilder's underlying object can be invalid if object was force
// deleted, so validity check is necessary.
FMetaSoundFrontendDocumentBuilder & DocBuilder = Builder . GetBuilder ( ) ;
if ( DocBuilder . IsValid ( ) )
{
2024-06-17 12:51:18 -04:00
if ( Metasound : : CanEverExecuteGraph ( ) )
2024-05-08 14:53:53 -04:00
{
2024-06-05 11:00:21 -04:00
const int32 TransactionCount = DocBuilder . GetTransactionCount ( ) ;
const int32 LastTransactionRegistered = Builder . GetLastTransactionRegistered ( ) ;
if ( bForceUnregisterNodeClass | | LastTransactionRegistered ! = TransactionCount )
2024-05-08 14:53:53 -04:00
{
2024-06-05 11:00:21 -04:00
UObject & MetaSound = DocBuilder . CastDocumentObjectChecked < UObject > ( ) ;
if ( FMetasoundAssetBase * MetaSoundAsset = IMetasoundUObjectRegistry : : Get ( ) . GetObjectAsAssetBase ( & MetaSound ) )
2024-05-10 12:40:21 -04:00
{
2024-06-05 11:00:21 -04:00
MetaSoundAsset - > UnregisterGraphWithFrontend ( ) ;
2024-05-10 12:40:21 -04:00
}
2024-05-08 14:53:53 -04:00
}
2024-05-29 15:33:29 -04:00
}
2024-06-05 11:00:21 -04:00
DocBuilder . FinishBuilding ( ) ;
2024-05-29 15:33:29 -04:00
}
}
bool FDocumentBuilderRegistry : : ReloadBuilder ( const FMetasoundFrontendClassName & InClassName ) const
{
2024-06-05 11:00:21 -04:00
bool bReloaded = false ;
TArray < UMetaSoundBuilderBase * > ClassBuilders = FindBuilderObjects ( InClassName ) ;
for ( UMetaSoundBuilderBase * Builder : ClassBuilders )
2024-05-29 15:33:29 -04:00
{
Builder - > Reload ( ) ;
2024-06-05 11:00:21 -04:00
bReloaded = true ;
2024-05-08 14:53:53 -04:00
}
2024-06-05 11:00:21 -04:00
return bReloaded ;
2024-05-08 14:53:53 -04:00
}
2024-06-10 13:52:48 -04:00
void FDocumentBuilderRegistry : : SetEventLogVerbosity ( ELogEvent Event , ELogVerbosity : : Type Verbosity )
{
EventLogVerbosity . FindOrAdd ( Event ) = Verbosity ;
}
2024-05-08 14:53:53 -04:00
} // namespace Metasound::Engine