// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Internationalization/Text.h" #include "MetasoundBuilderInterface.h" #include "MetasoundDataReference.h" #include "MetasoundFrontendDataTypeTraits.h" #include "MetasoundLiteral.h" #include "MetasoundNode.h" #include "MetasoundNodeInterface.h" #include "MetasoundNodeRegistrationMacro.h" #include "MetasoundOperatorInterface.h" #include "MetasoundTrigger.h" #include "MetasoundVertexData.h" #include "UObject/NameTypes.h" #define LOCTEXT_NAMESPACE "MetasoundFrontend" namespace Metasound { /** A writable input and a readable output. */ template class TInputOperator : public IOperator { public: using FDataWriteReference = TDataWriteReference; TInputOperator(const FVertexName& InDataReferenceName, FDataWriteReference InDataReference) : 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. , InputValue(InDataReference) , OutputValue(TExecutableDataType::bIsExecutable ? FDataWriteReference::CreateNew(*InDataReference) : InDataReference) { } virtual ~TInputOperator() = default; virtual FDataReferenceCollection GetInputs() const override { // This is slated to be deprecated and removed. checkNoEntry(); return {}; } virtual FDataReferenceCollection GetOutputs() const override { // This is slated to be deprecated and removed. checkNoEntry(); return {}; } virtual void Bind(FVertexInterfaceData& InOutVertexData) const override { // 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. InOutVertexData.GetInputs().BindWriteVertex(DataReferenceName, InputValue); InOutVertexData.GetOutputs().BindReadVertex(DataReferenceName, OutputValue); } void Execute() { TExecutableDataType::Execute(*InputValue, *OutputValue); } void ExecutPassThrough() { if (TExecutableDataType::bIsExecutable) { *OutputValue = *InputValue; } } static void ExecuteFunction(IOperator* InOperator) { static_cast*>(InOperator)->Execute(); } virtual FExecuteFunction GetExecuteFunction() override { if (TExecutableDataType::bIsExecutable) { return &TInputOperator::ExecuteFunction; } return nullptr; } protected: FVertexName DataReferenceName; FDataWriteReference InputValue; FDataWriteReference OutputValue; }; /** TPassThroughOperator supplies a readable input and a readable output. * * It does *not* invoke executable data types (see `TExecutableDataType<>`). */ template class TPassThroughOperator : public TInputOperator { public: using FDataReadReference = TDataReadReference; using Super = TInputOperator; TPassThroughOperator(const FVertexName& InDataReferenceName, FDataReadReference InDataReference) : TInputOperator(InDataReferenceName, WriteCast(InDataReference)) // Write cast is safe because `GetExecuteFunction() and Bind() are overridden, ensuring that data is not written. , DataReferenceName(InDataReferenceName) { } virtual ~TPassThroughOperator() = default; virtual void Bind(FVertexInterfaceData& InOutVertexData) const override { InOutVertexData.GetInputs().BindReadVertex(DataReferenceName, Super::InputValue); InOutVertexData.GetOutputs().BindReadVertex(DataReferenceName, Super::OutputValue); } static void ExecuteFunction(IOperator* InOperator) { static_cast*>(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`. // `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 // using TInputNodeBase = TInputNodePrivate; // Do not allow specialization of TInputNodePrivate<> or TInputNodeBase<> (this works because you can't specialize a template alias) // // // DO ALLOW specialization of TInputNode // template // class TInputNode : public TInputNodeBase // { // }; // // template<> // class TInputNode : public TInputNodeBase // { // GetOutputs() <-- OK to override // } // if (TExecutableDataType::bIsExecutable) { return &TPassThroughOperator::ExecuteFunction; } return nullptr; } private: FVertexName DataReferenceName; }; /** FInputValueOperator provides an input for value references. */ class METASOUNDFRONTEND_API FInputValueOperator : public IOperator { public: /** Construct an FInputValueOperator with the name of the vertex and the * value reference associated with input. */ template explicit FInputValueOperator(const FName& InVertexName, const TDataValueReference& InValueRef) : VertexName(InVertexName) , Default(InValueRef) { } virtual ~FInputValueOperator() = default; virtual FDataReferenceCollection GetInputs() const override; virtual FDataReferenceCollection GetOutputs() const override; virtual void Bind(FVertexInterfaceData& InVertexData) const override; virtual FExecuteFunction GetExecuteFunction() override; private: FName VertexName; FAnyDataReference Default; }; /** Data type creation policy to create by copy construction. */ template struct UE_DEPRECATED(5.1, "Moved to private implementation.") FCreateDataReferenceWithCopy { template FCreateDataReferenceWithCopy(ArgTypes&&... Args) : Data(Forward(Args)...) { } TDataWriteReference CreateDataReference(const FOperatorSettings& InOperatorSettings) const { return TDataWriteReferenceFactory::CreateExplicitArgs(InOperatorSettings, Data); } private: DataType Data; }; /** Data type creation policy to create by literal construction. */ template struct UE_DEPRECATED(5.1, "Moved to private implementation.") 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::bIsParsableFromAnyLiteralType; FCreateDataReferenceWithLiteral(FLiteral&& InLiteral) : Literal(MoveTemp(InLiteral)) { } TDataWriteReference CreateDataReference(const FOperatorSettings& InOperatorSettings) const { return TDataWriteReferenceLiteralFactory::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 class UE_DEPRECATED(5.1, "Moved to private implementation") TInputOperatorFactory : public IOperatorFactory { public: using FDataWriteReference = TDataWriteReference; using FDataReadReference = TDataReadReference; TInputOperatorFactory(ReferenceCreatorType&& InReferenceCreator) : ReferenceCreator(MoveTemp(InReferenceCreator)) { } virtual TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) override; private: ReferenceCreatorType ReferenceCreator; }; /** TInputNode represents an input to a metasound graph. */ template class TInputNode : public FNode { static constexpr bool bIsConstructorInput = VertexAccess == EVertexAccessType::Value; static constexpr bool bIsSupportedConstructorInput = TIsConstructorVertexSupported::Value && bIsConstructorInput; static constexpr bool bIsReferenceInput = VertexAccess == EVertexAccessType::Reference; static constexpr bool bIsSupportedReferenceInput = TLiteralTraits::bIsParsableFromAnyLiteralType && bIsReferenceInput; static constexpr bool bIsSupportedInput = bIsSupportedConstructorInput || bIsSupportedReferenceInput; // Use Variant names to differentiate between normal input nodes and constructor // input nodes. static FName GetVariantName() { if constexpr (EVertexAccessType::Value == VertexAccess) { return FName("Constructor"); } else { return FName(); } } // Factory for creating input operators. class FInputNodeOperatorFactory : public IOperatorFactory { static constexpr bool bIsReferenceVertexAccess = VertexAccess == EVertexAccessType::Reference; static constexpr bool bIsValueVertexAccess = VertexAccess == EVertexAccessType::Value; static_assert(bIsValueVertexAccess || bIsReferenceVertexAccess, "Unsupported EVertexAccessType"); // Choose which data reference type is created based on template parameters using FDataReference = std::conditional_t, TDataValueReference>; using FPassThroughDataReference = std::conditional_t, TDataValueReference>; // Utility struct for creating data references for varying flavors of // runtime scenarios and vertex access types. struct FDataReferenceCreatorBase { virtual ~FDataReferenceCreatorBase() = default; // Create a a new data reference for constructing operators virtual FDataReference CreateDataReference(const FOperatorSettings& InOperatorSettings) const = 0; // Create a data reference for constructing operators from a given data reference virtual FPassThroughDataReference CreateDataReference(const FAnyDataReference& InRef) const { if constexpr (bIsReferenceVertexAccess) { return InRef.GetDataReadReference(); } else if constexpr (bIsValueVertexAccess) { return InRef.GetDataValueReference(); } else { static_assert("Unsupported EVertexAccessType"); } } }; // Create data references using a literal struct FCreateWithLiteral : FDataReferenceCreatorBase { using FDataFactory = std::conditional_t, TDataValueReferenceLiteralFactory>; FLiteral Literal; FCreateWithLiteral(FLiteral&& InLiteral) : Literal(MoveTemp(InLiteral)) { } virtual FDataReference CreateDataReference(const FOperatorSettings& InOperatorSettings) const override { return FDataFactory::CreateExplicitArgs(InOperatorSettings, Literal); } }; // Create data references using a copy struct FCreateWithCopy : FDataReferenceCreatorBase { using FDataFactory = std::conditional_t, TDataValueReferenceFactory>; DataType Value; virtual FDataReference CreateDataReference(const FOperatorSettings& InOperatorSettings) const override { return FDataFactory::CreateExplicitArgs(InOperatorSettings, Value); } }; public: FInputNodeOperatorFactory(const DataType& InValue) : ReferenceCreator(MakeUnique(InValue)) { } explicit FInputNodeOperatorFactory(FLiteral&& InLiteral) : ReferenceCreator(MakeUnique(MoveTemp(InLiteral))) { } virtual TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) override { using FInputNodeType = TInputNode; using FOwnedInputOperatorType = std::conditional_t, FInputValueOperator>; using FPassThroughInputOperatorType = std::conditional_t, FInputValueOperator>; const FInputNodeType& InputNode = static_cast(InParams.Node); const FVertexName& VertexKey = InputNode.GetVertexName(); if (const FAnyDataReference* Ref = InParams.InputData.FindDataReference(VertexKey)) { // Pass through input value return MakeUnique(VertexKey, ReferenceCreator->CreateDataReference(*Ref)); } else { // Owned input value return MakeUnique(VertexKey, ReferenceCreator->CreateDataReference(InParams.OperatorSettings)); } } private: TUniquePtr ReferenceCreator; }; public: // If true, this node can be instantiated by the Frontend. static constexpr bool bCanRegister = bIsSupportedInput; static FVertexInterface DeclareVertexInterface(const FVertexName& InVertexName) { return FVertexInterface( FInputVertexInterface( FInputDataVertex(InVertexName, GetMetasoundDataTypeName(), FDataVertexMetadata{ FText::GetEmpty() }, VertexAccess) ), FOutputVertexInterface( FOutputDataVertex(InVertexName, GetMetasoundDataTypeName(), FDataVertexMetadata{ FText::GetEmpty() }, VertexAccess) ) ); } static FNodeClassMetadata GetNodeInfo(const FVertexName& InVertexName) { FNodeClassMetadata Info; Info.ClassName = { "Input", GetMetasoundDataTypeName(), GetVariantName() }; Info.MajorVersion = 1; Info.MinorVersion = 0; Info.Description = METASOUND_LOCTEXT("Metasound_InputNodeDescription", "Input into the parent Metasound graph."); Info.Author = PluginAuthor; Info.PromptIfMissing = PluginNodeMissingPrompt; Info.DefaultInterface = DeclareVertexInterface(InVertexName); return Info; } template UE_DEPRECATED(5.1, "Moved to internal implementation.") static FOperatorFactorySharedRef CreateOperatorFactoryWithArgs(ArgTypes&&... Args) { using FCreatorType = FCreateDataReferenceWithCopy; using FFactoryType = TInputOperatorFactory; return MakeOperatorFactoryRef(FCreatorType(Forward(Args)...)); } UE_DEPRECATED(5.1, "Moved to internal implementation.") static FOperatorFactorySharedRef CreateOperatorFactoryWithLiteral(FLiteral&& InLiteral) { using FCreatorType = FCreateDataReferenceWithLiteral; using FFactoryType = TInputOperatorFactory; return MakeOperatorFactoryRef(FCreatorType(MoveTemp(InLiteral))); } /* Construct a TInputNode using the TInputOperatorFactory<> and forwarding * Args to the TInputOperatorFactory constructor.*/ template UE_DEPRECATED(5.1, "Constructing an TInputNode with args will no longer be supported.") TInputNode(const FVertexName& InInstanceName, const FGuid& InInstanceID, const FVertexName& InVertexName, ArgTypes&&... Args) : FNode(InInstanceName, InInstanceID, GetNodeInfo(InVertexName)) , VertexName(InVertexName) , Interface(DeclareVertexInterface(InVertexName)) , Factory(MakeShared(Forward(Args)...)) { } /* Construct a TInputNode using the TInputOperatorLiteralFactory<> and moving * InParam to the TInputOperatorLiteralFactory constructor.*/ explicit TInputNode(const FVertexName& InNodeName, const FGuid& InInstanceID, const FVertexName& InVertexName, FLiteral&& InParam) : FNode(InNodeName, InInstanceID, GetNodeInfo(InVertexName)) , VertexName(InVertexName) , Interface(DeclareVertexInterface(InVertexName)) , Factory(MakeShared(MoveTemp(InParam))) { } const FVertexName& GetVertexName() const { return VertexName; } virtual const FVertexInterface& GetVertexInterface() const override { return Interface; } virtual bool SetVertexInterface(const FVertexInterface& InInterface) override { return Interface == InInterface; } virtual bool IsVertexInterfaceSupported(const FVertexInterface& InInterface) const override { return Interface == InInterface; } virtual TSharedRef GetDefaultOperatorFactory() const override { return Factory; } private: FVertexName VertexName; FVertexInterface Interface; FOperatorFactorySharedRef Factory; }; template TUniquePtr TInputOperatorFactory::CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) { using FInputNodeType = TInputNode; const FInputNodeType& InputNode = static_cast(InParams.Node); const FVertexName& VertexKey = InputNode.GetVertexName(); if (InParams.InputData.IsVertexBound(VertexKey)) { // Data is externally owned. Use pass through operator TDataReadReference DataRef = InParams.InputData.GetDataReadReference(VertexKey); return MakeUnique>(InputNode.GetVertexName(), DataRef); } else { // Create write reference by calling compatible constructor with literal. TDataWriteReference DataRef = ReferenceCreator.CreateDataReference(InParams.OperatorSettings); return MakeUnique>(InputNode.GetVertexName(), DataRef); } } } // namespace Metasound #undef LOCTEXT_NAMESPACE // MetasoundFrontend