2020-07-17 16:43:42 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundAssetBase.h"
2020-12-14 15:48:27 -04:00
# include "Algo/AnyOf.h"
2021-06-16 11:21:13 -04:00
# include "Algo/Transform.h"
2021-09-16 14:46:43 -04:00
# include "Containers/Set.h"
2020-07-21 14:34:07 -04:00
# include "HAL/FileManager.h"
2021-06-08 10:52:31 -04:00
# include "Internationalization/Text.h"
2020-07-17 16:43:42 -04:00
# include "IStructSerializerBackend.h"
2021-07-27 15:36:03 -04:00
# include "Logging/LogMacros.h"
2020-12-14 15:48:27 -04:00
# include "MetasoundArchetype.h"
2021-11-07 23:43:01 -05:00
# include "MetasoundAssetManager.h"
2021-06-23 20:08:21 -04:00
# include "MetasoundFrontendArchetypeRegistry.h"
2021-01-13 10:48:59 -04:00
# include "MetasoundFrontendController.h"
2021-07-12 16:13:03 -04:00
# include "MetasoundFrontendDocument.h"
2021-06-08 10:52:31 -04:00
# include "MetasoundFrontendGraph.h"
2021-07-28 17:12:57 -04:00
# include "MetasoundFrontendInjectReceiveNodes.h"
2021-08-18 15:16:57 -04:00
# include "MetasoundFrontendRegistries.h"
2021-06-08 10:52:31 -04:00
# include "MetasoundFrontendSearchEngine.h"
2021-01-13 10:48:59 -04:00
# include "MetasoundFrontendTransform.h"
2020-12-14 15:48:27 -04:00
# include "MetasoundJsonBackend.h"
2021-01-13 10:48:59 -04:00
# include "MetasoundLog.h"
2021-08-30 14:08:45 -04:00
# include "MetasoundParameterTransmitter.h"
2021-08-11 15:22:01 -04:00
# include "MetasoundTrace.h"
2021-09-13 14:14:37 -04:00
# include "MetasoundVertex.h"
2020-07-17 16:43:42 -04:00
# include "StructSerializer.h"
2021-06-16 11:21:13 -04:00
# include "UObject/MetaData.h"
2020-07-17 16:43:42 -04:00
2021-06-08 10:52:31 -04:00
# define LOCTEXT_NAMESPACE "MetaSound"
2021-06-16 11:21:13 -04:00
namespace Metasound
{
2021-07-27 15:36:03 -04:00
namespace AssetBasePrivate
{
void DepthFirstTraversal ( const FMetasoundAssetBase & InInitAsset , TFunctionRef < TSet < const FMetasoundAssetBase * > ( const FMetasoundAssetBase & ) > InVisitFunction )
{
// Non recursive depth first traversal.
TArray < const FMetasoundAssetBase * > Stack ( { & InInitAsset } ) ;
TSet < const FMetasoundAssetBase * > Visited ;
while ( ! Stack . IsEmpty ( ) )
{
const FMetasoundAssetBase * CurrentNode = Stack . Pop ( ) ;
if ( ! Visited . Contains ( CurrentNode ) )
{
TArray < const FMetasoundAssetBase * > Children = InVisitFunction ( * CurrentNode ) . Array ( ) ;
Stack . Append ( Children ) ;
Visited . Add ( CurrentNode ) ;
}
}
}
} // namespace AssetBasePrivate
2021-06-16 11:21:13 -04:00
} // namespace Metasound
2020-07-23 16:39:56 -04:00
const FString FMetasoundAssetBase : : FileExtension ( TEXT ( " .metasound " ) ) ;
2021-11-07 23:43:01 -05:00
void FMetasoundAssetBase : : RegisterGraphWithFrontend ( Metasound : : Frontend : : FMetaSoundAssetRegistrationOptions InRegistrationOptions )
2020-07-23 16:39:56 -04:00
{
2021-06-08 10:52:31 -04:00
using namespace Metasound ;
using namespace Metasound : : Frontend ;
2020-07-23 16:39:56 -04:00
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : RegisterGraphWithFrontend ) ;
2021-08-18 15:16:57 -04:00
if ( ! InRegistrationOptions . bForceReregister )
2021-06-08 10:52:31 -04:00
{
2021-08-18 15:16:57 -04:00
if ( IsRegistered ( ) )
{
return ;
}
}
2021-09-16 14:46:43 -04:00
// Triggers the existing runtime data to be out-of-date.
CurrentCachedRuntimeDataChangeID = FGuid : : NewGuid ( ) ;
2021-08-18 15:16:57 -04:00
2021-11-07 23:43:01 -05:00
if ( InRegistrationOptions . bRegisterDependencies )
{
// Must be called in case register is called prior to asset scan being completed
IMetaSoundAssetManager : : GetChecked ( ) . AddAssetReferences ( * this ) ;
}
2021-08-18 15:16:57 -04:00
if ( InRegistrationOptions . bRebuildReferencedAssetClassKeys )
{
2021-11-07 23:43:01 -05:00
TSet < FNodeRegistryKey > ReferencedKeys = IMetaSoundAssetManager : : GetChecked ( ) . GetReferencedKeys ( * this ) ;
SetReferencedAssetClassKeys ( MoveTemp ( ReferencedKeys ) ) ;
2021-08-18 15:16:57 -04:00
}
if ( InRegistrationOptions . bRegisterDependencies )
{
2021-11-07 23:43:01 -05:00
TArray < FMetasoundAssetBase * > References ;
ensureAlways ( IMetaSoundAssetManager : : GetChecked ( ) . TryLoadReferencedAssets ( * this , References ) ) ;
GetReferencedAssetClassCache ( ) . Reset ( ) ;
2021-08-18 15:16:57 -04:00
for ( FMetasoundAssetBase * Reference : References )
{
if ( InRegistrationOptions . bForceReregister | | ! Reference - > IsRegistered ( ) )
{
// TODO: Check for infinite recursion and error if so
Reference - > RegisterGraphWithFrontend ( InRegistrationOptions ) ;
}
if ( UObject * RefAsset = Reference - > GetOwningAsset ( ) )
{
GetReferencedAssetClassCache ( ) . Add ( RefAsset ) ;
}
}
}
// Auto update must be done after all referenced asset classes are registered
if ( InRegistrationOptions . bAutoUpdate )
{
AutoUpdate ( ) ;
2021-06-08 10:52:31 -04:00
}
2021-06-14 16:46:19 -04:00
// Registers node by copying document. Updates to document require re-registration.
class FNodeRegistryEntry : public INodeRegistryEntry
2021-06-08 10:52:31 -04:00
{
2021-06-14 16:46:19 -04:00
public :
2021-06-16 11:21:13 -04:00
FNodeRegistryEntry ( const FString & InName , const FMetasoundFrontendDocument & InDocument , FName InAssetPath )
2021-06-14 16:46:19 -04:00
: Name ( InName )
, Document ( InDocument )
2021-06-08 10:52:31 -04:00
{
2021-06-14 16:46:19 -04:00
// Copy frontend class to preserve original document.
FrontendClass = Document . RootGraph ;
2021-07-27 15:36:03 -04:00
FrontendClass . Metadata . SetType ( EMetasoundFrontendClassType : : External ) ;
2021-06-16 11:21:13 -04:00
ClassInfo = FNodeClassInfo ( Document . RootGraph , InAssetPath ) ;
2021-06-08 10:52:31 -04:00
}
2021-06-14 16:46:19 -04:00
virtual ~ FNodeRegistryEntry ( ) = default ;
virtual const FNodeClassInfo & GetClassInfo ( ) const override
{
return ClassInfo ;
}
2021-09-07 17:07:54 -04:00
virtual TUniquePtr < INode > CreateNode ( const FNodeInitData & ) const override
2021-06-14 16:46:19 -04:00
{
2021-09-07 17:07:54 -04:00
return FFrontendGraphBuilder ( ) . CreateGraph ( Document ) ;
2021-06-14 16:46:19 -04:00
}
virtual TUniquePtr < INode > CreateNode ( FDefaultLiteralNodeConstructorParams & & ) const override
{
return nullptr ;
}
2021-09-07 17:07:54 -04:00
virtual TUniquePtr < INode > CreateNode ( FDefaultNamedVertexNodeConstructorParams & & ) const override
2021-06-14 16:46:19 -04:00
{
2021-09-07 17:07:54 -04:00
return nullptr ;
2021-06-14 16:46:19 -04:00
}
2021-09-07 17:07:54 -04:00
virtual TUniquePtr < INode > CreateNode ( FDefaultNamedVertexWithLiteralNodeConstructorParams & & ) const override
{
return nullptr ;
}
2021-06-14 16:46:19 -04:00
virtual const FMetasoundFrontendClass & GetFrontendClass ( ) const override
{
return FrontendClass ;
}
virtual TUniquePtr < INodeRegistryEntry > Clone ( ) const override
{
2021-06-16 11:21:13 -04:00
return MakeUnique < FNodeRegistryEntry > ( Name , Document , ClassInfo . AssetPath ) ;
2021-06-14 16:46:19 -04:00
}
2021-07-27 15:36:03 -04:00
virtual bool IsNative ( ) const override
{
return false ;
}
2021-06-14 16:46:19 -04:00
private :
FString Name ;
FMetasoundFrontendDocument Document ;
FMetasoundFrontendClass FrontendClass ;
FNodeClassInfo ClassInfo ;
2021-06-08 10:52:31 -04:00
} ;
2021-07-27 15:36:03 -04:00
UnregisterGraphWithFrontend ( ) ;
2021-06-08 10:52:31 -04:00
2021-08-18 15:16:57 -04:00
FString AssetName ;
FString AssetPath ;
const UObject * OwningAsset = GetOwningAsset ( ) ;
if ( ensure ( OwningAsset ) )
{
AssetName = OwningAsset - > GetName ( ) ;
AssetPath = OwningAsset - > GetPathName ( ) ;
}
2021-07-27 15:36:03 -04:00
FNodeClassInfo AssetClassInfo = GetAssetClassInfo ( ) ;
2021-08-18 15:16:57 -04:00
const FMetasoundFrontendDocument * Doc = GetDocument ( ) . Get ( ) ;
if ( Doc )
2021-06-16 11:21:13 -04:00
{
2021-07-27 15:36:03 -04:00
RegistryKey = FMetasoundFrontendRegistryContainer : : Get ( ) - > RegisterNode ( MakeUnique < FNodeRegistryEntry > ( AssetName , * Doc , AssetClassInfo . AssetPath ) ) ;
}
2021-06-16 11:21:13 -04:00
2021-07-12 16:13:03 -04:00
if ( NodeRegistryKey : : IsValid ( RegistryKey ) )
2021-06-16 11:21:13 -04:00
{
# if WITH_EDITORONLY_DATA
// Refresh Asset Registry Info if successfully registered with Frontend
2021-07-27 15:36:03 -04:00
const FMetasoundFrontendGraphClass & DocumentClassGraph = GetDocumentHandle ( ) - > GetRootGraphClass ( ) ;
const FMetasoundFrontendClassMetadata & DocumentClassMetadata = DocumentClassGraph . Metadata ;
AssetClassInfo . AssetClassID = FGuid ( DocumentClassMetadata . GetClassName ( ) . Name . ToString ( ) ) ;
FNodeClassName ClassName = DocumentClassMetadata . GetClassName ( ) . ToNodeClassName ( ) ;
2021-06-16 11:21:13 -04:00
FMetasoundFrontendClass GraphClass ;
2021-07-27 15:36:03 -04:00
ensure ( ISearchEngine : : Get ( ) . FindClassWithMajorVersion ( ClassName , DocumentClassMetadata . GetVersion ( ) . Major , GraphClass ) ) ;
2021-06-16 11:21:13 -04:00
2021-07-27 15:36:03 -04:00
AssetClassInfo . Version = DocumentClassMetadata . GetVersion ( ) ;
2021-06-16 11:21:13 -04:00
2021-07-27 15:36:03 -04:00
AssetClassInfo . InputTypes . Reset ( ) ;
Algo : : Transform ( GraphClass . Interface . Inputs , AssetClassInfo . InputTypes , [ ] ( const FMetasoundFrontendClassInput & Input ) { return Input . TypeName ; } ) ;
2021-06-16 11:21:13 -04:00
2021-07-27 15:36:03 -04:00
AssetClassInfo . OutputTypes . Reset ( ) ;
Algo : : Transform ( GraphClass . Interface . Outputs , AssetClassInfo . OutputTypes , [ ] ( const FMetasoundFrontendClassOutput & Output ) { return Output . TypeName ; } ) ;
2021-06-16 11:21:13 -04:00
2021-07-27 15:36:03 -04:00
SetRegistryAssetClassInfo ( MoveTemp ( AssetClassInfo ) ) ;
2021-06-16 11:21:13 -04:00
# endif // WITH_EDITORONLY_DATA
}
else
2021-06-08 10:52:31 -04:00
{
2021-08-18 15:16:57 -04:00
FString ClassName ;
if ( OwningAsset )
{
if ( UClass * Class = OwningAsset - > GetClass ( ) )
{
ClassName = Class - > GetName ( ) ;
}
}
UE_LOG ( LogMetaSound , Error , TEXT ( " Registration failed for MetaSound node class '%s' of UObject class '%s' " ) , * AssetName , * ClassName ) ;
2021-06-08 10:52:31 -04:00
}
2020-07-23 16:39:56 -04:00
}
2021-07-27 15:36:03 -04:00
void FMetasoundAssetBase : : UnregisterGraphWithFrontend ( )
{
using namespace Metasound : : Frontend ;
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : UnregisterGraphWithFrontend ) ;
2021-07-27 15:36:03 -04:00
if ( ! NodeRegistryKey : : IsValid ( RegistryKey ) )
{
return ;
}
const UObject * OwningAsset = GetOwningAsset ( ) ;
if ( ! ensureAlways ( OwningAsset ) )
{
return ;
}
ensureAlways ( FMetasoundFrontendRegistryContainer : : Get ( ) - > UnregisterNode ( RegistryKey ) ) ;
RegistryKey = FNodeRegistryKey ( ) ;
}
2021-01-13 10:48:59 -04:00
void FMetasoundAssetBase : : SetMetadata ( FMetasoundFrontendClassMetadata & InMetadata )
2020-07-17 16:43:42 -04:00
{
2021-01-13 10:48:59 -04:00
FMetasoundFrontendDocument & Doc = GetDocumentChecked ( ) ;
Doc . RootGraph . Metadata = InMetadata ;
2021-07-27 15:36:03 -04:00
if ( Doc . RootGraph . Metadata . GetType ( ) ! = EMetasoundFrontendClassType : : Graph )
2021-01-13 10:48:59 -04:00
{
2021-04-02 03:03:27 -04:00
UE_LOG ( LogMetaSound , Display , TEXT ( " Forcing class type to EMetasoundFrontendClassType::Graph on root graph metadata " ) ) ;
2021-07-27 15:36:03 -04:00
Doc . RootGraph . Metadata . SetType ( EMetasoundFrontendClassType : : Graph ) ;
2021-01-13 10:48:59 -04:00
}
2021-02-01 15:59:27 -04:00
MarkMetasoundDocumentDirty ( ) ;
2020-07-17 16:43:42 -04:00
}
2021-06-23 20:08:21 -04:00
bool FMetasoundAssetBase : : IsArchetypeSupported ( const FMetasoundFrontendVersion & InArchetypeVersion ) const
2020-12-14 15:48:27 -04:00
{
2021-06-23 20:08:21 -04:00
return GetSupportedArchetypeVersions ( ) . Contains ( InArchetypeVersion ) ;
2020-12-14 15:48:27 -04:00
}
2021-06-23 20:08:21 -04:00
FMetasoundFrontendVersion FMetasoundAssetBase : : GetPreferredArchetypeVersion ( const FMetasoundFrontendDocument & InDocument ) const
2020-12-14 15:48:27 -04:00
{
2021-06-23 20:08:21 -04:00
using namespace Metasound : : Frontend ;
2021-06-08 10:52:31 -04:00
// Default to archetype provided in case it is supported.
2021-06-23 20:08:21 -04:00
if ( IsArchetypeSupported ( InDocument . ArchetypeVersion ) )
2020-12-14 15:48:27 -04:00
{
2021-06-23 20:08:21 -04:00
return InDocument . ArchetypeVersion ;
2020-12-14 15:48:27 -04:00
}
2021-06-23 20:08:21 -04:00
// If existing archetype is not supported, check if an updated version is preferred.
auto IsNewVersion = [ & ] ( const FMetasoundFrontendVersion & InVersion )
{
return ( InDocument . ArchetypeVersion . Name = = InVersion . Name ) & & ( InVersion . Number > InDocument . ArchetypeVersion . Number ) ;
} ;
TArray < FMetasoundFrontendVersion > UpdatedVersions = GetSupportedArchetypeVersions ( ) . FilterByPredicate ( IsNewVersion ) ;
if ( UpdatedVersions . Num ( ) > 0 )
{
UpdatedVersions . Sort ( ) ;
return UpdatedVersions . Last ( ) ;
}
2020-12-14 15:48:27 -04:00
// If existing archetype is not supported, get the most similar that still supports the documents environment.
2021-06-23 20:08:21 -04:00
TArray < FMetasoundFrontendArchetype > Archetypes ;
for ( const FMetasoundFrontendVersion & Version : GetSupportedArchetypeVersions ( ) )
{
const FArchetypeRegistryKey Key = GetArchetypeRegistryKey ( Version ) ;
FMetasoundFrontendArchetype Archetype ;
if ( IArchetypeRegistry : : Get ( ) . FindArchetype ( Key , Archetype ) )
{
Archetypes . Add ( MoveTemp ( Archetype ) ) ;
}
}
const FMetasoundFrontendArchetype * SimilarArchetype = Metasound : : Frontend : : FindMostSimilarArchetypeSupportingEnvironment ( InDocument , Archetypes ) ;
2020-12-14 15:48:27 -04:00
if ( nullptr ! = SimilarArchetype )
{
2021-06-23 20:08:21 -04:00
return SimilarArchetype - > Version ;
2020-12-14 15:48:27 -04:00
}
// Nothing found. Return the existing archetype for the FMetasoundAssetBase.
2021-06-23 20:08:21 -04:00
return GetDefaultArchetypeVersion ( ) ;
2020-12-14 15:48:27 -04:00
}
2021-07-28 17:12:57 -04:00
bool FMetasoundAssetBase : : GetArchetype ( FMetasoundFrontendArchetype & OutArchetype ) const
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
bool bFoundArchetype = false ;
const FMetasoundFrontendDocument * Document = GetDocument ( ) . Get ( ) ;
if ( nullptr ! = Document )
{
FArchetypeRegistryKey ArchetypeRegistryKey = GetArchetypeRegistryKey ( Document - > ArchetypeVersion ) ;
bFoundArchetype = IArchetypeRegistry : : Get ( ) . FindArchetype ( ArchetypeRegistryKey , OutArchetype ) ;
if ( ! bFoundArchetype )
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " No registered archetype matching archetype version on document [ArchetypeVersion:%s] " ) , * Document - > ArchetypeVersion . ToString ( ) ) ;
}
}
return bFoundArchetype ;
}
2021-06-08 10:52:31 -04:00
void FMetasoundAssetBase : : SetDocument ( const FMetasoundFrontendDocument & InDocument )
2020-12-14 15:48:27 -04:00
{
2021-01-13 10:48:59 -04:00
FMetasoundFrontendDocument & Document = GetDocumentChecked ( ) ;
2020-07-23 20:32:26 -04:00
Document = InDocument ;
2021-06-23 20:08:21 -04:00
MarkMetasoundDocumentDirty ( ) ;
2020-07-23 20:32:26 -04:00
}
2021-06-08 10:52:31 -04:00
void FMetasoundAssetBase : : ConformDocumentToArchetype ( )
2020-07-23 20:32:26 -04:00
{
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : ConformDocumentToArchetype ) ;
2021-06-23 20:08:21 -04:00
FMetasoundFrontendDocument & Doc = GetDocumentChecked ( ) ;
2021-02-01 15:59:27 -04:00
2021-06-23 20:08:21 -04:00
FMetasoundFrontendVersion PreferredArchetypeVersion = GetPreferredArchetypeVersion ( Doc ) ;
if ( PreferredArchetypeVersion ! = Doc . ArchetypeVersion )
{
Metasound : : Frontend : : FMatchRootGraphToArchetype Transform ( PreferredArchetypeVersion ) ;
if ( Transform . Transform ( GetDocumentHandle ( ) ) )
{
2021-08-18 15:16:57 -04:00
ConformObjectDataToArchetype ( ) ;
2021-06-23 20:08:21 -04:00
MarkMetasoundDocumentDirty ( ) ;
}
}
}
2021-08-18 15:16:57 -04:00
bool FMetasoundAssetBase : : AutoUpdate ( bool bInMarkDirty )
2021-07-27 15:36:03 -04:00
{
using namespace Metasound : : Frontend ;
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : AutoUpdate ) ;
2021-07-27 15:36:03 -04:00
2021-08-18 15:16:57 -04:00
const bool bUpdated = FAutoUpdateRootGraph ( ) . Transform ( GetDocumentHandle ( ) ) ;
if ( bUpdated & & bInMarkDirty )
2021-07-27 15:36:03 -04:00
{
MarkMetasoundDocumentDirty ( ) ;
}
2021-08-18 15:16:57 -04:00
return bUpdated ;
2021-07-27 15:36:03 -04:00
}
2021-08-18 15:16:57 -04:00
bool FMetasoundAssetBase : : VersionAsset ( )
2021-06-23 20:08:21 -04:00
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : VersionAsset ) ;
2021-06-23 20:08:21 -04:00
FName AssetName ;
FString AssetPath ;
if ( const UObject * OwningAsset = GetOwningAsset ( ) )
{
AssetName = FName ( OwningAsset - > GetName ( ) ) ;
AssetPath = OwningAsset - > GetPathName ( ) ;
}
2021-07-27 15:36:03 -04:00
bool bDidEdit = false ;
2021-06-23 20:08:21 -04:00
FDocumentHandle DocumentHandle = GetDocumentHandle ( ) ;
2021-07-27 15:36:03 -04:00
bDidEdit | = FVersionDocument ( AssetName , AssetPath ) . Transform ( DocumentHandle ) ;
2021-06-23 20:08:21 -04:00
if ( FMetasoundFrontendDocument * Doc = GetDocument ( ) . Get ( ) )
{
2021-08-18 15:16:57 -04:00
FMetasoundFrontendVersion ArchetypeVersion = GetPreferredArchetypeVersion ( * Doc ) ;
bDidEdit | = FMatchRootGraphToArchetype ( ArchetypeVersion ) . Transform ( DocumentHandle ) ;
if ( bDidEdit )
{
ConformObjectDataToArchetype ( ) ;
}
2021-06-23 20:08:21 -04:00
}
2021-07-27 15:36:03 -04:00
return bDidEdit ;
2021-02-01 15:59:27 -04:00
}
2021-09-16 14:46:43 -04:00
TSharedPtr < Metasound : : IGraph , ESPMode : : ThreadSafe > FMetasoundAssetBase : : BuildMetasoundDocument ( ) const
2021-06-08 10:52:31 -04:00
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : BuildMetasoundDocument ) ;
2021-07-28 17:12:57 -04:00
const FMetasoundFrontendDocument * Doc = GetDocument ( ) . Get ( ) ;
if ( nullptr = = Doc )
2021-06-08 10:52:31 -04:00
{
2021-07-28 17:12:57 -04:00
UE_LOG ( LogMetaSound , Error , TEXT ( " Cannot create graph. Null MetaSound document in MetaSound asset [Name:%s] " ) , * GetOwningAssetName ( ) ) ;
2021-09-16 14:46:43 -04:00
return TSharedPtr < IGraph , ESPMode : : ThreadSafe > ( nullptr ) ;
2021-07-28 17:12:57 -04:00
}
2021-06-08 10:52:31 -04:00
2021-07-28 17:12:57 -04:00
// Create graph which can spawn instances. TODO: cache graph.
TUniquePtr < FFrontendGraph > FrontendGraph = FFrontendGraphBuilder : : CreateGraph ( * Doc ) ;
if ( FrontendGraph . IsValid ( ) )
{
2021-09-13 14:14:37 -04:00
TSet < FVertexName > VerticesToSkip = GetNonTransmittableInputVertices ( * Doc ) ;
2021-08-30 14:08:45 -04:00
bool bSuccessfullyInjectedReceiveNodes = InjectReceiveNodes ( * FrontendGraph , FMetaSoundParameterTransmitter : : CreateSendAddressFromEnvironment , VerticesToSkip ) ;
2021-07-28 17:12:57 -04:00
if ( ! bSuccessfullyInjectedReceiveNodes )
2021-06-08 10:52:31 -04:00
{
2021-07-28 17:12:57 -04:00
UE_LOG ( LogMetaSound , Error , TEXT ( " Error while injecting async communication hooks. Instance communication may not function properly [Name:%s]. " ) , * GetOwningAssetName ( ) ) ;
2021-06-08 10:52:31 -04:00
}
}
2021-09-16 14:46:43 -04:00
TSharedPtr < Metasound : : IGraph , ESPMode : : ThreadSafe > SharedGraph ( FrontendGraph . Release ( ) ) ;
return SharedGraph ;
2021-06-08 10:52:31 -04:00
}
2021-08-18 15:16:57 -04:00
bool FMetasoundAssetBase : : IsRegistered ( ) const
2021-07-27 15:36:03 -04:00
{
2021-08-18 15:16:57 -04:00
using namespace Metasound : : Frontend ;
if ( ! NodeRegistryKey : : IsValid ( RegistryKey ) )
{
return false ;
}
return FMetasoundFrontendRegistryContainer : : Get ( ) - > IsNodeRegistered ( RegistryKey ) ;
2021-07-27 15:36:03 -04:00
}
2021-08-18 15:16:57 -04:00
bool FMetasoundAssetBase : : AddingReferenceCausesLoop ( const FSoftObjectPath & InReferencePath ) const
2021-07-27 15:36:03 -04:00
{
2021-11-07 23:43:01 -05:00
using namespace Metasound : : Frontend ;
2021-08-18 15:16:57 -04:00
const FMetasoundAssetBase * ReferenceAsset = IMetaSoundAssetManager : : GetChecked ( ) . TryLoadAsset ( InReferencePath ) ;
2021-07-27 15:36:03 -04:00
if ( ! ensureAlways ( ReferenceAsset ) )
{
return false ;
}
bool bCausesLoop = false ;
const FMetasoundAssetBase * Parent = this ;
Metasound : : AssetBasePrivate : : DepthFirstTraversal ( * ReferenceAsset , [ & ] ( const FMetasoundAssetBase & ChildAsset )
{
TSet < const FMetasoundAssetBase * > Children ;
if ( Parent = = & ChildAsset )
{
bCausesLoop = true ;
return Children ;
}
2021-11-07 23:43:01 -05:00
TArray < FMetasoundAssetBase * > ChildRefs ;
ensureAlways ( IMetaSoundAssetManager : : GetChecked ( ) . TryLoadReferencedAssets ( * this , ChildRefs ) ) ;
2021-08-18 15:16:57 -04:00
for ( const FMetasoundAssetBase * ChildRef : ChildRefs )
2021-07-27 15:36:03 -04:00
{
2021-08-18 15:16:57 -04:00
if ( ChildRef )
2021-07-27 15:36:03 -04:00
{
2021-08-18 15:16:57 -04:00
Children . Add ( ChildRef ) ;
2021-07-27 15:36:03 -04:00
}
}
return Children ;
} ) ;
return bCausesLoop ;
}
2021-09-13 14:14:37 -04:00
Metasound : : FSendAddress FMetasoundAssetBase : : CreateSendAddress ( uint64 InInstanceID , const Metasound : : FVertexName & InVertexName , const FName & InDataTypeName ) const
2021-07-28 17:12:57 -04:00
2021-06-08 10:52:31 -04:00
{
2021-10-12 21:21:22 -04:00
return Metasound : : FSendAddress ( InVertexName , InDataTypeName , InInstanceID ) ;
2021-06-08 10:52:31 -04:00
}
2021-06-16 11:21:13 -04:00
void FMetasoundAssetBase : : ConvertFromPreset ( )
{
using namespace Metasound : : Frontend ;
FGraphHandle GraphHandle = GetRootGraphHandle ( ) ;
FMetasoundFrontendGraphStyle Style = GraphHandle - > GetGraphStyle ( ) ;
Style . bIsGraphEditable = true ;
GraphHandle - > SetGraphStyle ( Style ) ;
2021-07-27 15:36:03 -04:00
FMetasoundFrontendClassMetadata Metadata = GraphHandle - > GetGraphMetadata ( ) ;
Metadata . SetAutoUpdateManagesInterface ( false ) ;
GraphHandle - > SetGraphMetadata ( Metadata ) ;
2021-06-16 11:21:13 -04:00
}
2021-06-08 10:52:31 -04:00
TArray < FMetasoundAssetBase : : FSendInfoAndVertexName > FMetasoundAssetBase : : GetSendInfos ( uint64 InInstanceID ) const
{
2021-07-28 17:12:57 -04:00
using namespace Metasound ;
2021-06-08 10:52:31 -04:00
using namespace Metasound : : Frontend ;
2021-08-30 14:08:45 -04:00
using FSendInfo = FMetaSoundParameterTransmitter : : FSendInfo ;
2021-06-08 10:52:31 -04:00
2021-09-16 14:46:43 -04:00
check ( IsInGameThread ( ) | | IsInAudioThread ( ) ) ;
const FRuntimeData & RuntimeData = GetRuntimeData ( ) ;
2021-06-08 10:52:31 -04:00
TArray < FSendInfoAndVertexName > SendInfos ;
2021-09-16 14:46:43 -04:00
for ( const FMetasoundFrontendClassInput & Vertex : RuntimeData . TransmittableInputs )
2021-06-08 10:52:31 -04:00
{
2021-09-16 14:46:43 -04:00
FSendInfoAndVertexName Info ;
2021-06-08 10:52:31 -04:00
2021-09-16 14:46:43 -04:00
Info . SendInfo . Address = FMetaSoundParameterTransmitter : : CreateSendAddressFromInstanceID ( InInstanceID , Vertex . Name , Vertex . TypeName ) ;
Info . SendInfo . ParameterName = Vertex . Name ;
Info . SendInfo . TypeName = Vertex . TypeName ;
Info . VertexName = Vertex . Name ;
2021-06-08 10:52:31 -04:00
2021-09-16 14:46:43 -04:00
SendInfos . Add ( Info ) ;
2021-06-08 10:52:31 -04:00
}
return SendInfos ;
}
2021-09-13 14:14:37 -04:00
TSet < Metasound : : FVertexName > FMetasoundAssetBase : : GetNonTransmittableInputVertices ( const FMetasoundFrontendDocument & InDoc ) const
2021-08-17 10:14:07 -04:00
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
2021-08-17 16:55:57 -04:00
check ( IsInGameThread ( ) | | IsInAudioThread ( ) ) ;
2021-08-17 10:14:07 -04:00
2021-09-13 14:14:37 -04:00
TSet < FVertexName > NonTransmittableInputVertices ;
2021-08-17 10:14:07 -04:00
auto GetVertexKey = [ ] ( const FMetasoundFrontendClassVertex & InVertex ) { return InVertex . Name ; } ;
// Do not transmit vertices defined in archetype. Those are already accounted
// for by owning object.
FMetasoundFrontendArchetype Archetype ;
GetArchetype ( Archetype ) ;
Algo : : Transform ( Archetype . Interface . Inputs , NonTransmittableInputVertices , GetVertexKey ) ;
// Do not transmit vertices which are not transmittable. Async communication
// is not supported without transmission.
2021-08-19 09:59:27 -04:00
const IDataTypeRegistry & Registry = IDataTypeRegistry : : Get ( ) ;
2021-08-17 10:14:07 -04:00
auto IsNotTransmittable = [ & Registry ] ( const FMetasoundFrontendClassVertex & InVertex ) - > bool
{
FDataTypeRegistryInfo Info ;
2021-08-19 09:59:27 -04:00
if ( Registry . GetDataTypeInfo ( InVertex . TypeName , Info ) )
2021-08-17 10:14:07 -04:00
{
return ! Info . bIsTransmittable ;
}
return true ;
} ;
Algo : : TransformIf ( InDoc . RootGraph . Interface . Inputs , NonTransmittableInputVertices , IsNotTransmittable , GetVertexKey ) ;
return NonTransmittableInputVertices ;
}
2021-09-13 14:14:37 -04:00
TArray < Metasound : : FVertexName > FMetasoundAssetBase : : GetTransmittableInputVertexNames ( ) const
2021-06-08 10:52:31 -04:00
{
using namespace Metasound ;
2021-06-23 20:08:21 -04:00
using namespace Metasound : : Frontend ;
FConstDocumentHandle Document = GetDocumentHandle ( ) ;
// Find the archetype for the document.
FMetasoundFrontendArchetype Archetype ;
FArchetypeRegistryKey ArchetypeRegistryKey = Frontend : : GetArchetypeRegistryKey ( Document - > GetArchetypeVersion ( ) ) ;
bool bFoundArchetype = IArchetypeRegistry : : Get ( ) . FindArchetype ( ArchetypeRegistryKey , Archetype ) ;
if ( ! bFoundArchetype )
{
UE_LOG ( LogMetaSound , Warning , TEXT ( " No registered archetype matching archetype version on document [ArchetypeVersion:%s] " ) , * Document - > GetArchetypeVersion ( ) . ToString ( ) ) ;
}
2021-06-08 10:52:31 -04:00
// Unused inputs are all input vertices that are not in the archetype.
2021-09-13 14:14:37 -04:00
TArray < FVertexName > ArchetypeInputVertexNames ;
2021-06-23 20:08:21 -04:00
for ( const FMetasoundFrontendClassVertex & Vertex : Archetype . Interface . Inputs )
2021-06-08 10:52:31 -04:00
{
ArchetypeInputVertexNames . Add ( Vertex . Name ) ;
}
2021-06-23 20:08:21 -04:00
Frontend : : FConstGraphHandle RootGraph = Document - > GetRootGraph ( ) ;
2021-09-13 14:14:37 -04:00
TArray < FVertexName > GraphInputVertexNames = RootGraph - > GetInputVertexNames ( ) ;
2021-06-08 10:52:31 -04:00
// Filter graph inputs by archetype inputs.
2021-09-13 14:14:37 -04:00
GraphInputVertexNames = GraphInputVertexNames . FilterByPredicate ( [ & ] ( const FVertexName & InName ) { return ! ArchetypeInputVertexNames . Contains ( InName ) ; } ) ;
2021-06-08 10:52:31 -04:00
2021-09-13 14:14:37 -04:00
auto IsDataTypeTransmittable = [ & ] ( const FVertexName & InVertexName )
2021-06-08 10:52:31 -04:00
{
2021-08-19 09:59:27 -04:00
using namespace Metasound : : Frontend ;
2021-06-08 10:52:31 -04:00
Frontend : : FConstClassInputAccessPtr ClassInputPtr = RootGraph - > FindClassInputWithName ( InVertexName ) ;
if ( const FMetasoundFrontendClassInput * ClassInput = ClassInputPtr . Get ( ) )
{
2021-06-14 16:46:19 -04:00
Frontend : : FDataTypeRegistryInfo TypeInfo ;
2021-08-19 09:59:27 -04:00
if ( IDataTypeRegistry : : Get ( ) . GetDataTypeInfo ( ClassInput - > TypeName , TypeInfo ) )
2021-06-08 10:52:31 -04:00
{
if ( TypeInfo . bIsTransmittable )
{
Frontend : : FConstNodeHandle InputNode = RootGraph - > GetNodeWithID ( ClassInput - > NodeID ) ;
if ( InputNode - > IsValid ( ) )
{
return true ;
}
}
}
}
return false ;
} ;
GraphInputVertexNames = GraphInputVertexNames . FilterByPredicate ( IsDataTypeTransmittable ) ;
return GraphInputVertexNames ;
}
2021-08-30 14:08:45 -04:00
Metasound : : Frontend : : FNodeHandle FMetasoundAssetBase : : AddInputPinForSendAddress ( const Metasound : : FMetaSoundParameterTransmitter : : FSendInfo & InSendInfo , Metasound : : Frontend : : FGraphHandle InGraph ) const
2021-06-08 10:52:31 -04:00
{
FMetasoundFrontendClassInput Description ;
FGuid VertexID = FGuid : : NewGuid ( ) ;
2021-10-12 21:21:22 -04:00
Description . Name = InSendInfo . Address . GetChannelName ( ) ;
2021-06-08 10:52:31 -04:00
Description . TypeName = Metasound : : GetMetasoundDataTypeName < Metasound : : FSendAddress > ( ) ;
Description . Metadata . Description = FText : : GetEmpty ( ) ;
Description . VertexID = VertexID ;
2021-10-12 21:21:22 -04:00
Description . DefaultLiteral . Set ( InSendInfo . Address . GetChannelName ( ) . ToString ( ) ) ;
2021-06-08 10:52:31 -04:00
return InGraph - > AddInputVertex ( Description ) ;
}
# if WITH_EDITORONLY_DATA
FText FMetasoundAssetBase : : GetDisplayName ( FString & & InTypeName ) const
{
using namespace Metasound : : Frontend ;
FConstGraphHandle GraphHandle = GetRootGraphHandle ( ) ;
const bool bIsPreset = ! GraphHandle - > GetGraphStyle ( ) . bIsGraphEditable ;
if ( ! bIsPreset )
{
return FText : : FromString ( MoveTemp ( InTypeName ) ) ;
}
return FText : : Format ( LOCTEXT ( " PresetDisplayNameFormat " , " {0} (Preset) " ) , FText : : FromString ( MoveTemp ( InTypeName ) ) ) ;
}
# endif // WITH_EDITORONLY_DATA
2021-02-01 15:59:27 -04:00
bool FMetasoundAssetBase : : MarkMetasoundDocumentDirty ( ) const
{
if ( const UObject * OwningAsset = GetOwningAsset ( ) )
{
return ensure ( OwningAsset - > MarkPackageDirty ( ) ) ;
}
return false ;
2020-07-23 20:32:26 -04:00
}
2021-01-13 10:48:59 -04:00
Metasound : : Frontend : : FDocumentHandle FMetasoundAssetBase : : GetDocumentHandle ( )
{
return Metasound : : Frontend : : IDocumentController : : CreateDocumentHandle ( GetDocument ( ) ) ;
}
2020-07-17 16:43:42 -04:00
2021-01-13 19:18:22 -04:00
Metasound : : Frontend : : FConstDocumentHandle FMetasoundAssetBase : : GetDocumentHandle ( ) const
{
return Metasound : : Frontend : : IDocumentController : : CreateDocumentHandle ( GetDocument ( ) ) ;
}
2020-12-14 15:48:27 -04:00
Metasound : : Frontend : : FGraphHandle FMetasoundAssetBase : : GetRootGraphHandle ( )
2020-07-17 16:43:42 -04:00
{
2021-01-13 10:48:59 -04:00
return GetDocumentHandle ( ) - > GetRootGraph ( ) ;
2020-07-17 16:43:42 -04:00
}
2021-01-13 19:18:22 -04:00
Metasound : : Frontend : : FConstGraphHandle FMetasoundAssetBase : : GetRootGraphHandle ( ) const
{
return GetDocumentHandle ( ) - > GetRootGraph ( ) ;
}
2020-08-05 12:47:19 -04:00
bool FMetasoundAssetBase : : ImportFromJSON ( const FString & InJSON )
2020-07-23 16:39:56 -04:00
{
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : ImportFromJSON ) ;
2021-07-12 16:13:03 -04:00
FMetasoundFrontendDocument * Document = GetDocument ( ) . Get ( ) ;
if ( ensure ( nullptr ! = Document ) )
2020-12-14 15:48:27 -04:00
{
2021-02-01 15:59:27 -04:00
bool bSuccess = Metasound : : Frontend : : ImportJSONToMetasound ( InJSON , * Document ) ;
if ( bSuccess )
{
ensure ( MarkMetasoundDocumentDirty ( ) ) ;
}
return bSuccess ;
2020-12-14 15:48:27 -04:00
}
return false ;
2020-07-23 16:39:56 -04:00
}
2020-08-05 12:47:19 -04:00
bool FMetasoundAssetBase : : ImportFromJSONAsset ( const FString & InAbsolutePath )
2020-07-17 16:43:42 -04:00
{
2021-08-11 15:22:01 -04:00
METASOUND_TRACE_CPUPROFILER_EVENT_SCOPE ( MetaSoundAssetBase : : ImportFromJSONAsset ) ;
2021-07-12 16:13:03 -04:00
Metasound : : Frontend : : FDocumentAccessPtr DocumentPtr = GetDocument ( ) ;
if ( FMetasoundFrontendDocument * Document = DocumentPtr . Get ( ) )
2020-12-14 15:48:27 -04:00
{
2021-02-01 15:59:27 -04:00
bool bSuccess = Metasound : : Frontend : : ImportJSONAssetToMetasound ( InAbsolutePath , * Document ) ;
if ( bSuccess )
{
ensure ( MarkMetasoundDocumentDirty ( ) ) ;
}
return bSuccess ;
2020-12-14 15:48:27 -04:00
}
return false ;
}
2021-01-13 10:48:59 -04:00
FMetasoundFrontendDocument & FMetasoundAssetBase : : GetDocumentChecked ( )
2020-12-14 15:48:27 -04:00
{
2021-07-12 16:13:03 -04:00
FMetasoundFrontendDocument * Document = GetDocument ( ) . Get ( ) ;
check ( nullptr ! = Document ) ;
return * Document ;
2020-12-14 15:48:27 -04:00
}
2021-01-13 10:48:59 -04:00
const FMetasoundFrontendDocument & FMetasoundAssetBase : : GetDocumentChecked ( ) const
2020-12-14 15:48:27 -04:00
{
2021-07-12 16:13:03 -04:00
const FMetasoundFrontendDocument * Document = GetDocument ( ) . Get ( ) ;
2020-12-14 15:48:27 -04:00
2021-07-12 16:13:03 -04:00
check ( nullptr ! = Document ) ;
return * Document ;
2020-08-05 12:47:19 -04:00
}
2021-07-28 17:12:57 -04:00
FString FMetasoundAssetBase : : GetOwningAssetName ( ) const
{
if ( const UObject * OwningAsset = GetOwningAsset ( ) )
{
return OwningAsset - > GetName ( ) ;
}
return FString ( ) ;
}
2021-09-16 14:46:43 -04:00
TSharedPtr < const Metasound : : IGraph , ESPMode : : ThreadSafe > FMetasoundAssetBase : : GetMetasoundCoreGraph ( ) const
{
return FMetasoundAssetBase : : GetRuntimeData ( ) . Graph ;
}
TArray < FMetasoundFrontendClassInput > FMetasoundAssetBase : : GetTransmittableClassInputs ( ) const
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
check ( IsInGameThread ( ) | | IsInAudioThread ( ) ) ;
TArray < FMetasoundFrontendClassInput > Inputs ;
if ( const FMetasoundFrontendDocument * Doc = GetDocument ( ) . Get ( ) )
{
TSet < FVertexName > ArchetypeInputs ;
auto GetVertexKey = [ ] ( const FMetasoundFrontendClassVertex & InVertex ) { return InVertex . Name ; } ;
// Do not transmit vertices defined in archetype. Those are already accounted
// for by owning object.
FMetasoundFrontendArchetype Archetype ;
GetArchetype ( Archetype ) ;
Algo : : Transform ( Archetype . Interface . Inputs , ArchetypeInputs , GetVertexKey ) ;
// Do not transmit vertices which are not transmittable. Async communication
// is not supported without transmission.
IDataTypeRegistry & Registry = IDataTypeRegistry : : Get ( ) ;
auto IsTransmittable = [ & Registry , & ArchetypeInputs ] ( const FMetasoundFrontendClassVertex & InVertex ) - > bool
{
if ( ! ArchetypeInputs . Contains ( InVertex . Name ) )
{
FDataTypeRegistryInfo Info ;
if ( Registry . GetDataTypeInfo ( InVertex . TypeName , Info ) )
{
return Info . bIsTransmittable ;
}
}
return false ;
} ;
for ( const FMetasoundFrontendClassInput & Vertex : Doc - > RootGraph . Interface . Inputs )
{
if ( IsTransmittable ( Vertex ) )
{
Inputs . Add ( Vertex ) ;
}
}
}
return Inputs ;
}
const FMetasoundAssetBase : : FRuntimeData & FMetasoundAssetBase : : GetRuntimeData ( ) const
{
// Check if a ChangeID has been generated before.
if ( ! CurrentCachedRuntimeDataChangeID . IsValid ( ) )
{
CurrentCachedRuntimeDataChangeID = FGuid : : NewGuid ( ) ;
}
// Check if CachedRuntimeData is out-of-date.
if ( CachedRuntimeData . ChangeID ! = CurrentCachedRuntimeDataChangeID )
{
// Update CachedRuntimeData.
CachedRuntimeData . TransmittableInputs = GetTransmittableClassInputs ( ) ;
CachedRuntimeData . Graph = BuildMetasoundDocument ( ) ;
CachedRuntimeData . ChangeID = CurrentCachedRuntimeDataChangeID ;
}
return CachedRuntimeData ;
}
2021-06-08 10:52:31 -04:00
# undef LOCTEXT_NAMESPACE // "MetaSound"