2020-07-20 00:05:22 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundFrontendRegistries.h"
# include "CoreMinimal.h"
2020-07-20 13:14:38 -04:00
# include "Misc/ScopeLock.h"
# include "HAL/PlatformTime.h"
2020-07-20 00:05:22 -04:00
# ifndef WITH_METASOUND_FRONTEND
# define WITH_METASOUND_FRONTEND 0
# endif
FMetasoundFrontendRegistryContainer : : FMetasoundFrontendRegistryContainer ( )
: bHasModuleBeenInitialized ( false )
{
}
FMetasoundFrontendRegistryContainer * FMetasoundFrontendRegistryContainer : : LazySingleton = nullptr ;
FMetasoundFrontendRegistryContainer * FMetasoundFrontendRegistryContainer : : Get ( )
{
if ( ! LazySingleton )
{
LazySingleton = new FMetasoundFrontendRegistryContainer ( ) ;
}
return LazySingleton ;
}
void FMetasoundFrontendRegistryContainer : : ShutdownMetasoundFrontend ( )
{
if ( LazySingleton )
{
delete LazySingleton ;
LazySingleton = nullptr ;
}
}
void FMetasoundFrontendRegistryContainer : : InitializeFrontend ( )
{
FScopeLock ScopeLock ( & LazyInitCommandCritSection ) ;
if ( bHasModuleBeenInitialized )
{
// this function should only be called once.
checkNoEntry ( ) ;
}
UE_LOG ( LogTemp , Display , TEXT ( " Initializing Metasounds Frontend. " ) ) ;
uint64 CurrentTime = FPlatformTime : : Cycles64 ( ) ;
for ( TUniqueFunction < void ( ) > & Command : LazyInitCommands )
{
Command ( ) ;
}
LazyInitCommands . Empty ( ) ;
bHasModuleBeenInitialized = true ;
uint64 CyclesUsed = FPlatformTime : : Cycles64 ( ) - CurrentTime ;
UE_LOG ( LogTemp , Display , TEXT ( " Initializing Metasounds Frontend took %f seconds. " ) , FPlatformTime : : ToSeconds64 ( CyclesUsed ) ) ;
}
bool FMetasoundFrontendRegistryContainer : : EnqueueInitCommand ( TUniqueFunction < void ( ) > & & InFunc )
{
// if the module has been initalized already, we can safely call this function now.
if ( bHasModuleBeenInitialized )
{
InFunc ( ) ;
}
// otherwise, we enqueue the function to be executed after the frontend module has been initialized.
if ( LazyInitCommands . Num ( ) > = MaxNumNodesAndDatatypesToInitialize )
{
UE_LOG ( LogTemp , Warning , TEXT ( " Registering more that %d nodes and datatypes for metasounds! Consider increasing MetasoundFrontendRegistryContainer::MaxNumNodesAndDatatypesToInitialize. " ) ) ;
}
LazyInitCommands . Add ( MoveTemp ( InFunc ) ) ;
return true ;
}
TMap < FMetasoundFrontendRegistryContainer : : FNodeRegistryKey , FMetasoundFrontendRegistryContainer : : FNodeRegistryElement > & FMetasoundFrontendRegistryContainer : : GetExternalNodeRegistry ( )
{
return ExternalNodeRegistry ;
}
2020-08-13 19:07:41 -04:00
TUniquePtr < Metasound : : INode > FMetasoundFrontendRegistryContainer : : ConstructInputNode ( const FName & InInputType , Metasound : : FInputNodeConstructorParams & & InParams )
2020-07-20 00:05:22 -04:00
{
if ( ensureAlwaysMsgf ( DataTypeRegistry . Contains ( InInputType ) , TEXT ( " Couldn't find data type %s! " ) , * InInputType . ToString ( ) ) )
{
2020-08-13 19:07:41 -04:00
return DataTypeRegistry [ InInputType ] . Callbacks . InputNodeConstructor ( MoveTemp ( InParams ) ) ;
2020-07-20 00:05:22 -04:00
}
else
{
return nullptr ;
}
}
TUniquePtr < Metasound : : INode > FMetasoundFrontendRegistryContainer : : ConstructOutputNode ( const FName & InOutputType , const Metasound : : FOutputNodeConstrutorParams & InParams )
{
if ( ensureAlwaysMsgf ( DataTypeRegistry . Contains ( InOutputType ) , TEXT ( " Couldn't find data type %s! " ) , * InOutputType . ToString ( ) ) )
{
2020-08-13 19:07:41 -04:00
return DataTypeRegistry [ InOutputType ] . Callbacks . OutputNodeConstructor ( InParams ) ;
2020-07-20 00:05:22 -04:00
}
else
{
return nullptr ;
}
}
2020-08-13 19:07:41 -04:00
Metasound : : FDataTypeLiteralParam FMetasoundFrontendRegistryContainer : : GenerateLiteralForUObject ( const FName & InDataType , UObject * InObject )
{
if ( ensureAlwaysMsgf ( DataTypeRegistry . Contains ( InDataType ) , TEXT ( " Couldn't find data type %s! " ) , * InDataType . ToString ( ) ) )
{
Audio : : IProxyDataPtr ProxyPtr = DataTypeRegistry [ InDataType ] . Callbacks . ProxyConstructor ( InObject ) ;
if ( ensureAlwaysMsgf ( ProxyPtr . IsValid ( ) , TEXT ( " UObject failed to create a valid proxy! " ) ) )
{
return Metasound : : FDataTypeLiteralParam ( MoveTemp ( ProxyPtr ) ) ;
}
else
{
return Metasound : : FDataTypeLiteralParam ( ) ;
}
}
else
{
return Metasound : : FDataTypeLiteralParam ( ) ;
}
}
Metasound : : FDataTypeLiteralParam FMetasoundFrontendRegistryContainer : : GenerateLiteralForUObjectArray ( const FName & InDataType , TArray < UObject * > InObjectArray )
{
if ( ensureAlwaysMsgf ( DataTypeRegistry . Contains ( InDataType ) , TEXT ( " Couldn't find data type %s! " ) , * InDataType . ToString ( ) ) )
{
TArray < Audio : : IProxyDataPtr > ProxyArray ;
for ( UObject * InObject : InObjectArray )
{
if ( InObject )
{
Audio : : IProxyDataPtr ProxyPtr = DataTypeRegistry [ InDataType ] . Callbacks . ProxyConstructor ( InObject ) ;
ensureAlwaysMsgf ( ProxyPtr . IsValid ( ) , TEXT ( " UObject failed to create a valid proxy! " ) ) ;
ProxyArray . Add ( MoveTemp ( ProxyPtr ) ) ;
}
}
return Metasound : : FDataTypeLiteralParam ( MoveTemp ( ProxyArray ) ) ;
}
else
{
return Metasound : : FDataTypeLiteralParam ( ) ;
}
}
2020-07-20 00:05:22 -04:00
TUniquePtr < Metasound : : INode > FMetasoundFrontendRegistryContainer : : ConstructExternalNode ( const FName & InNodeType , uint32 InNodeHash , const Metasound : : FNodeInitData & InInitData )
{
Metasound : : Frontend : : FNodeRegistryKey RegistryKey ;
RegistryKey . NodeName = InNodeType ;
RegistryKey . NodeHash = InNodeHash ;
if ( ! ExternalNodeRegistry . Contains ( RegistryKey ) )
{
return nullptr ;
}
else
{
return ExternalNodeRegistry [ RegistryKey ] . GetterCallback ( InInitData ) ;
}
}
Metasound : : ELiteralArgType FMetasoundFrontendRegistryContainer : : GetDesiredLiteralTypeForDataType ( FName InDataType ) const
{
if ( ! DataTypeRegistry . Contains ( InDataType ) )
{
return Metasound : : ELiteralArgType : : Invalid ;
}
const FDataTypeRegistryElement & DataTypeInfo = DataTypeRegistry [ InDataType ] ;
// If there's a designated preferred literal type for this datatype, use that.
if ( DataTypeInfo . Info . PreferredLiteralType ! = Metasound : : ELiteralArgType : : None )
{
return DataTypeInfo . Info . PreferredLiteralType ;
}
// Otherwise, we opt for the highest precision construction option available.
if ( DataTypeInfo . Info . bIsStringParsable )
{
return Metasound : : ELiteralArgType : : String ;
}
else if ( DataTypeInfo . Info . bIsFloatParsable )
{
return Metasound : : ELiteralArgType : : Float ;
}
else if ( DataTypeInfo . Info . bIsIntParsable )
{
return Metasound : : ELiteralArgType : : Integer ;
}
else if ( DataTypeInfo . Info . bIsBoolParsable )
{
return Metasound : : ELiteralArgType : : Boolean ;
}
else if ( DataTypeInfo . Info . bIsConstructableWithSettings | | DataTypeInfo . Info . bIsDefaultConstructible )
{
return Metasound : : ELiteralArgType : : None ;
}
else
{
// if we ever hit this, something has gone terribly wrong with the REGISTER_METASOUND_DATATYPE macro.
// we should have failed to compile if any of these are false.
checkNoEntry ( ) ;
return Metasound : : ELiteralArgType : : Invalid ;
}
}
2020-08-13 19:07:41 -04:00
UClass * FMetasoundFrontendRegistryContainer : : GetLiteralUClassForDataType ( FName InDataType ) const
{
if ( ! DataTypeRegistry . Contains ( InDataType ) )
{
ensureAlwaysMsgf ( false , TEXT ( " couldn't find DataType %s in the registry. " ) , * InDataType . ToString ( ) ) ;
return nullptr ;
}
else
{
return DataTypeRegistry [ InDataType ] . Info . ProxyGeneratorClass ;
}
}
2020-07-20 00:05:22 -04:00
bool FMetasoundFrontendRegistryContainer : : DoesDataTypeSupportLiteralType ( FName InDataType , Metasound : : ELiteralArgType InLiteralType ) const
{
if ( ! DataTypeRegistry . Contains ( InDataType ) )
{
ensureAlwaysMsgf ( false , TEXT ( " couldn't find DataType %s in the registry. " ) , * InDataType . ToString ( ) ) ;
return false ;
}
const FDataTypeRegistryElement & DataTypeInfo = DataTypeRegistry [ InDataType ] ;
switch ( InLiteralType )
{
case Metasound : : ELiteralArgType : : Boolean :
{
return DataTypeInfo . Info . bIsBoolParsable ;
}
case Metasound : : ELiteralArgType : : Integer :
{
return DataTypeInfo . Info . bIsIntParsable ;
}
case Metasound : : ELiteralArgType : : Float :
{
return DataTypeInfo . Info . bIsFloatParsable ;
}
case Metasound : : ELiteralArgType : : String :
{
return DataTypeInfo . Info . bIsStringParsable ;
}
2020-08-13 19:07:41 -04:00
case Metasound : : ELiteralArgType : : UObjectProxy :
{
return DataTypeInfo . Info . bIsProxyParsable ;
}
case Metasound : : ELiteralArgType : : UObjectProxyArray :
{
return DataTypeInfo . Info . bIsProxyArrayParsable ;
}
2020-07-20 00:05:22 -04:00
case Metasound : : ELiteralArgType : : None :
{
return DataTypeInfo . Info . bIsConstructableWithSettings | | DataTypeInfo . Info . bIsDefaultConstructible ;
}
case Metasound : : ELiteralArgType : : Invalid :
default :
{
return false ;
}
}
}
2020-08-13 19:07:41 -04:00
bool FMetasoundFrontendRegistryContainer : : RegisterDataType ( const : : Metasound : : FDataTypeRegistryInfo & InDataInfo , : : Metasound : : FDataTypeConstructorCallbacks & & InCallbacks )
2020-07-20 00:05:22 -04:00
{
if ( ! ensureAlwaysMsgf ( ! DataTypeRegistry . Contains ( InDataInfo . DataTypeName ) , TEXT ( " Name collision when trying to register Metasound Data Type %s! Make sure that you created a unique name for your data type, and that REGISTER_METASOUND_DATATYPE isn't called in a public header. " ) , * InDataInfo . DataTypeName . ToString ( ) ) )
{
// todo: capture callstack for previous declaration for non-shipping builds to help clarify who already registered this name for a type.
return false ;
}
else
{
2020-08-13 19:07:41 -04:00
FDataTypeRegistryElement InElement = { MoveTemp ( InCallbacks ) , InDataInfo } ;
2020-07-20 00:05:22 -04:00
DataTypeRegistry . Add ( InDataInfo . DataTypeName , MoveTemp ( InElement ) ) ;
UE_LOG ( LogTemp , Display , TEXT ( " Registered Metasound Datatype %s. " ) , * InDataInfo . DataTypeName . ToString ( ) ) ;
return true ;
}
}
bool FMetasoundFrontendRegistryContainer : : RegisterExternalNode ( FNodeGetterCallback & & InCallback )
{
2020-08-24 10:57:03 -04:00
using FVertexInterface = Metasound : : FVertexInterface ;
using FInputVertexInterface = Metasound : : FInputVertexInterface ;
using FOutputVertexInterface = Metasound : : FOutputVertexInterface ;
2020-07-20 00:05:22 -04:00
Metasound : : FNodeInitData DummyInitData ;
TUniquePtr < Metasound : : INode > DummyNodePtr = InCallback ( DummyInitData ) ;
if ( ! ensureAlwaysMsgf ( DummyNodePtr . IsValid ( ) , TEXT ( " Invalid getter registered! " ) ) )
{
return false ;
}
Metasound : : INode & DummyNode = * DummyNodePtr ;
// First, build the key.
const FName NodeName = DummyNode . GetClassName ( ) ;
2020-08-24 10:57:03 -04:00
const FInputVertexInterface & Inputs = DummyNode . GetDefaultVertexInterface ( ) . GetInputInterface ( ) ;
const FOutputVertexInterface & Outputs = DummyNode . GetDefaultVertexInterface ( ) . GetOutputInterface ( ) ;
2020-07-20 00:05:22 -04:00
// Construct a hash using a combination of the class name, input names and output names.
uint32 NodeHash = FCrc : : StrCrc32 ( * NodeName . ToString ( ) ) ;
TArray < FName > InputTypes ;
TArray < FName > OutputTypes ;
for ( auto & InputTuple : Inputs )
{
2020-08-24 10:57:03 -04:00
NodeHash = HashCombine ( NodeHash , FCrc : : StrCrc32 ( * InputTuple . Value . GetVertexName ( ) ) ) ;
InputTypes . Add ( InputTuple . Value . GetDataTypeName ( ) ) ;
2020-07-20 00:05:22 -04:00
}
for ( auto & OutputTuple : Outputs )
{
2020-08-24 10:57:03 -04:00
NodeHash = HashCombine ( NodeHash , FCrc : : StrCrc32 ( * OutputTuple . Value . GetVertexName ( ) ) ) ;
OutputTypes . Add ( OutputTuple . Value . GetDataTypeName ( ) ) ;
2020-07-20 00:05:22 -04:00
}
FNodeRegistryKey InKey = { NodeName , NodeHash } ;
// check to see if an identical node was already registered, and log
ensureAlwaysMsgf ( ! ExternalNodeRegistry . Contains ( InKey ) , TEXT ( " Node with identical name, inputs and outputs to node %s was already registered. The previously registered node will be overwritten. This could also happen because METASOUND_REGISTER_NODE is in a public header. " ) , * NodeName . ToString ( ) ) ;
UE_LOG ( LogTemp , Display , TEXT ( " Registered Metasound Node %s " ) , * NodeName . ToString ( ) ) ;
const bool bShouldLogInputsAndOutputs = true ;
if ( bShouldLogInputsAndOutputs )
{
UE_LOG ( LogTemp , Display , TEXT ( " %d inputs: " ) , Inputs . Num ( ) ) ;
for ( auto & InputTuple : Inputs )
{
2020-08-24 10:57:03 -04:00
UE_LOG ( LogTemp , Display , TEXT ( " %s (of type %s) " ) , * InputTuple . Value . GetVertexName ( ) , * InputTuple . Value . GetDataTypeName ( ) . ToString ( ) ) ;
2020-07-20 00:05:22 -04:00
}
UE_LOG ( LogTemp , Display , TEXT ( " %d outputs: " ) , Outputs . Num ( ) ) ;
for ( auto & OutputTuple : Outputs )
{
2020-08-24 10:57:03 -04:00
UE_LOG ( LogTemp , Display , TEXT ( " %s (of type %s) " ) , * OutputTuple . Value . GetVertexName ( ) , * OutputTuple . Value . GetDataTypeName ( ) . ToString ( ) ) ;
2020-07-20 00:05:22 -04:00
}
}
FNodeRegistryElement RegistryElement = FNodeRegistryElement ( MoveTemp ( InCallback ) ) ;
RegistryElement . InputTypes = MoveTemp ( InputTypes ) ;
RegistryElement . OutputTypes = MoveTemp ( OutputTypes ) ;
ExternalNodeRegistry . Add ( MoveTemp ( InKey ) , MoveTemp ( RegistryElement ) ) ;
return true ;
}
TArray < FName > FMetasoundFrontendRegistryContainer : : GetAllValidDataTypes ( )
{
TArray < FName > OutDataTypes ;
for ( auto & DataTypeTuple : DataTypeRegistry )
{
OutDataTypes . Add ( DataTypeTuple . Key ) ;
}
return OutDataTypes ;
}
bool FMetasoundFrontendRegistryContainer : : GetInfoForDataType ( FName InDataType , Metasound : : FDataTypeRegistryInfo & OutInfo )
{
if ( ! DataTypeRegistry . Contains ( InDataType ) )
{
return false ;
}
else
{
OutInfo = DataTypeRegistry [ InDataType ] . Info ;
return true ;
}
}