2020-05-22 23:46:09 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
# include "CoreMinimal.h"
2020-07-22 14:52:03 -04:00
# include "Internationalization/Text.h"
2021-05-20 19:33:21 -04:00
# include "MetasoundDataReference.h"
2020-05-22 23:46:09 -04:00
# include "MetasoundNodeInterface.h"
2021-05-28 14:09:45 -04:00
# include "MetasoundOperatorInterface.h"
2021-03-23 17:55:31 -04:00
# include "MetasoundTrigger.h"
2020-05-22 23:46:09 -04:00
2021-05-28 14:09:45 -04:00
# define LOCTEXT_NAMESPACE "MetasoundFrontend"
2020-07-22 14:52:03 -04:00
2020-05-22 23:46:09 -04:00
namespace Metasound
{
2021-05-28 14:09:45 -04:00
/** A writable input and a readable output. */
2020-11-04 14:26:37 -04:00
template < typename DataType >
class TInputOperator : public IOperator
{
public :
using FDataWriteReference = TDataWriteReference < DataType > ;
2021-09-13 14:14:37 -04:00
TInputOperator ( const FVertexName & InDataReferenceName , FDataWriteReference InDataReference )
2021-03-23 17:55:31 -04:00
: DataReferenceName ( InDataReferenceName )
// Executable DataTypes require a copy of the output to operate on whereas non-executable
// types do not. Avoid copy by assigning to reference for non-executable types.
2021-03-23 22:43:28 -04:00
, InputValue ( InDataReference )
, OutputValue ( TExecutableDataType < DataType > : : bIsExecutable ? FDataWriteReference : : CreateNew ( * InDataReference ) : InDataReference )
2020-11-04 14:26:37 -04:00
{
}
2021-05-20 19:33:21 -04:00
virtual ~ TInputOperator ( ) = default ;
2020-11-04 14:26:37 -04:00
2020-12-08 19:34:18 -04:00
virtual FDataReferenceCollection GetInputs ( ) const override
2020-11-04 14:26:37 -04:00
{
2021-05-20 19:33:21 -04:00
// TODO: Expose a readable reference instead of a writable reference.
//
// If data needs to be written to, outside entities should create
// it and pass it in as a readable reference. Currently, the workflow
// is to have the input node create a writable reference which is then
// queried by the outside world. Exposing writable references causes
// code maintainability issues where TInputNode<> specializations need
// to handle multiple situations which can happen in an input node.
//
// The only reason that this code is not removed immediately is because
// of the `TExecutableDataType<>` which primarily supports the FTrigger.
// The TExecutableDataType<> advances the trigger within the graph. But,
// with graph composition, the owner of the data type becomes more
// complicated and hence triggers advancing should be managed by a
// different object. Preferably the graph operator itself, or an
// explicit trigger manager tied to the environment.
2021-03-23 22:43:28 -04:00
FDataReferenceCollection Inputs ;
Inputs . AddDataWriteReference < DataType > ( DataReferenceName , InputValue ) ;
2020-11-04 14:26:37 -04:00
return Inputs ;
}
2020-12-08 19:34:18 -04:00
virtual FDataReferenceCollection GetOutputs ( ) const override
2020-11-04 14:26:37 -04:00
{
2021-03-23 22:43:28 -04:00
FDataReferenceCollection Outputs ;
Outputs . AddDataReadReference < DataType > ( DataReferenceName , OutputValue ) ;
2020-11-04 14:26:37 -04:00
return Outputs ;
}
2021-03-23 17:55:31 -04:00
void Execute ( )
{
2021-03-23 22:43:28 -04:00
TExecutableDataType < DataType > : : Execute ( * InputValue , * OutputValue ) ;
2021-03-23 17:55:31 -04:00
}
2021-05-20 19:33:21 -04:00
void ExecutPassThrough ( )
{
if ( TExecutableDataType < DataType > : : bIsExecutable )
{
* OutputValue = * InputValue ;
}
}
2021-03-23 17:55:31 -04:00
static void ExecuteFunction ( IOperator * InOperator )
{
static_cast < TInputOperator < DataType > * > ( InOperator ) - > Execute ( ) ;
}
2020-11-04 14:26:37 -04:00
virtual FExecuteFunction GetExecuteFunction ( ) override
{
2021-03-23 17:55:31 -04:00
if ( TExecutableDataType < DataType > : : bIsExecutable )
{
2021-09-07 17:07:54 -04:00
return & TInputOperator < DataType > : : ExecuteFunction ;
2021-03-23 17:55:31 -04:00
}
2020-11-04 14:26:37 -04:00
return nullptr ;
}
2021-05-28 14:09:45 -04:00
protected :
2021-09-13 14:14:37 -04:00
FVertexName DataReferenceName ;
2021-03-23 17:55:31 -04:00
2021-03-23 22:43:28 -04:00
FDataWriteReference InputValue ;
FDataWriteReference OutputValue ;
2020-11-04 14:26:37 -04:00
} ;
2021-05-20 19:33:21 -04:00
/** TPassThroughOperator supplies a readable input and a readable output.
*
* It does * not * invoke executable data types ( see ` TExecutableDataType < > ` ) .
2020-11-04 14:26:37 -04:00
*/
template < typename DataType >
2021-05-20 19:33:21 -04:00
class TPassThroughOperator : public TInputOperator < DataType >
{
public :
using FDataReadReference = TDataReadReference < DataType > ;
using Super = TInputOperator < DataType > ;
2021-09-13 14:14:37 -04:00
TPassThroughOperator ( const FVertexName & InDataReferenceName , FDataReadReference InDataReference )
2021-05-20 19:33:21 -04:00
: TInputOperator < DataType > ( InDataReferenceName , WriteCast ( InDataReference ) ) // Write cast is safe because `GetExecuteFunction() and GetInputs() are overridden, ensuring that data is not written.
, DataReferenceName ( InDataReferenceName )
{
}
virtual ~ TPassThroughOperator ( ) = default ;
virtual FDataReferenceCollection GetInputs ( ) const override
{
FDataReferenceCollection Inputs ;
ensure ( Inputs . AddDataReadReferenceFrom ( DataReferenceName , Super : : GetInputs ( ) , DataReferenceName , GetMetasoundDataTypeName < DataType > ( ) ) ) ;
return Inputs ;
}
static void ExecuteFunction ( IOperator * InOperator )
{
static_cast < TPassThroughOperator < DataType > * > ( InOperator ) - > ExecutPassThrough ( ) ;
}
virtual IOperator : : FExecuteFunction GetExecuteFunction ( ) override
{
// TODO: this is a hack until we can remove TExecutableOperator<>.
//
// The primary contention is that we would like to allow developers
// to specialize `TInputNode<>` as in `TInputNode<FStereoAudioFormat>`.
// `TExecutableOperator<>` adds in a level of complexity that makes it
// difficult to allow specialization of TInputNode and to derive from
// TInputNode to create the TPassThroughOperator. Particularly because
// TExecutableOperator<> alters which output data reference is used.
// Specializations of TInputNode also tend to alter the output data
// references. Supporting both is likely to cause issues.
//
// We may need to ensure that input nodes do not provide execution
// functions. Or we may need a more explicit way of only allowing
// outputs to be modified. Likely a mix of the `final` keyword
// and disabling template specialization of a base class.
//
// namespace Private
// {
// class TInputNodePrivate<>
// {
// GetInputs() final
// GetExecutionFunction() final
// GetOutputs()
// }
// }
//
// template<DataType>
// using TInputNodeBase<DataType> = TInputNodePrivate<DataType>; // Do not allow specialization of TInputNodePrivate<> or TInputNodeBase<> (this works because you can't specialize a template alias)
//
// // DO ALLOW specialization of TInputNode
// template<DataType>
// class TInputNode<DataType> : public TInputNodeBase<DataType>
// {
// };
//
// template<>
// class TInputNode<MyType> : public TInputNodeBase<MyType>
// {
// GetOutputs() <-- OK to override
// }
//
if ( TExecutableDataType < DataType > : : bIsExecutable )
{
return & TPassThroughOperator < DataType > : : ExecuteFunction ;
}
return nullptr ;
}
private :
2021-09-13 14:14:37 -04:00
FVertexName DataReferenceName ;
2021-05-20 19:33:21 -04:00
} ;
/** Data type creation policy to create by copy construction. */
template < typename DataType >
struct FCreateDataReferenceWithCopy
{
template < typename . . . ArgTypes >
FCreateDataReferenceWithCopy ( ArgTypes & & . . . Args )
: Data ( Forward < ArgTypes > ( Args ) . . . )
{
}
TDataWriteReference < DataType > CreateDataReference ( const FOperatorSettings & InOperatorSettings ) const
{
return TDataWriteReferenceFactory < DataType > : : CreateExplicitArgs ( InOperatorSettings , Data ) ;
}
private :
DataType Data ;
} ;
/** Data type creation policy to create by literal construction. */
template < typename DataType >
struct FCreateDataReferenceWithLiteral
{
// If the data type is parsable from a literal type, then the data type
// can be registered as an input type with the frontend. To make a
// DataType registrable, either create a constructor for the data type
// which accepts the one of the supported literal types with an optional
// FOperatorSettings argument, or create a default constructor, or specialize
// this factory with an implementation for that specific data type.
static constexpr bool bCanCreateWithLiteral = TLiteralTraits < DataType > : : bIsParsableFromAnyLiteralType ;
FCreateDataReferenceWithLiteral ( FLiteral & & InLiteral )
: Literal ( MoveTemp ( InLiteral ) )
{
}
TDataWriteReference < DataType > CreateDataReference ( const FOperatorSettings & InOperatorSettings ) const
{
return TDataWriteReferenceLiteralFactory < DataType > : : CreateExplicitArgs ( InOperatorSettings , Literal ) ;
}
private :
FLiteral Literal ;
} ;
/** TInputOperatorFactory initializes the DataType at construction. It uses
* the ReferenceCreatorType to create a data reference if one is not passed in .
*/
template < typename DataType , typename ReferenceCreatorType >
2020-11-04 14:26:37 -04:00
class TInputOperatorFactory : public IOperatorFactory
{
public :
using FDataWriteReference = TDataWriteReference < DataType > ;
2021-05-20 19:33:21 -04:00
using FDataReadReference = TDataReadReference < DataType > ;
2020-11-04 14:26:37 -04:00
2021-05-20 19:33:21 -04:00
TInputOperatorFactory ( ReferenceCreatorType & & InReferenceCreator )
: ReferenceCreator ( MoveTemp ( InReferenceCreator ) )
2020-11-04 14:26:37 -04:00
{
}
virtual TUniquePtr < IOperator > CreateOperator ( const FCreateOperatorParams & InParams , FBuildErrorArray & OutErrors ) override ;
private :
2021-05-20 19:33:21 -04:00
ReferenceCreatorType ReferenceCreator ;
2020-11-04 14:26:37 -04:00
} ;
/** TInputNode represents an input to a metasound graph. */
2020-05-22 23:46:09 -04:00
template < typename DataType >
2020-08-24 10:57:03 -04:00
class TInputNode : public FNode
2020-05-22 23:46:09 -04:00
{
2020-08-24 10:57:03 -04:00
public :
2020-11-04 14:26:37 -04:00
// If true, this node can be instantiated by the FrontEnd.
2021-05-20 19:33:21 -04:00
static constexpr bool bCanRegister = FCreateDataReferenceWithLiteral < DataType > : : bCanCreateWithLiteral ;
2020-05-22 23:46:09 -04:00
2021-09-13 14:14:37 -04:00
static FVertexInterface DeclareVertexInterface ( const FVertexName & InVertexName )
2020-08-24 10:57:03 -04:00
{
return FVertexInterface (
FInputVertexInterface (
TInputDataVertexModel < DataType > ( InVertexName , FText : : GetEmpty ( ) )
) ,
FOutputVertexInterface (
TOutputDataVertexModel < DataType > ( InVertexName , FText : : GetEmpty ( ) )
)
) ;
}
2021-09-13 14:14:37 -04:00
static FNodeClassMetadata GetNodeInfo ( const FVertexName & InVertexName )
2020-11-24 15:24:29 -04:00
{
2021-01-28 19:02:51 -04:00
FNodeClassMetadata Info ;
2020-11-24 15:24:29 -04:00
2021-09-17 14:41:22 -04:00
Info . ClassName = { " Input " , GetMetasoundDataTypeName < DataType > ( ) , FName ( ) } ;
2020-11-24 15:24:29 -04:00
Info . MajorVersion = 1 ;
Info . MinorVersion = 0 ;
Info . Description = LOCTEXT ( " Metasound_InputNodeDescription " , " Input into the parent Metasound graph. " ) ;
Info . Author = PluginAuthor ;
Info . PromptIfMissing = PluginNodeMissingPrompt ;
Info . DefaultInterface = DeclareVertexInterface ( InVertexName ) ;
return Info ;
}
2021-05-20 19:33:21 -04:00
template < typename . . . ArgTypes >
static FOperatorFactorySharedRef CreateOperatorFactoryWithArgs ( ArgTypes & & . . . Args )
{
using FCreatorType = FCreateDataReferenceWithCopy < DataType > ;
using FFactoryType = TInputOperatorFactory < DataType , FCreatorType > ;
return MakeOperatorFactoryRef < FFactoryType > ( FCreatorType ( Forward < ArgTypes > ( Args ) . . . ) ) ;
}
static FOperatorFactorySharedRef CreateOperatorFactoryWithLiteral ( FLiteral & & InLiteral )
{
using FCreatorType = FCreateDataReferenceWithLiteral < DataType > ;
using FFactoryType = TInputOperatorFactory < DataType , FCreatorType > ;
return MakeOperatorFactoryRef < FFactoryType > ( FCreatorType ( MoveTemp ( InLiteral ) ) ) ;
}
2020-11-24 15:24:29 -04:00
2020-11-04 14:26:37 -04:00
/* Construct a TInputNode using the TInputOperatorFactory<> and forwarding
* Args to the TInputOperatorFactory constructor . */
2020-07-20 00:05:22 -04:00
template < typename . . . ArgTypes >
2021-09-13 14:14:37 -04:00
TInputNode ( const FVertexName & InInstanceName , const FGuid & InInstanceID , const FVertexName & InVertexName , ArgTypes & & . . . Args )
: FNode ( InInstanceName , InInstanceID , GetNodeInfo ( InVertexName ) )
2020-06-25 18:06:30 -04:00
, VertexName ( InVertexName )
2020-08-24 10:57:03 -04:00
, Interface ( DeclareVertexInterface ( InVertexName ) )
2021-05-20 19:33:21 -04:00
, Factory ( CreateOperatorFactoryWithArgs ( Forward < ArgTypes > ( Args ) . . . ) )
2020-05-22 23:46:09 -04:00
{
}
2020-11-04 14:26:37 -04:00
/* Construct a TInputNode using the TInputOperatorLiteralFactory<> and moving
* InParam to the TInputOperatorLiteralFactory constructor . */
2021-09-13 14:14:37 -04:00
explicit TInputNode ( const FVertexName & InNodeName , const FGuid & InInstanceID , const FVertexName & InVertexName , FLiteral & & InParam )
: FNode ( InNodeName , InInstanceID , GetNodeInfo ( InVertexName ) )
2020-08-24 10:57:03 -04:00
, VertexName ( InVertexName )
, Interface ( DeclareVertexInterface ( InVertexName ) )
2021-05-20 19:33:21 -04:00
, Factory ( CreateOperatorFactoryWithLiteral ( MoveTemp ( InParam ) ) )
2020-07-20 00:05:22 -04:00
{
}
2021-09-13 14:14:37 -04:00
const FVertexName & GetVertexName ( ) const
2020-06-25 18:06:30 -04:00
{
return VertexName ;
}
2020-08-24 10:57:03 -04:00
virtual const FVertexInterface & GetVertexInterface ( ) const override
2020-05-22 23:46:09 -04:00
{
2020-08-24 10:57:03 -04:00
return Interface ;
2020-05-22 23:46:09 -04:00
}
2020-08-24 10:57:03 -04:00
virtual bool SetVertexInterface ( const FVertexInterface & InInterface ) override
{
return Interface = = InInterface ;
}
virtual bool IsVertexInterfaceSupported ( const FVertexInterface & InInterface ) const override
{
return Interface = = InInterface ;
}
virtual TSharedRef < IOperatorFactory , ESPMode : : ThreadSafe > GetDefaultOperatorFactory ( ) const override
2020-05-22 23:46:09 -04:00
{
return Factory ;
}
private :
2021-09-13 14:14:37 -04:00
FVertexName VertexName ;
2020-05-22 23:46:09 -04:00
2020-08-24 10:57:03 -04:00
FVertexInterface Interface ;
FOperatorFactorySharedRef Factory ;
2020-05-22 23:46:09 -04:00
} ;
2020-11-04 14:26:37 -04:00
2021-05-20 19:33:21 -04:00
template < typename DataType , typename ReferenceCreatorType >
TUniquePtr < IOperator > TInputOperatorFactory < DataType , ReferenceCreatorType > : : CreateOperator ( const FCreateOperatorParams & InParams , FBuildErrorArray & OutErrors )
2020-11-04 14:26:37 -04:00
{
using FInputNodeType = TInputNode < DataType > ;
const FInputNodeType & InputNode = static_cast < const FInputNodeType & > ( InParams . Node ) ;
2021-09-13 14:14:37 -04:00
const FVertexName & VertexKey = InputNode . GetVertexName ( ) ;
2020-11-04 14:26:37 -04:00
2021-05-20 19:33:21 -04:00
if ( InParams . InputDataReferences . ContainsDataWriteReference < DataType > ( VertexKey ) )
{
// Data is externally owned. Use pass through operator
FDataWriteReference DataRef = InParams . InputDataReferences . GetDataWriteReference < DataType > ( VertexKey ) ;
return MakeUnique < TPassThroughOperator < DataType > > ( InputNode . GetVertexName ( ) , DataRef ) ;
}
else if ( InParams . InputDataReferences . ContainsDataReadReference < DataType > ( VertexKey ) )
{
// Data is externally owned. Use pass through operator
FDataReadReference DataRef = InParams . InputDataReferences . GetDataReadReference < DataType > ( VertexKey ) ;
return MakeUnique < TPassThroughOperator < DataType > > ( InputNode . GetVertexName ( ) , DataRef ) ;
}
else
{
// Create write reference by calling compatible constructor with literal.
FDataWriteReference DataRef = ReferenceCreator . CreateDataReference ( InParams . OperatorSettings ) ;
return MakeUnique < TInputOperator < DataType > > ( InputNode . GetVertexName ( ) , DataRef ) ;
}
2020-11-04 14:26:37 -04:00
}
2020-07-22 14:52:03 -04:00
} // namespace Metasound
2021-05-28 14:09:45 -04:00
# undef LOCTEXT_NAMESPACE // MetasoundFrontend