2020-12-04 11:19:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundFrontendGraph.h"
# include "Algo/AllOf.h"
# include "Algo/AnyOf.h"
2021-05-20 19:33:21 -04:00
# include "Algo/TopologicalSort.h"
2020-12-04 11:19:17 -04:00
# include "Algo/Transform.h"
# include "CoreMinimal.h"
# include "MetasoundFrontend.h"
2021-08-19 09:59:27 -04:00
# include "MetasoundFrontendDataTypeRegistry.h"
2023-10-16 18:42:49 -04:00
# include "MetasoundFrontendDocumentIdGenerator.h"
2022-08-10 14:18:10 -04:00
# include "MetasoundFrontendNodeTemplateRegistry.h"
2023-09-13 14:21:35 -04:00
# include "MetasoundFrontendProxyDataCache.h"
2022-08-10 14:18:10 -04:00
# include "MetasoundFrontendRegistries.h"
2020-12-04 11:19:17 -04:00
# include "MetasoundGraph.h"
2021-09-07 17:07:54 -04:00
# include "MetasoundLiteralNode.h"
2021-01-13 10:48:59 -04:00
# include "MetasoundLog.h"
2020-12-04 11:19:17 -04:00
# include "MetasoundNodeInterface.h"
namespace Metasound
{
2021-05-28 14:09:45 -04:00
namespace FrontendGraphPrivate
{
FNodeInitData CreateNodeInitData ( const FMetasoundFrontendNode & InNode )
{
FNodeInitData InitData ;
2023-10-17 19:51:15 -04:00
InitData . InstanceName = InNode . Name ;
2021-09-13 14:14:37 -04:00
InitData . InstanceID = InNode . GetID ( ) ;
2021-05-28 14:09:45 -04:00
return InitData ;
}
}
2021-05-20 19:33:21 -04:00
2021-01-28 19:02:51 -04:00
FFrontendGraph : : FFrontendGraph ( const FString & InInstanceName , const FGuid & InInstanceID )
: FGraph ( InInstanceName , InInstanceID )
2020-12-04 11:19:17 -04:00
{
}
2021-09-13 14:14:37 -04:00
void FFrontendGraph : : AddInputNode ( FGuid InDependencyId , int32 InIndex , const FVertexName & InVertexName , TSharedPtr < const INode > InNode )
2020-12-04 11:19:17 -04:00
{
if ( InNode . IsValid ( ) )
{
// There shouldn't be duplicate IDs.
check ( ! InputNodes . Contains ( InIndex ) ) ;
// Input nodes need an extra Index value to keep track of their position in the graph's inputs.
InputNodes . Add ( InIndex , InNode . Get ( ) ) ;
2021-09-13 14:14:37 -04:00
AddInputDataDestination ( * InNode , InVertexName ) ;
2021-05-20 19:33:21 -04:00
AddNode ( InDependencyId , InNode ) ;
2020-12-04 11:19:17 -04:00
}
}
2021-09-13 14:14:37 -04:00
void FFrontendGraph : : AddOutputNode ( FGuid InNodeID , int32 InIndex , const FVertexName & InVertexName , TSharedPtr < const INode > InNode )
2020-12-04 11:19:17 -04:00
{
if ( InNode . IsValid ( ) )
{
// There shouldn't be duplicate IDs.
check ( ! OutputNodes . Contains ( InIndex ) ) ;
// Output nodes need an extra Index value to keep track of their position in the graph's inputs.
OutputNodes . Add ( InIndex , InNode . Get ( ) ) ;
2021-09-13 14:14:37 -04:00
AddOutputDataSource ( * InNode , InVertexName ) ;
2021-05-20 19:33:21 -04:00
AddNode ( InNodeID , InNode ) ;
2020-12-04 11:19:17 -04:00
}
}
const INode * FFrontendGraph : : FindInputNode ( int32 InIndex ) const
{
2021-05-20 19:33:21 -04:00
const INode * const * NodePtr = InputNodes . Find ( InIndex ) ;
2020-12-04 11:19:17 -04:00
if ( nullptr ! = NodePtr )
{
return * NodePtr ;
}
return nullptr ;
}
const INode * FFrontendGraph : : FindOutputNode ( int32 InIndex ) const
{
2021-05-20 19:33:21 -04:00
const INode * const * NodePtr = OutputNodes . Find ( InIndex ) ;
2020-12-04 11:19:17 -04:00
if ( nullptr ! = NodePtr )
{
return * NodePtr ;
}
return nullptr ;
}
bool FFrontendGraph : : OwnsAllReferencedNodes ( ) const
{
2023-06-23 14:53:34 -04:00
// This function is deprecated in 5.3 and is no longer functional.
2020-12-04 11:19:17 -04:00
return true ;
}
2023-09-13 14:21:35 -04:00
TUniquePtr < INode > FFrontendGraphBuilder : : CreateVariableNode ( const FBuildContext & InContext , const FMetasoundFrontendNode & InNode , const FMetasoundFrontendGraph & InGraph )
2020-12-04 11:19:17 -04:00
{
2021-10-12 21:21:22 -04:00
using namespace Metasound : : Frontend ;
const FMetasoundFrontendVariable * FrontendVariable = FindVariableForVariableNode ( InNode , InGraph ) ;
if ( nullptr ! = FrontendVariable )
{
IDataTypeRegistry & DataTypeRegistry = IDataTypeRegistry : : Get ( ) ;
const bool IsLiteralParsableByDataType = DataTypeRegistry . IsLiteralTypeSupported ( FrontendVariable - > TypeName , FrontendVariable - > Literal . GetType ( ) ) ;
if ( IsLiteralParsableByDataType )
{
2023-09-13 14:21:35 -04:00
FLiteral Literal = FrontendVariable - > Literal . ToLiteral ( FrontendVariable - > TypeName , & InContext . DataTypeRegistry , & InContext . ProxyDataCache ) ;
2021-10-12 21:21:22 -04:00
FVariableNodeConstructorParams InitParams =
{
InNode . Name ,
InNode . GetID ( ) ,
MoveTemp ( Literal )
} ;
return DataTypeRegistry . CreateVariableNode ( FrontendVariable - > TypeName , MoveTemp ( InitParams ) ) ;
}
else
{
2022-05-02 12:35:54 -04:00
UE_LOG ( LogMetaSound , Error , TEXT ( " Cannot create variable node [NodeID:%s]. [Variable:%s] cannot be constructed with the provided literal type. " ) , * InNode . GetID ( ) . ToString ( ) , * FrontendVariable - > Name . ToString ( ) ) ;
2021-10-12 21:21:22 -04:00
}
}
else
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Cannot create variable node [NodeID:%s]. No variable found for variable node. " ) , * InNode . GetID ( ) . ToString ( ) ) ;
}
return TUniquePtr < INode > ( nullptr ) ;
}
2023-09-13 14:21:35 -04:00
TUniquePtr < INode > FFrontendGraphBuilder : : CreateInputNode ( const FBuildContext & InContext , const FMetasoundFrontendNode & InNode , const FMetasoundFrontendClass & InClass , const FMetasoundFrontendClassInput & InOwningGraphClassInput )
2020-12-04 11:19:17 -04:00
{
2021-08-19 09:59:27 -04:00
using namespace Metasound : : Frontend ;
2021-02-24 18:37:19 -04:00
const FMetasoundFrontendLiteral * FrontendLiteral = FindInputLiteralForInputNode ( InNode , InClass , InOwningGraphClassInput ) ;
2021-01-13 10:48:59 -04:00
2021-02-24 18:37:19 -04:00
if ( nullptr ! = FrontendLiteral )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
if ( ensure ( InNode . Interface . Inputs . Num ( ) = = 1 ) )
{
2021-08-19 09:59:27 -04:00
IDataTypeRegistry & DataTypeRegistry = IDataTypeRegistry : : Get ( ) ;
2021-01-13 10:48:59 -04:00
const FMetasoundFrontendVertex & InputVertex = InNode . Interface . Inputs [ 0 ] ;
2021-08-19 09:59:27 -04:00
const bool IsLiteralParsableByDataType = DataTypeRegistry . IsLiteralTypeSupported ( InputVertex . TypeName , FrontendLiteral - > GetType ( ) ) ;
2021-01-13 10:48:59 -04:00
if ( IsLiteralParsableByDataType )
{
2023-09-13 14:21:35 -04:00
FLiteral Literal = FrontendLiteral - > ToLiteral ( InputVertex . TypeName , & InContext . DataTypeRegistry , & InContext . ProxyDataCache ) ;
2021-01-13 10:48:59 -04:00
FInputNodeConstructorParams InitParams =
{
InNode . Name ,
2021-09-13 14:14:37 -04:00
InNode . GetID ( ) ,
2021-01-13 10:48:59 -04:00
InputVertex . Name ,
2023-04-05 17:38:47 -04:00
MoveTemp ( Literal )
2021-01-13 10:48:59 -04:00
} ;
2022-09-13 21:51:22 -04:00
ensureAlwaysMsgf ( InOwningGraphClassInput . AccessType ! = EMetasoundFrontendVertexAccessType : : Unset , TEXT ( " Graph Class Input cannot be set to access type of 'Unset' " ) ) ;
2022-07-18 17:14:25 -04:00
if ( InOwningGraphClassInput . AccessType = = EMetasoundFrontendVertexAccessType : : Reference )
{
return DataTypeRegistry . CreateInputNode ( InputVertex . TypeName , MoveTemp ( InitParams ) ) ;
}
else // InOwningGraphClassInput.AccessType == EMetasoundFrontendVertexAccessType::Value
{
return DataTypeRegistry . CreateConstructorInputNode ( InputVertex . TypeName , MoveTemp ( InitParams ) ) ;
}
2021-01-13 10:48:59 -04:00
}
else
{
2021-09-13 14:14:37 -04:00
UE_LOG ( LogMetaSound , Error , TEXT ( " Cannot create input node [NodeID:%s]. [Vertex:%s] cannot be constructed with the provided literal type. " ) , * InNode . GetID ( ) . ToString ( ) , * InputVertex . Name . ToString ( ) ) ;
2021-01-13 10:48:59 -04:00
}
}
}
else
{
2021-09-13 14:14:37 -04:00
UE_LOG ( LogMetaSound , Error , TEXT ( " Cannot create input node [NodeID:%s]. No default literal set for input node. " ) , * InNode . GetID ( ) . ToString ( ) ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
return TUniquePtr < INode > ( nullptr ) ;
2020-12-04 11:19:17 -04:00
}
2023-09-13 14:21:35 -04:00
TUniquePtr < INode > FFrontendGraphBuilder : : CreateOutputNode ( const FBuildContext & InContext , const FMetasoundFrontendNode & InNode , const FMetasoundFrontendClass & InClass , FBuildGraphContext & InGraphContext , const TSet < FNodeIDVertexID > & InEdgeDestinations )
2020-12-04 11:19:17 -04:00
{
2021-08-19 09:59:27 -04:00
using namespace Metasound : : Frontend ;
2021-07-27 15:36:03 -04:00
check ( InClass . Metadata . GetType ( ) = = EMetasoundFrontendClassType : : Output ) ;
2021-01-13 10:48:59 -04:00
check ( InNode . ClassID = = InClass . ID ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( ensure ( InNode . Interface . Outputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendVertex & OutputVertex = InNode . Interface . Outputs [ 0 ] ;
FOutputNodeConstructorParams InitParams =
{
InNode . Name ,
2021-09-13 14:14:37 -04:00
InNode . GetID ( ) ,
2021-05-28 14:09:45 -04:00
OutputVertex . Name
2021-01-13 10:48:59 -04:00
} ;
2021-05-28 14:09:45 -04:00
{
const FNodeInitData InitData = FrontendGraphPrivate : : CreateNodeInitData ( InNode ) ;
2023-09-13 14:21:35 -04:00
TArray < FDefaultLiteralData > DefaultLiteralData = GetInputDefaultLiteralData ( InContext , InNode , InitData , InEdgeDestinations ) ;
2021-09-07 17:07:54 -04:00
for ( FDefaultLiteralData & Data : DefaultLiteralData )
2021-05-28 14:09:45 -04:00
{
2021-09-13 14:14:37 -04:00
InGraphContext . DefaultInputs . Emplace ( FNodeIDVertexID { InNode . GetID ( ) , Data . DestinationVertexID } , MoveTemp ( Data ) ) ;
2021-05-28 14:09:45 -04:00
}
}
2022-07-18 17:14:25 -04:00
ensure ( InClass . Interface . Outputs . Num ( ) = = 1 ) ;
if ( InClass . Interface . Outputs [ 0 ] . AccessType = = EMetasoundFrontendVertexAccessType : : Reference )
{
return IDataTypeRegistry : : Get ( ) . CreateOutputNode ( OutputVertex . TypeName , MoveTemp ( InitParams ) ) ;
}
else // InClass.Interface.Outputs[0].AccessType == EMetasoundFrontendVertexAccessType::Value
{
return IDataTypeRegistry : : Get ( ) . CreateConstructorOutputNode ( OutputVertex . TypeName , MoveTemp ( InitParams ) ) ;
}
2021-01-13 10:48:59 -04:00
}
return TUniquePtr < INode > ( nullptr ) ;
2020-12-04 11:19:17 -04:00
}
2023-09-13 14:21:35 -04:00
TUniquePtr < INode > FFrontendGraphBuilder : : CreateExternalNode ( const FBuildContext & InContext , const FMetasoundFrontendNode & InNode , const FMetasoundFrontendClass & InClass , FBuildGraphContext & InGraphContext , const TSet < FNodeIDVertexID > & InEdgeDestinations )
2020-12-04 11:19:17 -04:00
{
2022-08-10 14:18:10 -04:00
using namespace Frontend ;
2021-01-13 10:48:59 -04:00
check ( InNode . ClassID = = InClass . ID ) ;
2020-12-04 11:19:17 -04:00
2021-05-28 14:09:45 -04:00
const FNodeInitData InitData = FrontendGraphPrivate : : CreateNodeInitData ( InNode ) ;
{
2023-09-13 14:21:35 -04:00
TArray < FDefaultLiteralData > DefaultLiteralData = GetInputDefaultLiteralData ( InContext , InNode , InitData , InEdgeDestinations ) ;
2021-09-07 17:07:54 -04:00
for ( FDefaultLiteralData & Data : DefaultLiteralData )
2021-05-28 14:09:45 -04:00
{
2022-02-25 09:40:33 -05:00
InGraphContext . DefaultInputs . Emplace ( FNodeIDVertexID { InNode . GetID ( ) , Data . DestinationVertexID } , MoveTemp ( Data ) ) ;
2021-05-28 14:09:45 -04:00
}
}
2021-01-28 19:02:51 -04:00
2021-01-13 10:48:59 -04:00
// TODO: handle check to see if node interface conforms to class interface here.
// TODO: check to see if external object supports class interface.
2020-12-04 11:19:17 -04:00
2023-10-12 16:37:45 -04:00
const FNodeRegistryKey Key = FNodeRegistryKey ( InClass . Metadata ) ;
2021-06-14 16:46:19 -04:00
return FMetasoundFrontendRegistryContainer : : Get ( ) - > CreateNode ( Key , InitData ) ;
2021-01-13 10:48:59 -04:00
}
2021-05-28 14:09:45 -04:00
const FMetasoundFrontendClassInput * FFrontendGraphBuilder : : FindClassInputForInputNode ( const FMetasoundFrontendGraphClass & InOwningGraph , const FMetasoundFrontendNode & InInputNode , int32 & OutClassInputIndex )
2021-01-13 10:48:59 -04:00
{
OutClassInputIndex = INDEX_NONE ;
2021-05-20 19:33:21 -04:00
// Input nodes should have exactly one input.
2021-01-13 10:48:59 -04:00
if ( ensure ( InInputNode . Interface . Inputs . Num ( ) = = 1 ) )
{
const FName & TypeName = InInputNode . Interface . Inputs [ 0 ] . TypeName ;
auto IsMatchingInput = [ & ] ( const FMetasoundFrontendClassInput & GraphInput )
{
2021-09-13 14:14:37 -04:00
return ( InInputNode . GetID ( ) = = GraphInput . NodeID ) ;
2021-01-13 10:48:59 -04:00
} ;
OutClassInputIndex = InOwningGraph . Interface . Inputs . IndexOfByPredicate ( IsMatchingInput ) ;
if ( INDEX_NONE ! = OutClassInputIndex )
{
return & InOwningGraph . Interface . Inputs [ OutClassInputIndex ] ;
}
}
return nullptr ;
}
2021-05-28 14:09:45 -04:00
const FMetasoundFrontendClassOutput * FFrontendGraphBuilder : : FindClassOutputForOutputNode ( const FMetasoundFrontendGraphClass & InOwningGraph , const FMetasoundFrontendNode & InOutputNode , int32 & OutClassOutputIndex )
2021-01-13 10:48:59 -04:00
{
OutClassOutputIndex = INDEX_NONE ;
2021-05-20 19:33:21 -04:00
// Output nodes should have exactly one output
2021-01-13 10:48:59 -04:00
if ( ensure ( InOutputNode . Interface . Outputs . Num ( ) = = 1 ) )
{
const FName & TypeName = InOutputNode . Interface . Outputs [ 0 ] . TypeName ;
auto IsMatchingOutput = [ & ] ( const FMetasoundFrontendClassOutput & GraphOutput )
{
2021-09-13 14:14:37 -04:00
return ( InOutputNode . GetID ( ) = = GraphOutput . NodeID ) ;
2021-01-13 10:48:59 -04:00
} ;
OutClassOutputIndex = InOwningGraph . Interface . Outputs . IndexOfByPredicate ( IsMatchingOutput ) ;
if ( INDEX_NONE ! = OutClassOutputIndex )
{
return & InOwningGraph . Interface . Outputs [ OutClassOutputIndex ] ;
}
}
return nullptr ;
}
2021-10-12 21:21:22 -04:00
const FMetasoundFrontendVariable * FFrontendGraphBuilder : : FindVariableForVariableNode ( const FMetasoundFrontendNode & InVariableNode , const FMetasoundFrontendGraph & InGraph )
{
const FGuid & DesiredID = InVariableNode . GetID ( ) ;
return InGraph . Variables . FindByPredicate ( [ & ] ( const FMetasoundFrontendVariable & InVar ) { return InVar . VariableNodeID = = DesiredID ; } ) ;
}
2021-01-20 17:26:40 -04:00
2021-05-28 14:09:45 -04:00
const FMetasoundFrontendLiteral * FFrontendGraphBuilder : : FindInputLiteralForInputNode ( const FMetasoundFrontendNode & InInputNode , const FMetasoundFrontendClass & InInputNodeClass , const FMetasoundFrontendClassInput & InOwningGraphClassInput )
2021-01-13 10:48:59 -04:00
{
// Default value priority is:
// 1. A value set directly on the node
// 2. A default value of the owning graph
// 3. A default value on the input node class.
2021-02-24 18:37:19 -04:00
const FMetasoundFrontendLiteral * Literal = nullptr ;
2021-01-13 10:48:59 -04:00
// Check for default value directly on node.
if ( ensure ( InInputNode . Interface . Inputs . Num ( ) = = 1 ) )
{
const FMetasoundFrontendVertex & InputVertex = InInputNode . Interface . Inputs [ 0 ] ;
2021-02-24 18:37:19 -04:00
// Find input literal matching VerteXID
const FMetasoundFrontendVertexLiteral * VertexLiteral = InInputNode . InputLiterals . FindByPredicate (
[ & ] ( const FMetasoundFrontendVertexLiteral & InVertexLiteral )
{
return InVertexLiteral . VertexID = = InputVertex . VertexID ;
}
) ;
if ( nullptr ! = VertexLiteral )
2021-01-13 10:48:59 -04:00
{
2021-02-24 18:37:19 -04:00
Literal = & VertexLiteral - > Value ;
2021-01-13 10:48:59 -04:00
}
}
// Check for default value on owning graph.
2021-02-24 18:37:19 -04:00
if ( nullptr = = Literal )
2021-01-13 10:48:59 -04:00
{
2021-02-24 18:37:19 -04:00
// Find Class Default that is not invalid
if ( InOwningGraphClassInput . DefaultLiteral . IsValid ( ) )
2021-01-13 10:48:59 -04:00
{
2021-02-24 18:37:19 -04:00
Literal = & InOwningGraphClassInput . DefaultLiteral ;
2021-01-13 10:48:59 -04:00
}
}
// Check for default value on input node class
2021-02-24 18:37:19 -04:00
if ( nullptr = = Literal & & ensure ( InInputNodeClass . Interface . Inputs . Num ( ) = = 1 ) )
2021-01-13 10:48:59 -04:00
{
const FMetasoundFrontendClassInput & InputNodeClassInput = InInputNodeClass . Interface . Inputs [ 0 ] ;
2021-02-24 18:37:19 -04:00
if ( InputNodeClassInput . DefaultLiteral . IsValid ( ) )
2021-01-13 10:48:59 -04:00
{
2021-02-24 18:37:19 -04:00
Literal = & InputNodeClassInput . DefaultLiteral ;
2021-01-13 10:48:59 -04:00
}
}
2021-02-24 18:37:19 -04:00
return Literal ;
2020-12-04 11:19:17 -04:00
}
2023-09-13 14:21:35 -04:00
bool FFrontendGraphBuilder : : AddNodesToGraph ( FBuildGraphContext & InGraphContext )
2020-12-04 11:19:17 -04:00
{
2022-02-25 19:46:58 -05:00
TSet < FNodeIDVertexID > GraphEdgeDestinations ;
2022-02-25 09:40:33 -05:00
const TArray < FMetasoundFrontendEdge > & GraphEdges = InGraphContext . GraphClass . Graph . Edges ;
Algo : : Transform ( GraphEdges , GraphEdgeDestinations , [ ] ( const FMetasoundFrontendEdge & Edge )
{
2022-02-25 19:46:58 -05:00
return FNodeIDVertexID { Edge . ToNodeID , Edge . ToVertexID } ;
2022-02-25 09:40:33 -05:00
} ) ;
2021-05-28 14:09:45 -04:00
for ( const FMetasoundFrontendNode & Node : InGraphContext . GraphClass . Graph . Nodes )
2020-12-04 11:19:17 -04:00
{
2021-05-28 14:09:45 -04:00
const FMetasoundFrontendClass * NodeClass = InGraphContext . BuildContext . FrontendClasses . FindRef ( Node . ClassID ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( ensure ( nullptr ! = NodeClass ) )
{
2021-07-27 15:36:03 -04:00
switch ( NodeClass - > Metadata . GetType ( ) )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
case EMetasoundFrontendClassType : : Input :
2021-05-28 14:09:45 -04:00
{
int32 InputIndex = INDEX_NONE ;
const FMetasoundFrontendClassInput * ClassInput = FindClassInputForInputNode ( InGraphContext . GraphClass , Node , InputIndex ) ;
if ( ( nullptr ! = ClassInput ) & & ( INDEX_NONE ! = InputIndex ) )
2021-01-13 10:48:59 -04:00
{
2023-09-13 14:21:35 -04:00
TSharedPtr < const INode > InputNode ( CreateInputNode ( InGraphContext . BuildContext , Node , * NodeClass , * ClassInput ) . Release ( ) ) ;
2021-09-13 14:14:37 -04:00
InGraphContext . Graph - > AddInputNode ( Node . GetID ( ) , InputIndex , ClassInput - > Name , InputNode ) ;
2021-01-13 10:48:59 -04:00
}
2021-05-28 14:09:45 -04:00
else
{
const FString GraphClassIDString = InGraphContext . GraphClass . ID . ToString ( ) ;
2023-02-15 16:55:49 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to match input node [NodeID:%s, NodeName:%s] to owning graph [ClassID:%s] input. " ) , * InGraphContext . BuildContext . DebugAssetName , * Node . GetID ( ) . ToString ( ) , * Node . Name . ToString ( ) , * GraphClassIDString ) ;
2022-03-04 04:25:00 -05:00
return false ;
2021-05-28 14:09:45 -04:00
}
}
break ;
2021-01-13 10:48:59 -04:00
case EMetasoundFrontendClassType : : Output :
2021-05-28 14:09:45 -04:00
{
int32 OutputIndex = INDEX_NONE ;
const FMetasoundFrontendClassOutput * ClassOutput = FindClassOutputForOutputNode ( InGraphContext . GraphClass , Node , OutputIndex ) ;
if ( ( nullptr ! = ClassOutput ) & & ( INDEX_NONE ! = OutputIndex ) )
2021-01-13 10:48:59 -04:00
{
2023-09-13 14:21:35 -04:00
TSharedPtr < const INode > OutputNode ( CreateOutputNode ( InGraphContext . BuildContext , Node , * NodeClass , InGraphContext , GraphEdgeDestinations ) . Release ( ) ) ;
2021-09-13 14:14:37 -04:00
InGraphContext . Graph - > AddOutputNode ( Node . GetID ( ) , OutputIndex , ClassOutput - > Name , OutputNode ) ;
2021-01-13 10:48:59 -04:00
}
2021-05-28 14:09:45 -04:00
else
{
const FString GraphClassIDString = InGraphContext . GraphClass . ID . ToString ( ) ;
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to match output node [NodeID:%s, NodeName:%s] to owning graph [ClassID:%s] output. " ) , * InGraphContext . BuildContext . DebugAssetName , * Node . GetID ( ) . ToString ( ) , * Node . Name . ToString ( ) , * GraphClassIDString ) ;
return false ;
2021-05-28 14:09:45 -04:00
}
}
break ;
2021-01-13 10:48:59 -04:00
2021-05-20 19:33:21 -04:00
case EMetasoundFrontendClassType : : Graph :
2021-05-28 14:09:45 -04:00
{
const TSharedPtr < const INode > SubgraphPtr = InGraphContext . BuildContext . Graphs . FindRef ( Node . ClassID ) ;
2021-05-20 19:33:21 -04:00
2021-05-28 14:09:45 -04:00
if ( SubgraphPtr . IsValid ( ) )
{
2021-09-13 14:14:37 -04:00
InGraphContext . Graph - > AddNode ( Node . GetID ( ) , SubgraphPtr ) ;
2021-05-20 19:33:21 -04:00
}
2021-05-28 14:09:45 -04:00
else
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to find subgraph for node [NodeID:%s, NodeName:%s, ClassID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * Node . GetID ( ) . ToString ( ) , * Node . Name . ToString ( ) , * Node . ClassID . ToString ( ) ) ;
return false ;
2021-05-28 14:09:45 -04:00
}
}
break ;
2021-05-20 19:33:21 -04:00
2021-10-12 21:21:22 -04:00
case EMetasoundFrontendClassType : : Literal :
{
checkNoEntry ( ) ; // Unsupported.
2022-03-04 04:25:00 -05:00
return false ;
2021-10-12 21:21:22 -04:00
}
case EMetasoundFrontendClassType : : Variable :
{
2023-09-13 14:21:35 -04:00
TSharedPtr < const INode > VariableNode ( CreateVariableNode ( InGraphContext . BuildContext , Node , InGraphContext . GraphClass . Graph ) . Release ( ) ) ;
2021-10-12 21:21:22 -04:00
InGraphContext . Graph - > AddNode ( Node . GetID ( ) , VariableNode ) ;
}
break ;
2022-08-10 14:18:10 -04:00
// Templates, variable accessors and mutators are
// constructed with the same parameters as external nodes.
case EMetasoundFrontendClassType : : Template :
2021-10-12 21:21:22 -04:00
case EMetasoundFrontendClassType : : VariableAccessor :
2021-11-18 14:37:34 -05:00
case EMetasoundFrontendClassType : : VariableDeferredAccessor :
2021-10-12 21:21:22 -04:00
case EMetasoundFrontendClassType : : VariableMutator :
2021-05-20 19:33:21 -04:00
case EMetasoundFrontendClassType : : External :
2021-01-13 10:48:59 -04:00
default :
2021-05-28 14:09:45 -04:00
{
2023-09-13 14:21:35 -04:00
TSharedPtr < const INode > ExternalNode ( CreateExternalNode ( InGraphContext . BuildContext , Node , * NodeClass , InGraphContext , GraphEdgeDestinations ) . Release ( ) ) ;
2021-09-13 14:14:37 -04:00
InGraphContext . Graph - > AddNode ( Node . GetID ( ) , ExternalNode ) ;
2021-05-28 14:09:45 -04:00
}
break ;
2020-12-04 11:19:17 -04:00
}
}
}
2022-03-04 04:25:00 -05:00
return true ;
2020-12-04 11:19:17 -04:00
}
2022-03-04 04:25:00 -05:00
bool FFrontendGraphBuilder : : AddEdgesToGraph ( FBuildGraphContext & InGraphContext )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
// Pair of frontend node and core node. The frontend node can be one of
// several types.
struct FCoreNodeAndFrontendVertex
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
const INode * Node = nullptr ;
const FMetasoundFrontendVertex * Vertex = nullptr ;
} ;
2020-12-04 11:19:17 -04:00
2021-02-24 18:37:19 -04:00
TMap < FNodeIDVertexID , FCoreNodeAndFrontendVertex > NodeSourcesByID ;
TMap < FNodeIDVertexID , FCoreNodeAndFrontendVertex > NodeDestinationsByID ;
2021-01-13 10:48:59 -04:00
2021-02-24 18:37:19 -04:00
// Add nodes to NodeID/VertexID map
2021-05-28 14:09:45 -04:00
for ( const FMetasoundFrontendNode & Node : InGraphContext . GraphClass . Graph . Nodes )
2021-01-13 10:48:59 -04:00
{
2021-09-13 14:14:37 -04:00
const INode * CoreNode = InGraphContext . Graph - > FindNode ( Node . GetID ( ) ) ;
2021-01-13 10:48:59 -04:00
if ( nullptr = = CoreNode )
2020-12-04 11:19:17 -04:00
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Warning , TEXT ( " Metasound '%s': Could not find referenced node [Name:%s, NodeID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * Node . Name . ToString ( ) , * Node . GetID ( ) . ToString ( ) ) ;
return false ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
for ( const FMetasoundFrontendVertex & Vertex : Node . Interface . Inputs )
2020-12-04 11:19:17 -04:00
{
2021-09-13 14:14:37 -04:00
NodeDestinationsByID . Add ( FNodeIDVertexID ( Node . GetID ( ) , Vertex . VertexID ) , FCoreNodeAndFrontendVertex ( { CoreNode , & Vertex } ) ) ;
2021-01-13 10:48:59 -04:00
}
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
for ( const FMetasoundFrontendVertex & Vertex : Node . Interface . Outputs )
{
2021-09-13 14:14:37 -04:00
NodeSourcesByID . Add ( FNodeIDVertexID ( Node . GetID ( ) , Vertex . VertexID ) , FCoreNodeAndFrontendVertex ( { CoreNode , & Vertex } ) ) ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
} ;
2020-12-04 11:19:17 -04:00
2021-05-28 14:09:45 -04:00
for ( const FMetasoundFrontendEdge & Edge : InGraphContext . GraphClass . Graph . Edges )
2020-12-04 11:19:17 -04:00
{
2021-02-24 18:37:19 -04:00
const FNodeIDVertexID DestinationKey ( Edge . ToNodeID , Edge . ToVertexID ) ;
2021-01-13 10:48:59 -04:00
const FCoreNodeAndFrontendVertex * DestinationNodeAndVertex = NodeDestinationsByID . Find ( DestinationKey ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( nullptr = = DestinationNodeAndVertex )
2020-12-04 11:19:17 -04:00
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to add edge. Could not find destination [NodeID:%s, VertexID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * Edge . ToNodeID . ToString ( ) , * Edge . ToVertexID . ToString ( ) ) ;
return false ;
2020-12-04 11:19:17 -04:00
}
2021-01-13 10:48:59 -04:00
if ( nullptr = = DestinationNodeAndVertex - > Node )
2020-12-04 11:19:17 -04:00
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Warning , TEXT ( " MetaSound '%s': 'Failed to add edge. Null destination node [NodeID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * Edge . ToNodeID . ToString ( ) ) ;
return false ;
2020-12-04 11:19:17 -04:00
}
2021-02-24 18:37:19 -04:00
const FNodeIDVertexID SourceKey ( Edge . FromNodeID , Edge . FromVertexID ) ;
2021-01-13 10:48:59 -04:00
const FCoreNodeAndFrontendVertex * SourceNodeAndVertex = NodeSourcesByID . Find ( SourceKey ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( nullptr = = SourceNodeAndVertex )
2020-12-04 11:19:17 -04:00
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to add edge. Could not find source [NodeID:%s, VertexID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * Edge . FromNodeID . ToString ( ) , * Edge . FromVertexID . ToString ( ) ) ;
return false ;
2021-01-13 10:48:59 -04:00
}
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
if ( nullptr = = SourceNodeAndVertex - > Node )
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Warning , TEXT ( " MetaSound '%s': Skipping edge. Null source node [NodeID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * Edge . FromNodeID . ToString ( ) ) ;
return false ;
2021-01-13 10:48:59 -04:00
}
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
const INode * FromNode = SourceNodeAndVertex - > Node ;
2021-09-13 14:14:37 -04:00
const FVertexName FromVertexKey = SourceNodeAndVertex - > Vertex - > Name ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
const INode * ToNode = DestinationNodeAndVertex - > Node ;
2021-09-13 14:14:37 -04:00
const FVertexName ToVertexKey = DestinationNodeAndVertex - > Vertex - > Name ;
2020-12-04 11:19:17 -04:00
2021-05-28 14:09:45 -04:00
bool bSuccess = InGraphContext . Graph - > AddDataEdge ( * FromNode , FromVertexKey , * ToNode , ToVertexKey ) ;
2020-12-04 11:19:17 -04:00
2021-05-28 14:09:45 -04:00
// If succeeded, remove input as viable vertex to construct default variable, as it has been superceded by a connection.
if ( bSuccess )
{
2021-06-02 11:30:20 -04:00
FNodeIDVertexID DestinationPair { ToNode - > GetInstanceID ( ) , DestinationNodeAndVertex - > Vertex - > VertexID } ;
InGraphContext . DefaultInputs . Remove ( DestinationPair ) ;
2021-05-28 14:09:45 -04:00
}
else
2021-01-13 10:48:59 -04:00
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to connect edge from [NodeID:%s, VertexID:%s] to [NodeID:%s, VertexID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * Edge . FromNodeID . ToString ( ) , * Edge . FromVertexID . ToString ( ) , * Edge . ToNodeID . ToString ( ) , * Edge . ToVertexID . ToString ( ) ) ;
return false ;
2020-12-04 11:19:17 -04:00
}
}
2022-03-04 04:25:00 -05:00
return true ;
2020-12-04 11:19:17 -04:00
}
2022-03-04 04:25:00 -05:00
bool FFrontendGraphBuilder : : AddDefaultInputLiterals ( FBuildGraphContext & InGraphContext )
2021-05-28 14:09:45 -04:00
{
2021-08-19 09:59:27 -04:00
using namespace Metasound : : Frontend ;
2022-03-17 13:14:50 -04:00
using namespace LiteralNodeNames ;
2021-08-19 09:59:27 -04:00
2021-07-28 17:12:57 -04:00
using FIterator = FDefaultInputByIDMap : : TIterator ;
for ( FDefaultInputByIDMap : : ElementType & Pair : InGraphContext . DefaultInputs )
2021-05-28 14:09:45 -04:00
{
2021-09-07 17:07:54 -04:00
FDefaultLiteralData & LiteralData = Pair . Value ;
2023-10-16 18:42:49 -04:00
const FGuid LiteralNodeID = Frontend : : CreateLocallyUniqueId ( ) ;
2021-05-28 14:09:45 -04:00
// 1. Construct and add the default variable to the graph
{
2021-09-07 17:07:54 -04:00
TUniquePtr < const INode > DefaultLiteral = IDataTypeRegistry : : Get ( ) . CreateLiteralNode ( LiteralData . TypeName , MoveTemp ( LiteralData . InitParams ) ) ;
InGraphContext . Graph - > AddNode ( LiteralNodeID , TSharedPtr < const INode > ( DefaultLiteral . Release ( ) ) ) ;
2021-05-28 14:09:45 -04:00
}
// 2. Connect the default variable to the expected input
2021-09-07 17:07:54 -04:00
const INode * FromNode = InGraphContext . Graph - > FindNode ( LiteralNodeID ) ;
2022-03-04 04:25:00 -05:00
if ( nullptr = = FromNode )
2021-05-28 14:09:45 -04:00
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to find node in graph [NodeID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * LiteralNodeID . ToString ( ) ) ;
return false ;
2021-05-28 14:09:45 -04:00
}
2022-03-17 13:14:50 -04:00
const FVertexName & FromVertexName = METASOUND_GET_PARAM_NAME ( OutputValue ) ;
2021-05-28 14:09:45 -04:00
2021-09-07 17:07:54 -04:00
const INode * ToNode = InGraphContext . Graph - > FindNode ( LiteralData . DestinationNodeID ) ;
2022-03-04 04:25:00 -05:00
if ( nullptr = = ToNode )
2021-05-28 14:09:45 -04:00
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to node in graph [NodeID:%s] " ) , * InGraphContext . BuildContext . DebugAssetName , * LiteralData . DestinationNodeID . ToString ( ) ) ;
return false ;
2021-05-28 14:09:45 -04:00
}
2021-09-13 14:14:37 -04:00
const FVertexName & ToVertexName = LiteralData . DestinationVertexKey ;
2021-05-28 14:09:45 -04:00
2021-09-13 14:14:37 -04:00
bool bSuccess = InGraphContext . Graph - > AddDataEdge ( * FromNode , FromVertexName , * ToNode , ToVertexName ) ;
2021-05-28 14:09:45 -04:00
if ( ! bSuccess )
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " MetaSound '%s': Failed to connect default variable edge: from '%s' to '%s' " ) , * InGraphContext . BuildContext . DebugAssetName , * FromVertexName . ToString ( ) , * ToVertexName . ToString ( ) ) ;
return false ;
2021-05-28 14:09:45 -04:00
}
}
2021-07-28 17:12:57 -04:00
// Clear default inputs because literals have been moved out of default input map.
InGraphContext . DefaultInputs . Reset ( ) ;
2022-03-04 04:25:00 -05:00
return true ;
2021-05-28 14:09:45 -04:00
}
2023-09-13 14:21:35 -04:00
TArray < FFrontendGraphBuilder : : FDefaultLiteralData > FFrontendGraphBuilder : : GetInputDefaultLiteralData ( const FBuildContext & InContext , const FMetasoundFrontendNode & InNode , const FNodeInitData & InInitData , const TSet < FNodeIDVertexID > & InEdgeDestinations )
2021-05-28 14:09:45 -04:00
{
2021-09-07 17:07:54 -04:00
TArray < FDefaultLiteralData > DefaultLiteralData ;
2021-05-28 14:09:45 -04:00
TArray < FMetasoundFrontendVertex > InputVertices = InNode . Interface . Inputs ;
for ( const FMetasoundFrontendVertexLiteral & Literal : InNode . InputLiterals )
{
2021-09-07 17:07:54 -04:00
FLiteralNodeConstructorParams InitParams ;
2021-05-28 14:09:45 -04:00
FName TypeName ;
FGuid VertexID = Literal . VertexID ;
2021-09-13 14:14:37 -04:00
FVertexName DestinationVertexName ;
2022-02-25 09:40:33 -05:00
bool bRequiresDefault = false ;
2021-05-28 14:09:45 -04:00
auto RemoveAndBuildParams = [ & ] ( const FMetasoundFrontendVertex & Vertex )
{
if ( Vertex . VertexID = = VertexID )
{
2022-02-25 09:40:33 -05:00
// Only build params and forward along to connect to dynamically generated
// literal node if edge is not explicitly connected to vertex as destination.
2022-02-25 19:46:58 -05:00
bRequiresDefault = ! InEdgeDestinations . Contains ( { InNode . GetID ( ) , Vertex . VertexID } ) ;
2022-02-25 09:40:33 -05:00
if ( bRequiresDefault )
{
2023-09-13 14:21:35 -04:00
InitParams . Literal = Literal . Value . ToLiteral ( Vertex . TypeName , & InContext . DataTypeRegistry , & InContext . ProxyDataCache ) ;
2023-10-16 18:42:49 -04:00
InitParams . InstanceID = Frontend : : CreateLocallyUniqueId ( ) ;
2022-02-25 09:40:33 -05:00
InitParams . NodeName = " Literal " ;
TypeName = Vertex . TypeName ;
2021-05-28 14:09:45 -04:00
2022-02-25 09:40:33 -05:00
DestinationVertexName = Vertex . Name ;
}
2021-05-28 14:09:45 -04:00
return true ;
}
return false ;
} ;
2024-01-19 16:41:35 -05:00
const bool bRemoved = InputVertices . RemoveAllSwap ( RemoveAndBuildParams , EAllowShrinking : : No ) > 0 ;
2021-05-28 14:09:45 -04:00
if ( ensure ( bRemoved ) )
{
2022-02-25 09:40:33 -05:00
if ( bRequiresDefault )
2021-05-28 14:09:45 -04:00
{
2022-02-25 09:40:33 -05:00
DefaultLiteralData . Emplace ( FDefaultLiteralData
{
InNode . GetID ( ) ,
VertexID ,
DestinationVertexName ,
TypeName ,
MoveTemp ( InitParams )
} ) ;
}
2021-05-28 14:09:45 -04:00
}
}
2021-09-07 17:07:54 -04:00
return DefaultLiteralData ;
2021-05-28 14:09:45 -04:00
}
2020-12-04 11:19:17 -04:00
/** Check that all dependencies are C++ class dependencies. */
2021-05-28 14:09:45 -04:00
bool FFrontendGraphBuilder : : IsFlat ( const FMetasoundFrontendDocument & InDocument )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
if ( InDocument . Subgraphs . Num ( ) > 0 )
{
return false ;
}
return IsFlat ( InDocument . RootGraph , InDocument . Dependencies ) ;
2020-12-04 11:19:17 -04:00
}
2021-05-28 14:09:45 -04:00
bool FFrontendGraphBuilder : : IsFlat ( const FMetasoundFrontendGraphClass & InRoot , const TArray < FMetasoundFrontendClass > & InDependencies )
2020-12-04 11:19:17 -04:00
{
// All dependencies are external dependencies in a flat graph
2021-01-13 10:48:59 -04:00
auto IsClassExternal = [ & ] ( const FMetasoundFrontendClass & InDesc )
2022-08-10 14:18:10 -04:00
{
const bool bIsExternal = ( InDesc . Metadata . GetType ( ) = = EMetasoundFrontendClassType : : External ) | |
( InDesc . Metadata . GetType ( ) = = EMetasoundFrontendClassType : : Template ) | |
2021-07-27 15:36:03 -04:00
( InDesc . Metadata . GetType ( ) = = EMetasoundFrontendClassType : : Input ) | |
( InDesc . Metadata . GetType ( ) = = EMetasoundFrontendClassType : : Output ) ;
2021-01-13 10:48:59 -04:00
return bIsExternal ;
} ;
2020-12-04 11:19:17 -04:00
const bool bIsEveryDependencyExternal = Algo : : AllOf ( InDependencies , IsClassExternal ) ;
if ( ! bIsEveryDependencyExternal )
{
return false ;
}
2021-01-13 10:48:59 -04:00
// All the dependencies are met
2021-01-20 17:26:40 -04:00
TSet < FGuid > AvailableDependencies ;
2021-01-13 10:48:59 -04:00
Algo : : Transform ( InDependencies , AvailableDependencies , [ ] ( const FMetasoundFrontendClass & InDesc ) { return InDesc . ID ; } ) ;
2020-12-04 11:19:17 -04:00
2021-01-13 10:48:59 -04:00
auto IsDependencyMet = [ & ] ( const FMetasoundFrontendNode & InNode )
2020-12-04 11:19:17 -04:00
{
2021-01-13 10:48:59 -04:00
return AvailableDependencies . Contains ( InNode . ClassID ) ;
2020-12-04 11:19:17 -04:00
} ;
const bool bIsEveryDependencyMet = Algo : : AllOf ( InRoot . Graph . Nodes , IsDependencyMet ) ;
2021-01-13 10:48:59 -04:00
2020-12-04 11:19:17 -04:00
return bIsEveryDependencyMet ;
}
2021-05-28 14:09:45 -04:00
bool FFrontendGraphBuilder : : SortSubgraphDependencies ( TArray < const FMetasoundFrontendGraphClass * > & Subgraphs )
2020-12-04 11:19:17 -04:00
{
2021-05-20 19:33:21 -04:00
// Helper for caching and querying subgraph dependencies
struct FSubgraphDependencyLookup
2020-12-04 11:19:17 -04:00
{
2021-05-20 19:33:21 -04:00
FSubgraphDependencyLookup ( TArrayView < const FMetasoundFrontendGraphClass * > InGraphs )
{
// Map ClassID to graph pointer.
TMap < FGuid , const FMetasoundFrontendGraphClass * > ClassIDAndGraph ;
for ( const FMetasoundFrontendGraphClass * Graph : InGraphs )
{
ClassIDAndGraph . Add ( Graph - > ID , Graph ) ;
}
// Cache subgraph dependencies.
for ( const FMetasoundFrontendGraphClass * GraphClass : InGraphs )
{
for ( const FMetasoundFrontendNode & Node : GraphClass - > Graph . Nodes )
{
if ( ClassIDAndGraph . Contains ( Node . ClassID ) )
{
DependencyMap . Add ( GraphClass , ClassIDAndGraph [ Node . ClassID ] ) ;
}
}
}
}
TArray < const FMetasoundFrontendGraphClass * > operator ( ) ( const FMetasoundFrontendGraphClass * InParent ) const
{
TArray < const FMetasoundFrontendGraphClass * > Dependencies ;
DependencyMap . MultiFind ( InParent , Dependencies ) ;
return Dependencies ;
}
private :
TMultiMap < const FMetasoundFrontendGraphClass * , const FMetasoundFrontendGraphClass * > DependencyMap ;
} ;
bool bSuccess = Algo : : TopologicalSort ( Subgraphs , FSubgraphDependencyLookup ( Subgraphs ) ) ;
if ( ! bSuccess )
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Failed to topologically sort subgraphs. Possible recursive subgraph dependency " ) ) ;
2020-12-04 11:19:17 -04:00
}
2021-05-20 19:33:21 -04:00
return bSuccess ;
}
2020-12-04 11:19:17 -04:00
2023-09-13 14:21:35 -04:00
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( FBuildContext & InContext , const FMetasoundFrontendGraphClass & InGraphClass )
2021-05-20 19:33:21 -04:00
{
2023-09-05 11:21:01 -04:00
const FString GraphName = InContext . DebugAssetName ;
2020-12-04 11:19:17 -04:00
2021-05-28 14:09:45 -04:00
FBuildGraphContext BuildGraphContext
{
2023-10-16 18:42:49 -04:00
MakeUnique < FFrontendGraph > ( GraphName , Frontend : : CreateLocallyUniqueId ( ) ) ,
2021-05-28 14:09:45 -04:00
InGraphClass ,
InContext
} ;
2020-12-04 11:19:17 -04:00
2023-09-13 14:21:35 -04:00
bool bSuccess = AddNodesToGraph ( BuildGraphContext ) ;
2020-12-04 11:19:17 -04:00
2022-03-04 04:25:00 -05:00
if ( bSuccess )
{
bSuccess = AddEdgesToGraph ( BuildGraphContext ) ;
}
if ( bSuccess )
{
bSuccess = AddDefaultInputLiterals ( BuildGraphContext ) ;
}
if ( bSuccess )
{
return MoveTemp ( BuildGraphContext . Graph ) ;
}
else
{
return TUniquePtr < FFrontendGraph > ( nullptr ) ;
}
2021-01-13 10:48:59 -04:00
}
2021-05-20 19:33:21 -04:00
2022-10-10 15:44:28 -04:00
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendGraphClass & InGraph , const TArray < FMetasoundFrontendGraphClass > & InSubgraphs , const TArray < FMetasoundFrontendClass > & InDependencies , const TSet < FName > & InTransmittableInputNames , const FString & InDebugAssetName )
2021-05-20 19:33:21 -04:00
{
2023-09-13 14:21:35 -04:00
return CreateGraph ( InGraph , InSubgraphs , InDependencies , InDebugAssetName ) ;
}
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendGraphClass & InGraph , const TArray < FMetasoundFrontendGraphClass > & InSubgraphs , const TArray < FMetasoundFrontendClass > & InDependencies , const Frontend : : FProxyDataCache & InProxyDataCache , const FString & InDebugAssetName )
{
FBuildContext Context
{
InDebugAssetName , // DebugAssetName
{ } , // FrontendClasses
{ } , // Graphs
Frontend : : IDataTypeRegistry : : Get ( ) , // DataTypeRegistry
InProxyDataCache // ProxyDataCache
} ;
2021-05-20 19:33:21 -04:00
// Gather all references to node classes from external dependencies and subgraphs.
for ( const FMetasoundFrontendClass & ExtClass : InDependencies )
{
Context . FrontendClasses . Add ( ExtClass . ID , & ExtClass ) ;
}
for ( const FMetasoundFrontendClass & ExtClass : InSubgraphs )
{
Context . FrontendClasses . Add ( ExtClass . ID , & ExtClass ) ;
}
// Sort subgraphs so that dependent subgraphs are created in correct order.
TArray < const FMetasoundFrontendGraphClass * > FrontendSubgraphPtrs ;
Algo : : Transform ( InSubgraphs , FrontendSubgraphPtrs , [ ] ( const FMetasoundFrontendGraphClass & InClass ) { return & InClass ; } ) ;
bool bSuccess = SortSubgraphDependencies ( FrontendSubgraphPtrs ) ;
if ( ! bSuccess )
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Error , TEXT ( " Failed to create graph due to failed subgraph ordering in asset '%s'. " ) , * InDebugAssetName ) ;
2021-05-20 19:33:21 -04:00
return TUniquePtr < FFrontendGraph > ( nullptr ) ;
}
// Create each subgraph.
for ( const FMetasoundFrontendGraphClass * FrontendSubgraphPtr : FrontendSubgraphPtrs )
{
2023-09-13 14:21:35 -04:00
TSharedPtr < const INode > Subgraph ( CreateGraph ( Context , * FrontendSubgraphPtr ) . Release ( ) ) ;
2021-05-20 19:33:21 -04:00
if ( ! Subgraph . IsValid ( ) )
{
2022-03-04 04:25:00 -05:00
UE_LOG ( LogMetaSound , Warning , TEXT ( " Failed to create subgraph [SubgraphName: %s] in asset '%s' " ) , * FrontendSubgraphPtr - > Metadata . GetClassName ( ) . ToString ( ) , * InDebugAssetName ) ;
2021-05-20 19:33:21 -04:00
}
else
{
// Add subgraphs to context so they are accessible for subsequent graphs.
Context . Graphs . Add ( FrontendSubgraphPtr - > ID , Subgraph ) ;
}
}
// Create parent graph.
2023-09-13 14:21:35 -04:00
return CreateGraph ( Context , InGraph ) ;
}
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendDocument & InDocument , const FString & InDebugAssetName )
{
// Create proxies before creating graph.
Frontend : : FProxyDataCache ProxyDataCache ;
ProxyDataCache . CreateAndCacheProxies ( InDocument ) ;
return CreateGraph ( InDocument , ProxyDataCache , InDebugAssetName ) ;
}
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendGraphClass & InGraph , const TArray < FMetasoundFrontendGraphClass > & InSubgraphs , const TArray < FMetasoundFrontendClass > & InDependencies , const FString & InDebugAssetName )
{
// Create proxies before building graph
Frontend : : FProxyDataCache ProxyDataCache ;
ProxyDataCache . CreateAndCacheProxies ( InGraph ) ;
for ( const FMetasoundFrontendGraphClass & SubgraphClass : InSubgraphs )
{
ProxyDataCache . CreateAndCacheProxies ( SubgraphClass ) ;
}
for ( const FMetasoundFrontendClass & DependencyClass : InDependencies )
{
ProxyDataCache . CreateAndCacheProxies ( DependencyClass ) ;
}
return CreateGraph ( InGraph , InSubgraphs , InDependencies , ProxyDataCache , InDebugAssetName ) ;
2021-05-20 19:33:21 -04:00
}
2021-01-13 10:48:59 -04:00
2022-10-10 15:44:28 -04:00
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendDocument & InDocument , const TSet < FName > & InTransmittableInputNames , const FString & InDebugAssetName )
2021-01-13 10:48:59 -04:00
{
2023-09-13 14:21:35 -04:00
return CreateGraph ( InDocument , InDebugAssetName ) ;
}
TUniquePtr < FFrontendGraph > FFrontendGraphBuilder : : CreateGraph ( const FMetasoundFrontendDocument & InDocument , const Frontend : : FProxyDataCache & InProxyDataCache , const FString & InDebugAssetName )
{
return CreateGraph ( InDocument . RootGraph , InDocument . Subgraphs , InDocument . Dependencies , InProxyDataCache , InDebugAssetName ) ;
2020-12-04 11:19:17 -04:00
}
}