// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "MetasoundBasicNode.h" #include "MetasoundBuilderInterface.h" #include "MetasoundBuildError.h" #include "MetasoundDataReference.h" #include "MetasoundExecutableOperator.h" #include "MetasoundFrontendDataTypeTraits.h" #include "MetasoundNodeConstructorParams.h" #include "MetasoundLiteral.h" #include "MetasoundNodeInterface.h" #include "MetasoundOperatorInterface.h" #include "MetasoundVertexData.h" #include "UObject/NameTypes.h" #define UE_API METASOUNDFRONTEND_API namespace Metasound { namespace MetasoundInputNodePrivate { class FNonExecutableInputOperatorBase : public IOperator { public: UE_API virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override; UE_API virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override; UE_API virtual IOperator::FExecuteFunction GetExecuteFunction() override; UE_API virtual IOperator::FPostExecuteFunction GetPostExecuteFunction() override; UE_API virtual IOperator::FResetFunction GetResetFunction() override; protected: UE_API FNonExecutableInputOperatorBase(const FVertexName& InVertexName, FAnyDataReference&& InDataRef); FVertexName VertexName; FAnyDataReference DataRef; }; class FNonExecutableInputPassThroughOperator : public FNonExecutableInputOperatorBase { public: template FNonExecutableInputPassThroughOperator(const FVertexName& InVertexName, const TDataReadReference& InDataRef) : FNonExecutableInputOperatorBase(InVertexName, FAnyDataReference{InDataRef}) { } template FNonExecutableInputPassThroughOperator(const FVertexName& InVertexName, const TDataWriteReference& InDataRef) : FNonExecutableInputPassThroughOperator(InVertexName, TDataReadReference(InDataRef)) { } }; /** TInputValueOperator provides an input for value references. */ template class TInputValueOperator : public FNonExecutableInputOperatorBase { public: /** Construct an TInputValueOperator with the name of the vertex and the * value reference associated with input. */ explicit TInputValueOperator(const FName& InVertexName, const TDataValueReference& InValueRef) : FNonExecutableInputOperatorBase(InVertexName, FAnyDataReference{InValueRef}) { } TInputValueOperator(const FVertexName& InVertexName, const FOperatorSettings& InSettings, const FLiteral& InLiteral) : FNonExecutableInputOperatorBase(InVertexName, FAnyDataReference{TDataValueReferenceLiteralFactory::CreateExplicitArgs(InSettings, InLiteral)}) { } TInputValueOperator(const FVertexName& InVertexName, const FOperatorSettings& InSettings, const FInputVertexInterfaceData& InInterfaceData) : FNonExecutableInputOperatorBase(InVertexName, FAnyDataReference{InInterfaceData.GetOrCreateDefaultDataValueReference(InVertexName, InSettings)}) { } }; template class TPostExecutableInputOperator : public IOperator { static_assert(TPostExecutableDataType::bIsPostExecutable, "TPostExecutableInputOperator should only be used with post executable data types"); public: using FDataWriteReference = TDataWriteReference; TPostExecutableInputOperator(const FVertexName& InDataReferenceName, TDataWriteReference InValue) : DataReferenceName(InDataReferenceName) , DataRef(InValue) { } virtual void BindInputs(FInputVertexInterfaceData& InOutVertexData) override { InOutVertexData.BindVertex(DataReferenceName, DataRef); } virtual void BindOutputs(FOutputVertexInterfaceData& InOutVertexData) override { InOutVertexData.BindVertex(DataReferenceName, DataRef); } virtual FExecuteFunction GetExecuteFunction() override { return nullptr; } virtual FPostExecuteFunction GetPostExecuteFunction() override { // This condition is checked at runtime as its possible dynamic graphs may reassign ownership // of underlying data to operate on in post execute. In this case, the expectation is that the // data reference is now owned by another provider/operator. if (DataRef.GetAccessType() == EDataReferenceAccessType::Write) { return &PostExecute; } else { return nullptr; } } virtual FResetFunction GetResetFunction() override { // This condition is checked at runtime as its possible dynamic graphs may reassign ownership // of underlying data to operate on in post execute. In this case, the expectation is that the // data reference is now owned by another provider/operator. if (DataRef.GetAccessType() == EDataReferenceAccessType::Write) { return &NoOpReset; } else { return nullptr; } } protected: static void NoOpReset(IOperator* InOperator, const IOperator::FResetParams& InParams) { // All post executable nodes must have a reset. This is a special // case of a non-owning node performing post execute on a data type // owned by an external system. } static void PostExecute(IOperator* InOperator) { using FPostExecutableInputOperator = TPostExecutableInputOperator; FPostExecutableInputOperator* DerivedOperator = static_cast(InOperator); check(nullptr != DerivedOperator); DataType* Value = DerivedOperator->DataRef.template GetWritableValue(); if (ensure(Value != nullptr)) { TPostExecutableDataType::PostExecute(*Value); } } FVertexName DataReferenceName; FAnyDataReference DataRef; }; // To reset the state of a PostExecutable input operator, we need to reset // the data to it's original state. In order to do that, the FLiteral is // stored on the operator so that it can be used to reinitialize the data // when the operator reset. template class TResetablePostExecutableInputOperator : public TPostExecutableInputOperator { public: using FDataWriteReference = TDataWriteReference; using FDataWriteReferenceFactory = TDataWriteReferenceLiteralFactory; using TPostExecutableInputOperator::DataRef; TResetablePostExecutableInputOperator(const FVertexName& InDataReferenceName, const FOperatorSettings& InSettings, const FLiteral& InLiteral) : TPostExecutableInputOperator(InDataReferenceName, FDataWriteReferenceFactory::CreateExplicitArgs(InSettings, InLiteral)) , Literal(InLiteral) { } TResetablePostExecutableInputOperator(const FVertexName& InDataReferenceName, const FOperatorSettings& InSettings, const FInputVertexInterfaceData& InData) : TPostExecutableInputOperator(InDataReferenceName, InData.GetOrCreateDefaultDataWriteReference(InDataReferenceName, InSettings)) , Literal(InData.GetVertex(InDataReferenceName).GetDefaultLiteral()) { checkf(!InData.IsVertexBound(InDataReferenceName), TEXT("Vertex %s should not be bound when using TResetablePostExecutableInputOperator"), *InDataReferenceName.ToString()); } virtual IOperator::FResetFunction GetResetFunction() override { if (DataRef.GetAccessType() == EDataReferenceAccessType::Write) { return &Reset; } else { // If DataRef is not writable, reference is assumed to be reset by another owning operator. return nullptr; } } private: static void Reset(IOperator* InOperator, const IOperator::FResetParams& InParams) { using FResetablePostExecutableInputOperator = TResetablePostExecutableInputOperator; FResetablePostExecutableInputOperator* Operator = static_cast(InOperator); check(nullptr != Operator); DataType* Value = Operator->DataRef.template GetWritableValue(); if (ensure(Value != nullptr)) { *Value = TDataTypeLiteralFactory::CreateExplicitArgs(InParams.OperatorSettings, Operator->Literal); } } FLiteral Literal; }; /** Non owning input operator that may need execution. */ template using TNonOwningInputOperator = std::conditional_t< TPostExecutableDataType::bIsPostExecutable, TPostExecutableInputOperator, // Use this input operator if the data type is not owned by the input node but needs post execution. MetasoundInputNodePrivate::FNonExecutableInputPassThroughOperator // Use this input operator if the data type is not owned by the input node and is not executable, nor post executable. >; } /** Owning input operator that may need execution. */ template using TInputOperator = std::conditional_t< VertexAccess == EVertexAccessType::Value || !TPostExecutableDataType::bIsPostExecutable, MetasoundInputNodePrivate::TInputValueOperator, // Use this input operator if the data type is owned by the input node and is not executable, nor post executable. MetasoundInputNodePrivate::TResetablePostExecutableInputOperator // Use this input operator if the data type is owned by the input node and is post executable. >; /** Choose pass through operator based upon data type and access type */ template using TPassThroughOperator = std::conditional_t< VertexAccess == EVertexAccessType::Value, MetasoundInputNodePrivate::TInputValueOperator, MetasoundInputNodePrivate::FNonExecutableInputPassThroughOperator >; namespace MetasoundInputNodePrivate { // Factory for creating input operators. template class TInputNodeOperatorFactory : 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 FPassThroughDataReference = std::conditional_t, TDataValueReference>; // Return correct data reference type based on vertex access type for pass through scenario. FPassThroughDataReference CreatePassThroughDataReference(const FAnyDataReference& InRef) { if constexpr (bIsReferenceVertexAccess) { return InRef.GetDataReadReference(); } else if constexpr (bIsValueVertexAccess) { return InRef.GetDataValueReference(); } else { static_assert("Unsupported EVertexAccessType"); } } public: explicit TInputNodeOperatorFactory(const FVertexName& InVertexName) : VertexName(InVertexName) { } virtual TUniquePtr CreateOperator(const FBuildOperatorParams& InParams, FBuildResults& OutResults) override { using namespace MetasoundInputNodePrivate; if (const FAnyDataReference* Ref = InParams.InputData.FindDataReference(VertexName)) { if constexpr (bIsReferenceVertexAccess) { if (EDataReferenceAccessType::Write == Ref->GetAccessType()) { return MakeUnique>(VertexName, Ref->GetDataWriteReference()); } } // Pass through input value return MakeUnique>(VertexName, CreatePassThroughDataReference(*Ref)); } else { // Owned input value return MakeUnique>(VertexName, InParams.OperatorSettings, InParams.InputData); } } private: FVertexName VertexName; }; } /** FInputNode represents an input to a metasound graph. */ class FInputNode : public FBasicNode { static FLazyName ConstructorVariant; // Use Variant names to differentiate between normal input nodes and constructor // input nodes. static FName GetVariantName(EVertexAccessType InVertexAccess); static FVertexInterface CreateVertexInterface(const FVertexName& InVertexName, const FName& InDataTypeName, EVertexAccessType InVertexAccess, const FLiteral& InLiteral); protected: static UE_API FVertexInterface CreateDefaultVertexInterface(const FVertexName& InVertexName, const FName& InDataTypeName, EVertexAccessType InVertexAccess, const FLiteral* InDefaultLiteral=nullptr); UE_API explicit FInputNode(FOperatorFactorySharedRef InFactory, FNodeData InNodeData, TSharedRef InClassMetadata); public: static UE_API FText GetInputDescription(); UE_DEPRECATED(5.6, "Use CreateNodeClassMetadata(...) instead") static UE_API FNodeClassMetadata GetNodeMetadata(const FVertexName& InVertexName, const FName& InDataTypeName, EVertexAccessType InVertexAccess); static UE_API FNodeClassMetadata CreateNodeClassMetadata(const FVertexName& InVertexName, const FName& InDataTypeName, EVertexAccessType InVertexAccess); /* Construct a TInputNode using the TInputOperatorLiteralFactory<> and moving * InParam to the TInputOperatorLiteralFactory constructor.*/ UE_API explicit FInputNode(FInputNodeConstructorParams&& InParams, const FName& InDataTypeName, EVertexAccessType InVertexAccess, FOperatorFactorySharedRef InFactory); UE_DEPRECATED(5.6, "You can find the vertex name by inspecting the FVertexInterface.") UE_API const FVertexName& GetVertexName() const; UE_API virtual TSharedRef GetDefaultOperatorFactory() const override; private: FOperatorFactorySharedRef Factory; }; /** TInputNode represents an input to a metasound graph. */ template class TInputNode : public FInputNode { 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; public: // If true, this node can be instantiated by the Frontend. static constexpr bool bCanRegister = bIsSupportedInput; UE_DEPRECATED(5.6, "Use CreateNodeClassMetadata(...) instead") static FNodeClassMetadata GetNodeInfo(const FVertexName& InVertexName) { return FInputNode::CreateNodeClassMetadata(InVertexName, GetMetasoundDataTypeName(), VertexAccess); } static FNodeClassMetadata CreateNodeClassMetadata(const FVertexName& InVertexName) { return FInputNode::CreateNodeClassMetadata(InVertexName, GetMetasoundDataTypeName(), VertexAccess); } /* Construct a TInputNode using the TInputOperatorLiteralFactory<> and moving * InParam to the TInputOperatorLiteralFactory constructor.*/ explicit TInputNode(FInputNodeConstructorParams&& InParams) : FInputNode(MoveTemp(InParams), GetMetasoundDataTypeName(), VertexAccess, MakeShared>(InParams.VertexName)) { } explicit TInputNode(const FVertexName& InVertexName, FNodeData InNodeData, TSharedRef InClassMetadata) : FInputNode(MakeShared>(InVertexName), MoveTemp(InNodeData), MoveTemp(InClassMetadata)) { } }; } // namespace Metasound #undef UE_API