// Copyright Epic Games, Inc. All Rights Reserved. #include "RigVMCompiler/RigVMAST.h" #include "RigVMCompiler/RigVMCompiler.h" #include "RigVMModel/Nodes/RigVMUnitNode.h" #include "RigVMModel/Nodes/RigVMParameterNode.h" #include "RigVMModel/Nodes/RigVMVariableNode.h" #include "RigVMModel/Nodes/RigVMCommentNode.h" #include "RigVMModel/Nodes/RigVMRerouteNode.h" #include "RigVMModel/Nodes/RigVMEnumNode.h" #include "RigVMModel/Nodes/RigVMFunctionReturnNode.h" #include "RigVMModel/Nodes/RigVMFunctionEntryNode.h" #include "RigVMModel/Nodes/RigVMInvokeEntryNode.h" #include "RigVMModel/RigVMGraph.h" #include "RigVMModel/RigVMController.h" #include "RigVMCore/RigVMExecuteContext.h" #include "Stats/StatsHierarchical.h" #include "RigVMDeveloperModule.h" #include "VisualGraphUtils.h" #include "RigVMModel/Nodes/RigVMDispatchNode.h" #include "UObject/FieldIterator.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMAST) FRigVMExprAST::FRigVMExprAST(EType InType, const FRigVMASTProxy& InProxy) : Name(NAME_None) , Type(InType) , Index(INDEX_NONE) { } FName FRigVMExprAST::GetTypeName() const { switch (GetType()) { case EType::Block: { return TEXT("[.Block.]"); } case EType::Entry: { return TEXT("[.Entry.]"); } case EType::CallExtern: { return TEXT("[.Call..]"); } case EType::InlineFunction: { return TEXT("[.Inline..]"); } case EType::NoOp: { return TEXT("[.NoOp..]"); } case EType::Var: { return TEXT("[.Var...]"); } case EType::Literal: { return TEXT("[Literal]"); } case EType::ExternalVar: { return TEXT("[ExtVar.]"); } case EType::Assign: { return TEXT("[.Assign]"); } case EType::Copy: { return TEXT("[.Copy..]"); } case EType::CachedValue: { return TEXT("[.Cache.]"); } case EType::Exit: { return TEXT("[.Exit..]"); } case EType::Invalid: { return TEXT("[Invalid]"); } default: { ensure(false); } } return NAME_None; } const FRigVMExprAST* FRigVMExprAST::GetParent() const { if (Parents.Num() > 0) { return ParentAt(0); } return nullptr; } const FRigVMExprAST* FRigVMExprAST::GetFirstParentOfType(EType InExprType) const { for(const FRigVMExprAST* Parent : Parents) { if (Parent->IsA(InExprType)) { return Parent; } } for (const FRigVMExprAST* Parent : Parents) { if (const FRigVMExprAST* GrandParent = Parent->GetFirstParentOfType(InExprType)) { return GrandParent; } } return nullptr; } const FRigVMExprAST* FRigVMExprAST::GetFirstChildOfType(EType InExprType) const { for (const FRigVMExprAST* Child : Children) { if (Child->IsA(InExprType)) { return Child; } } for (const FRigVMExprAST* Child : Children) { if (const FRigVMExprAST* GrandChild = Child->GetFirstChildOfType(InExprType)) { return GrandChild; } } return nullptr; } const FRigVMBlockExprAST* FRigVMExprAST::GetBlock() const { if (Parents.Num() == 0) { if (IsA(EType::Block)) { return To(); } return ParserPtr->GetObsoleteBlock(); } const FRigVMExprAST* Parent = GetParent(); if (Parent->IsA(EType::Block)) { return Parent->To(); } return Parent->GetBlock(); } const FRigVMBlockExprAST* FRigVMExprAST::GetRootBlock() const { const FRigVMBlockExprAST* Block = GetBlock(); if (IsA(EType::Block)) { if (Block && NumParents() > 0) { return Block->GetRootBlock(); } return To(); } if (Block) { return Block->GetRootBlock(); } return nullptr; } int32 FRigVMExprAST::GetMinChildIndexWithinParent(const FRigVMExprAST* InParentExpr) const { int32 MinIndex = INDEX_NONE; const TTuple MapKey(InParentExpr, this); const FRigVMParserAST* Parser = GetParser(); if(Parser) { if(const int32* IndexPtr = Parser->MinIndexOfChildWithinParent.Find(MapKey)) { return *IndexPtr; } } for (const FRigVMExprAST* Parent : Parents) { int32 ChildIndex = INDEX_NONE; if (Parent == InParentExpr) { Parent->Children.Find((FRigVMExprAST*)this, ChildIndex); } else { ChildIndex = Parent->GetMinChildIndexWithinParent(InParentExpr); } if (ChildIndex != INDEX_NONE) { if (ChildIndex < MinIndex || MinIndex == INDEX_NONE) { MinIndex = ChildIndex; } } } if(Parser) { Parser->MinIndexOfChildWithinParent.Add(MapKey, MinIndex); } return MinIndex; } void FRigVMExprAST::AddParent(FRigVMExprAST* InParent) { ensure(IsValid()); ensure(InParent->IsValid()); ensure(InParent != this); if (Parents.Contains(InParent)) { return; } InParent->Children.Add(this); Parents.Add(InParent); } void FRigVMExprAST::RemoveParent(FRigVMExprAST* InParent) { ensure(IsValid()); ensure(InParent->IsValid()); if (Parents.Remove(InParent) > 0) { const int32 ExistingIndex = InParent->Children.Find(this); if(ExistingIndex != INDEX_NONE) { FName NameToRemove(NAME_None); for(TPair& Pair: InParent->PinNameToChildIndex) { if(Pair.Value > ExistingIndex) { Pair.Value--; } else if(Pair.Value == ExistingIndex) { NameToRemove = Pair.Key; } } InParent->PinNameToChildIndex.Remove(NameToRemove); } InParent->Children.Remove(this); } } void FRigVMExprAST::RemoveChild(FRigVMExprAST* InChild) { ensure(IsValid()); ensure(InChild->IsValid()); InChild->RemoveParent(this); } void FRigVMExprAST::ReplaceParent(FRigVMExprAST* InCurrentParent, FRigVMExprAST* InNewParent) { ensure(IsValid()); ensure(InCurrentParent->IsValid()); ensure(InNewParent->IsValid()); for (int32 ParentIndex = 0; ParentIndex < Parents.Num(); ParentIndex++) { if (Parents[ParentIndex] == InCurrentParent) { Parents[ParentIndex] = InNewParent; InCurrentParent->Children.Remove(this); InNewParent->Children.Add(this); } } } void FRigVMExprAST::ReplaceChild(FRigVMExprAST* InCurrentChild, FRigVMExprAST* InNewChild) { ensure(IsValid()); ensure(InCurrentChild->IsValid()); ensure(InNewChild->IsValid()); for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ChildIndex++) { if (Children[ChildIndex] == InCurrentChild) { Children[ChildIndex] = InNewChild; InCurrentChild->Parents.Remove(this); InNewChild->Parents.Add(this); } } } void FRigVMExprAST::ReplaceBy(FRigVMExprAST* InReplacement) { TArray PreviousParents; PreviousParents.Append(Parents); for (FRigVMExprAST* PreviousParent : PreviousParents) { PreviousParent->ReplaceChild(this, InReplacement); } } bool FRigVMExprAST::IsConstant() const { for (FRigVMExprAST* ChildExpr : Children) { if (!ChildExpr->IsConstant()) { return false; } } return true; } FString FRigVMExprAST::DumpText(const FString& InPrefix) const { FString Result; if (Name.IsNone()) { Result = FString::Printf(TEXT("%s%s"), *InPrefix, *GetTypeName().ToString()); } else { Result = FString::Printf(TEXT("%s%s %s"), *InPrefix, *GetTypeName().ToString(), *Name.ToString()); } if (Children.Num() > 0) { FString Prefix = InPrefix; if (Prefix.IsEmpty()) { Prefix = TEXT("-- "); } else { Prefix = TEXT("---") + Prefix; } for (FRigVMExprAST* Child : Children) { Result += TEXT("\n") + Child->DumpText(Prefix); } } return Result; } bool FRigVMBlockExprAST::ShouldExecute() const { return ContainsEntry(); } bool FRigVMBlockExprAST::ContainsEntry() const { if (IsA(FRigVMExprAST::EType::Entry)) { return true; } for (FRigVMExprAST* Expression : *this) { if (Expression->IsA(EType::Entry)) { return true; } } return false; } bool FRigVMBlockExprAST::Contains(const FRigVMExprAST* InExpression, TMap* ContainedExpressionsCache) const { if (InExpression == this) { return true; } if (ContainedExpressionsCache) { if (bool* Result = ContainedExpressionsCache->Find(InExpression)) { return *Result; } } for (int32 ParentIndex = 0; ParentIndex < InExpression->NumParents(); ParentIndex++) { const FRigVMExprAST* ParentExpr = InExpression->ParentAt(ParentIndex); if (Contains(ParentExpr, ContainedExpressionsCache)) { if (ContainedExpressionsCache) { ContainedExpressionsCache->Add(InExpression, true); } return true; } } if (ContainedExpressionsCache) { ContainedExpressionsCache->Add(InExpression, false); } return false; } bool FRigVMNodeExprAST::IsConstant() const { if (URigVMNode* CurrentNode = GetNode()) { if (CurrentNode->IsDefinedAsConstant()) { return true; } else if (CurrentNode->IsDefinedAsVarying()) { return false; } TArray AllPins = CurrentNode->GetAllPinsRecursively(); for (URigVMPin* Pin : AllPins) { // don't flatten pins which have a watch if(Pin->RequiresWatch(false)) { return false; } } } return FRigVMExprAST::IsConstant(); } const FRigVMExprAST* FRigVMNodeExprAST::FindExprWithPinName(const FName& InPinName) const { if(const int32* PinIndex = PinNameToChildIndex.Find(InPinName)) { return ChildAt(*PinIndex); } if(URigVMNode* CurrentNode = GetNode()) { if(const URigVMPin* Pin = CurrentNode->FindPin(InPinName.ToString())) { const int32 PinIndex = Pin->GetPinIndex(); if(PinIndex <= NumChildren()) { return ChildAt(PinIndex); } } } return nullptr; } const FRigVMVarExprAST* FRigVMNodeExprAST::FindVarWithPinName(const FName& InPinName) const { const FRigVMExprAST* Child = FindExprWithPinName(InPinName); if (Child->IsA(FRigVMExprAST::Var)) { return Child->To(); } return nullptr; } FRigVMNodeExprAST::FRigVMNodeExprAST(EType InType, const FRigVMASTProxy& InNodeProxy) : FRigVMBlockExprAST(InType) , Proxy(InNodeProxy) { } FName FRigVMEntryExprAST::GetEventName() const { if (URigVMNode* EventNode = GetNode()) { return EventNode->GetEventName(); } return NAME_None; } bool FRigVMVarExprAST::IsConstant() const { if (GetPin()->IsExecuteContext()) { return false; } if (GetPin()->IsDefinedAsConstant()) { return true; } if (SupportsSoftLinks()) { return false; } ERigVMPinDirection Direction = GetPin()->GetDirection(); if (Direction == ERigVMPinDirection::Hidden) { if (Cast(GetPin()->GetNode())) { if (GetPin()->GetName() == URigVMVariableNode::VariableName) { return true; } } return false; } if (GetPin()->GetDirection() == ERigVMPinDirection::IO || GetPin()->GetDirection() == ERigVMPinDirection::Output) { if (GetPin()->GetNode()->IsDefinedAsVarying()) { return false; } } return FRigVMExprAST::IsConstant(); } FString FRigVMVarExprAST::GetCPPType() const { return GetPin()->GetCPPType(); } UObject* FRigVMVarExprAST::GetCPPTypeObject() const { return GetPin()->GetCPPTypeObject(); } ERigVMPinDirection FRigVMVarExprAST::GetPinDirection() const { return GetPin()->GetDirection(); } FString FRigVMVarExprAST::GetDefaultValue() const { return GetPin()->GetDefaultValue(URigVMPin::FPinOverride(GetProxy(), GetParser()->GetPinOverrides())); } bool FRigVMVarExprAST::IsExecuteContext() const { return GetPin()->IsExecuteContext(); } bool FRigVMVarExprAST::IsGraphVariable() const { if (Cast(GetPin()->GetNode())) { return GetPin()->GetName() == URigVMVariableNode::ValueName; } return false; } bool FRigVMVarExprAST::IsEnumValue() const { if (Cast(GetPin()->GetNode())) { return GetPin()->GetName() == TEXT("EnumIndex"); } return false; } bool FRigVMVarExprAST::SupportsSoftLinks() const { if (const URigVMNode* Node = Cast(GetPin()->GetNode())) { if (Node->IsControlFlowNode()) { return !Node->GetControlFlowBlocks().Contains(GetPin()->GetFName()); } } return false; } void FRigVMParserASTSettings::Report(EMessageSeverity::Type InSeverity, UObject* InSubject, const FString& InMessage) const { if (ReportDelegate.IsBound()) { ReportDelegate.Execute(InSeverity, InSubject, InMessage); } else { if (InSeverity == EMessageSeverity::Error) { FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, *InMessage, *FString()); } else if (InSeverity == EMessageSeverity::Warning) { FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Warning, *InMessage, *FString()); } else { UE_LOG(LogRigVMDeveloper, Display, TEXT("%s"), *InMessage); } } } const TArray FRigVMParserAST::EmptyProxyArray; FRigVMParserAST::FRigVMParserAST(TArray InGraphs, URigVMController* InController, const FRigVMParserASTSettings& InSettings, const TArray& InExternalVariables) : LibraryNodeBeingCompiled(nullptr) { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() if (InGraphs.Num() == 1 && InGraphs[0]->GetTypedOuter() != nullptr) { LibraryNodeBeingCompiled = Cast(InGraphs[0]->GetOuter()); } Settings = InSettings; ObsoleteBlock = nullptr; LastCycleCheckExpr = nullptr; LinksToSkip = InSettings.LinksToSkip; check(!InGraphs.IsEmpty()); // construct the inlined nodes and links information Inline(InGraphs); if (LibraryNodeBeingCompiled == nullptr) { for (const FRigVMASTProxy& NodeProxy : NodeProxies) { URigVMNode* Node = NodeProxy.GetSubjectChecked(); if(Node->IsEvent()) { TraverseMutableNode(NodeProxy, nullptr); } } } else { URigVMFunctionEntryNode* Entry = LibraryNodeBeingCompiled->GetEntryNode(); URigVMFunctionReturnNode* Return = LibraryNodeBeingCompiled->GetReturnNode(); if (Entry && Entry->IsMutable()) { for (const FRigVMASTProxy& NodeProxy : NodeProxies) { URigVMNode* Node = NodeProxy.GetSubjectChecked(); if(Node == Entry) { TraverseMutableNode(NodeProxy, nullptr); break; } } } else if (Return) { for (const FRigVMASTProxy& NodeProxy : NodeProxies) { URigVMNode* Node = NodeProxy.GetSubjectChecked(); if(Node == Return) { TraverseNode(NodeProxy, nullptr); break; } } } else { return; } } // traverse all remaining mutable nodes, // followed by a pass for all remaining non-mutable nodes for (int32 PassIndex = 0; PassIndex < 2; PassIndex++) { const bool bTraverseMutable = PassIndex == 0; for (int32 NodeIndex = 0; NodeIndex < NodeProxies.Num(); NodeIndex++) { if (const int32* ExprIndex = NodeExpressionIndex.Find(NodeProxies[NodeIndex])) { if (*ExprIndex != INDEX_NONE) { continue; } } URigVMNode* Node = NodeProxies[NodeIndex].GetSubjectChecked(); if (Node->IsMutable() == bTraverseMutable) { if (bTraverseMutable) { TraverseMutableNode(NodeProxies[NodeIndex], GetObsoleteBlock()); } else { TraverseNode(NodeProxies[NodeIndex], GetObsoleteBlock()); } } } } FoldEntries(); InjectExitsToEntries(); FoldNoOps(); BubbleUpExpressions(); if (InSettings.bFoldAssignments) { FoldAssignments(); } if (InSettings.bFoldLiterals) { FoldLiterals(); } } FRigVMParserAST::FRigVMParserAST(TArray InGraphs, const TArray& InNodesToCompute) : LibraryNodeBeingCompiled(nullptr) { check(!InGraphs.IsEmpty()); LastCycleCheckExpr = nullptr; FRigVMBlockExprAST* Block = MakeExpr(FRigVMExprAST::EType::Block, FRigVMASTProxy()); Block->Name = TEXT("NodesToCompute"); RootExpressions.Add(Block); NodeProxies = InNodesToCompute; Inline(InGraphs, InNodesToCompute); for (const FRigVMASTProxy& NodeProxy : NodeProxies) { URigVMNode* Node = NodeProxy.GetSubjectChecked(); if (Node->IsEvent()) { continue; } if (Node->IsMutable()) { continue; } TraverseNode(NodeProxy, Block); } FRigVMExprAST* ExitExpr = MakeExpr(FRigVMASTProxy()); ExitExpr->AddParent(Block); } FRigVMParserAST::~FRigVMParserAST() { for (FRigVMExprAST* Expression : Expressions) { delete(Expression); } Expressions.Empty(); for (FRigVMExprAST* Expression : DeletedExpressions) { delete(Expression); } DeletedExpressions.Empty(); // root expressions are a subset of the // expressions array, so no cleanup necessary RootExpressions.Empty(); } FRigVMExprAST* FRigVMParserAST::TraverseMutableNode(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr) { if (SubjectToExpression.Contains(InNodeProxy)) { return SubjectToExpression.FindChecked(InNodeProxy); } URigVMNode* Node = InNodeProxy.GetSubjectChecked(); if(Node->HasOrphanedPins()) { return nullptr; } FRigVMExprAST* NodeExpr = CreateExpressionForNode(InNodeProxy, InParentExpr); if (NodeExpr) { if (InParentExpr == nullptr) { InParentExpr = NodeExpr; } TraversePins(InNodeProxy, NodeExpr); TArray SourcePins = Node->GetPins().FilterByPredicate([](const URigVMPin* InPin) -> bool { return (InPin->GetDirection() == ERigVMPinDirection::Output || InPin->GetDirection() == ERigVMPinDirection::IO) && InPin->IsExecuteContext(); }); for(int32 SourcePinIndex = 0; SourcePinIndex < SourcePins.Num(); SourcePinIndex++) { URigVMPin* SourcePin = SourcePins[SourcePinIndex]; FRigVMASTProxy SourcePinProxy = InNodeProxy.GetSibling(SourcePin); FRigVMExprAST* ParentExpr = InParentExpr; if(SourcePin->IsFixedSizeArray()) { // also process elements of execute fixed arrays SourcePins.Append(SourcePin->GetSubPins()); } else if (Node->IsControlFlowNode()) { if (FRigVMExprAST** PinExpr = SubjectToExpression.Find(SourcePinProxy)) { FRigVMBlockExprAST* BlockExpr = MakeExpr(FRigVMExprAST::EType::Block, FRigVMASTProxy()); BlockExpr->AddParent(*PinExpr); BlockExpr->Name = SourcePin->GetFName(); ParentExpr = BlockExpr; } } const TArray& LinkIndices = GetTargetLinkIndices(SourcePinProxy); for(const int32 LinkIndex : LinkIndices) { const FRigVMASTLinkDescription& Link = Links[LinkIndex]; if(ShouldLinkBeSkipped(Link)) { continue; } URigVMNode* TargetNode = Link.TargetProxy.GetSubjectChecked()->GetNode(); FRigVMASTProxy TargetNodeProxy = Link.TargetProxy.GetSibling(TargetNode); TraverseMutableNode(TargetNodeProxy, ParentExpr); } } } return NodeExpr; } FRigVMExprAST* FRigVMParserAST::TraverseNode(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr) { URigVMNode* Node = InNodeProxy.GetSubjectChecked(); if (Cast(Node)) { return nullptr; } if(Node->HasOrphanedPins()) { return nullptr; } if (SubjectToExpression.Contains(InNodeProxy)) { FRigVMExprAST* NodeExpr = SubjectToExpression.FindChecked(InNodeProxy); NodeExpr->AddParent(InParentExpr); return NodeExpr; } FRigVMExprAST* NodeExpr = CreateExpressionForNode(InNodeProxy, InParentExpr); if (NodeExpr) { TraversePins(InNodeProxy, NodeExpr); } return NodeExpr; } FRigVMExprAST* FRigVMParserAST::CreateExpressionForNode(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr) { URigVMNode* Node = InNodeProxy.GetSubjectChecked(); bool bIsEntry = Node->IsEvent(); if (LibraryNodeBeingCompiled) { if (Node->GetTypedOuter() == LibraryNodeBeingCompiled) { if (URigVMFunctionEntryNode* EntryNode = Cast(Node)) { if (EntryNode->IsMutable()) { bIsEntry = true; } } if (URigVMFunctionReturnNode* ReturnNode = Cast(Node)) { if (!ReturnNode->IsMutable()) { bIsEntry = true; } } } } FRigVMExprAST* NodeExpr = nullptr; if (bIsEntry) { NodeExpr = MakeExpr(InNodeProxy); NodeExpr->Name = Node->GetEventName(); } else { if (InNodeProxy.IsA()) { NodeExpr = MakeExpr(InNodeProxy); } else if (InNodeProxy.IsA() || InNodeProxy.IsA() || InNodeProxy.IsA() || InNodeProxy.IsA() || InNodeProxy.IsA() || InNodeProxy.IsA()) { NodeExpr = MakeExpr(InNodeProxy); } else if (InNodeProxy.IsA()) { NodeExpr = MakeExpr(InNodeProxy); } else { NodeExpr = MakeExpr(InNodeProxy); } NodeExpr->Name = Node->GetFName(); } if (InParentExpr != nullptr) { NodeExpr->AddParent(InParentExpr); } else { RootExpressions.Add(NodeExpr); } SubjectToExpression.Add(InNodeProxy, NodeExpr); NodeExpressionIndex.Add(InNodeProxy, NodeExpr->GetIndex()); return NodeExpr; } TArray FRigVMParserAST::TraversePins(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr) { URigVMNode* Node = InNodeProxy.GetSubjectChecked(); TArray PinExpressions; TArray Pins; // traverse the pins on a unit node in the order of the property definitions if(const URigVMUnitNode* UnitNode = Cast(Node)) { if(UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct()) { TArray Structs = FRigVMTemplate::GetSuperStructs(ScriptStruct, true); for(const UStruct* Struct : Structs) { for (TFieldIterator PropertyIt(Struct, EFieldIterationFlags::None); PropertyIt; ++PropertyIt) { if(URigVMPin* Pin = UnitNode->FindPin(PropertyIt->GetName())) { Pins.Add(Pin); } } } } } if(Pins.IsEmpty()) { Pins = Node->GetPins(); } // dispatch nodes may contain a fixed size array if(Node->IsA() || Node->IsA()) { TArray FlattenedPins; FlattenedPins.Reserve(Pins.Num()); for(URigVMPin* Pin : Pins) { if(Pin->IsFixedSizeArray()) { FlattenedPins.Append(Pin->GetSubPins()); } else { FlattenedPins.Add(Pin); } } Swap(Pins, FlattenedPins); } for (URigVMPin* Pin : Pins) { FRigVMASTProxy PinProxy = InNodeProxy.GetSibling(Pin); PinExpressions.Add(TraversePin(PinProxy, InParentExpr)); if (InParentExpr && !Pin->IsRootPin()) { const FString PinName = FRigVMBranchInfo::GetFixedArrayLabel(Pin->GetParentPin()->GetName(), Pin->GetName()); const int32 ChildIndex = InParentExpr->Children.Find(PinExpressions.Last()); InParentExpr->PinNameToChildIndex.FindOrAdd(*PinName) = ChildIndex; } if(InParentExpr) { const int32 ChildIndex = InParentExpr->Children.Find(PinExpressions.Last()); InParentExpr->PinNameToChildIndex.FindOrAdd(Pin->GetFName()) = ChildIndex; } } return PinExpressions; } FRigVMExprAST* FRigVMParserAST::TraversePin(const FRigVMASTProxy& InPinProxy, FRigVMExprAST* InParentExpr) { ensure(!SubjectToExpression.Contains(InPinProxy)); URigVMPin* Pin = InPinProxy.GetSubjectChecked(); URigVMPin::FPinOverride PinOverride(InPinProxy, PinOverrides); TArray LinkIndices = GetSourceLinkIndices(InPinProxy, true); if (LinksToSkip.Num() > 0) { LinkIndices.RemoveAll([this](int32 LinkIndex) { return this->ShouldLinkBeSkipped(Links[LinkIndex]); } ); } FRigVMExprAST* PinExpr = nullptr; if (Cast(Pin->GetNode())) { if (Pin->GetName() == URigVMVariableNode::VariableName) { return nullptr; } } else if (Cast(Pin->GetNode())) { if (Pin->GetDirection() == ERigVMPinDirection::Visible) { return nullptr; } } if ((Pin->GetDirection() == ERigVMPinDirection::Input || Pin->GetDirection() == ERigVMPinDirection::Visible) && LinkIndices.Num() == 0) { if (Cast(Pin->GetNode())) { PinExpr = MakeExpr(FRigVMExprAST::EType::Var, InPinProxy); FRigVMExprAST* PinLiteralExpr = MakeExpr(InPinProxy); PinLiteralExpr->Name = PinExpr->Name; const FRigVMASTLinkDescription LiteralLink(InPinProxy, InPinProxy, FString()); FRigVMExprAST* PinCopyExpr = MakeExpr(LiteralLink); PinCopyExpr->AddParent(PinExpr); PinLiteralExpr->AddParent(PinCopyExpr); } else { PinExpr = MakeExpr(InPinProxy); } } else if (Cast(Pin->GetNode())) { PinExpr = MakeExpr(InPinProxy); } else { PinExpr = MakeExpr(FRigVMExprAST::EType::Var, InPinProxy); } PinExpr->AddParent(InParentExpr); PinExpr->Name = *Pin->GetPinPath(); SubjectToExpression.Add(InPinProxy, PinExpr); if (Pin->IsExecuteContext()) { return PinExpr; } if (PinExpr->IsA(FRigVMExprAST::ExternalVar)) { return PinExpr; } if ((Pin->GetDirection() == ERigVMPinDirection::IO || Pin->GetDirection() == ERigVMPinDirection::Input) && !Pin->IsExecuteContext()) { bool bHasSourceLinkToRoot = false; URigVMPin* RootPin = Pin->GetRootPin(); for (const int32 LinkIndex : LinkIndices) { if (Links[LinkIndex].TargetProxy.GetSubject() == RootPin) { bHasSourceLinkToRoot = true; break; } } if (!bHasSourceLinkToRoot && GetSourceLinkIndices(InPinProxy, false).Num() == 0 && (Pin->GetDirection() == ERigVMPinDirection::IO || LinkIndices.Num() > 0)) { FRigVMLiteralExprAST* LiteralExpr = MakeExpr(InPinProxy); const FRigVMASTLinkDescription LiteralLink(InPinProxy, InPinProxy, FString()); FRigVMCopyExprAST* LiteralCopyExpr = MakeExpr(LiteralLink); LiteralCopyExpr->Name = *GetLinkAsString(LiteralCopyExpr->GetLink()); LiteralCopyExpr->AddParent(PinExpr); LiteralExpr->AddParent(LiteralCopyExpr); LiteralExpr->Name = *Pin->GetPinPath(); SubjectToExpression[InPinProxy] = LiteralExpr; } } FRigVMExprAST* ParentExprForLinks = PinExpr; if(LinkIndices.Num() > 0) { if (Pin->IsLazy()) { // create a block for each lazily executing pin FRigVMBlockExprAST* BlockExpr = MakeExpr(FRigVMExprAST::EType::Block, FRigVMASTProxy()); BlockExpr->Name = Pin->GetFName(); BlockExpr->AddParent(PinExpr); ParentExprForLinks = BlockExpr; } else if (Pin->GetNode()->HasLazyPin(true)) { // for greedy pins on nodes containing a lazy pin - we need to also create a block for the node itself FRigVMBlockExprAST* BlockExpr = nullptr; if(const FRigVMExprAST* NodeExpr = PinExpr->GetFirstParentOfType(FRigVMExprAST::CallExtern)) { const FRigVMCallExternExprAST* CallExternExpr = NodeExpr->To(); // try to find the block under all non-lazy pins. // this is the block that is run before anything else - so for example // if you have a lazy interplate with values A and B being lazy and a greedy T blend pin, // you need a block to store the instructions related to computing T. once T is computed, // the callextern can run and lazily pull on A or B. for(const URigVMPin* NodePin : Pin->GetNode()->GetPins()) { if(NodePin == Pin) { continue; } if(!NodePin->IsLazy() && (NodePin->GetDirection() == ERigVMPinDirection::Input || NodePin->GetDirection() == ERigVMPinDirection::IO)) { if(const FRigVMExprAST* NodePinExpr = CallExternExpr->FindExprWithPinName(NodePin->GetFName())) { if(const FRigVMExprAST* ExistingBlockExpr = NodePinExpr->GetFirstChildOfType(FRigVMExprAST::Block)) { BlockExpr = (FRigVMBlockExprAST*)ExistingBlockExpr->To(); break; } } } } } if(BlockExpr == nullptr) { BlockExpr = MakeExpr(FRigVMExprAST::EType::Block, FRigVMASTProxy()); BlockExpr->Name = Pin->GetFName(); } if(BlockExpr) { BlockExpr->AddParent(PinExpr); ParentExprForLinks = BlockExpr; } } } for (const int32 LinkIndex : LinkIndices) { TraverseLink(LinkIndex, ParentExprForLinks); } return PinExpr; } FRigVMExprAST* FRigVMParserAST::TraverseLink(int32 InLinkIndex, FRigVMExprAST* InParentExpr) { const FRigVMASTLinkDescription& Link = Links[InLinkIndex]; const FRigVMASTProxy& SourceProxy = Link.SourceProxy; const FRigVMASTProxy& TargetProxy = Link.TargetProxy; URigVMPin* SourcePin = SourceProxy.GetSubjectChecked(); URigVMPin* TargetPin = TargetProxy.GetSubjectChecked(); URigVMPin* SourceRootPin = SourcePin->GetRootPin(); URigVMPin* TargetRootPin = TargetPin->GetRootPin(); FRigVMASTProxy SourceNodeProxy = SourceProxy.GetSibling(SourcePin->GetNode()); bool bRequiresCopy = SourceRootPin != SourcePin || TargetRootPin != TargetPin || !Link.SegmentPath.IsEmpty(); if (!bRequiresCopy) { if(Cast(TargetRootPin->GetNode())) { bRequiresCopy = true; } } FRigVMAssignExprAST* AssignExpr = nullptr; if (bRequiresCopy) { AssignExpr = MakeExpr(Link); } else { AssignExpr = MakeExpr(FRigVMExprAST::EType::Assign, Link); } AssignExpr->Name = *GetLinkAsString(Link); AssignExpr->AddParent(InParentExpr); FRigVMExprAST* NodeExpr = TraverseNode(SourceNodeProxy, AssignExpr); if (NodeExpr) { // if this is a copy expression - we should require the copy to use a ref instead if (NodeExpr->IsA(FRigVMExprAST::EType::CallExtern) || NodeExpr->IsA(FRigVMExprAST::EType::InlineFunction)) { for (FRigVMExprAST* ChildExpr : *NodeExpr) { if (ChildExpr->IsA(FRigVMExprAST::EType::Var)) { FRigVMVarExprAST* VarExpr = ChildExpr->To(); if (VarExpr->GetPin() == SourceRootPin) { if (VarExpr->SupportsSoftLinks()) { AssignExpr->ReplaceChild(NodeExpr, VarExpr); return AssignExpr; } FRigVMCachedValueExprAST* CacheExpr = nullptr; for (FRigVMExprAST* VarExprParent : VarExpr->Parents) { if (VarExprParent->IsA(FRigVMExprAST::EType::CachedValue)) { CacheExpr = VarExprParent->To(); break; } } if (CacheExpr == nullptr) { CacheExpr = MakeExpr(FRigVMASTProxy()); CacheExpr->Name = AssignExpr->GetName(); VarExpr->AddParent(CacheExpr); NodeExpr->AddParent(CacheExpr); } AssignExpr->ReplaceChild(NodeExpr, CacheExpr); return AssignExpr; } } } checkNoEntry(); } else if (LibraryNodeBeingCompiled && SourceRootPin->GetNode()->IsA() && SourceRootPin->GetTypedOuter() == LibraryNodeBeingCompiled) { FRigVMASTProxy SourceRootProxy = SourceProxy; if (SourceRootPin != SourcePin) { SourceRootProxy = FRigVMASTProxy::MakeFromUObject(SourceRootPin); } FRigVMVarExprAST* SourcePinExpr = MakeExpr(FRigVMExprAST::EType::Var, SourceRootProxy); AssignExpr->ReplaceChild(NodeExpr, SourcePinExpr); return AssignExpr; } } return AssignExpr; } void FRigVMParserAST::FoldEntries() { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() TArray FoldRootExpressions; TArray ExpressionsToRemove; TMap EntryByName; for (FRigVMExprAST* RootExpr : RootExpressions) { if (RootExpr->IsA(FRigVMExprAST::EType::Entry)) { FRigVMEntryExprAST* Entry = RootExpr->To(); if (EntryByName.Contains(Entry->GetEventName())) { FRigVMEntryExprAST* FoldEntry = EntryByName.FindChecked(Entry->GetEventName()); // replace the original entry with a noop FRigVMNoOpExprAST* NoOpExpr = MakeExpr(Entry->GetProxy()); NoOpExpr->AddParent(FoldEntry); NoOpExpr->Name = Entry->Name; SubjectToExpression.FindChecked(Entry->GetProxy()) = NoOpExpr; TArray Children = Entry->Children; // copy since the loop changes the array for (FRigVMExprAST* ChildExpr : Children) { ChildExpr->RemoveParent(Entry); if (ChildExpr->IsA(FRigVMExprAST::Var)) { if (ChildExpr->To()->IsExecuteContext()) { ExpressionsToRemove.AddUnique(ChildExpr); continue; } } ChildExpr->AddParent(FoldEntry); } ExpressionsToRemove.AddUnique(Entry); } else { FoldRootExpressions.Add(Entry); EntryByName.Add(Entry->GetEventName(), Entry); } } else { FoldRootExpressions.Add(RootExpr); } } RootExpressions = FoldRootExpressions; RemoveExpressions(ExpressionsToRemove); } void FRigVMParserAST::InjectExitsToEntries() { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() if (LibraryNodeBeingCompiled) { return; } for (FRigVMExprAST* RootExpr : RootExpressions) { if (RootExpr->IsA(FRigVMExprAST::EType::Entry)) { bool bHasExit = false; if (RootExpr->Children.Num() > 0) { if (RootExpr->Children.Last()->IsA(FRigVMExprAST::EType::Exit)) { bHasExit = true; break; } } if (!bHasExit) { FRigVMExprAST* ExitExpr = MakeExpr(FRigVMASTProxy()); ExitExpr->AddParent(RootExpr); } } } } void FRigVMParserAST::BubbleUpExpressions() { for (int32 ExpressionIndex = 0; ExpressionIndex < Expressions.Num(); ExpressionIndex++) { FRigVMExprAST* Expression = Expressions[ExpressionIndex]; if (!Expression->IsA(FRigVMExprAST::CachedValue)) { continue; } if (Expression->NumParents() < 2) { continue; } // collect all of the blocks this is in and make sure it's bubbled up before that TArray Blocks; for (int32 ParentIndex = 0; ParentIndex < Expression->NumParents(); ParentIndex++) { const FRigVMExprAST* ParentExpression = Expression->ParentAt(ParentIndex); if (ParentExpression->IsA(FRigVMExprAST::Block)) { Blocks.AddUnique((FRigVMBlockExprAST*)ParentExpression->To()); } else { Blocks.AddUnique((FRigVMBlockExprAST*)ParentExpression->GetBlock()); } } if (Blocks.Num() > 1) { TArray BlockCandidates; BlockCandidates.Append(Blocks); FRigVMBlockExprAST* OuterBlock = nullptr; // check if we need to add this expression to the block representing the non-lazy pins on the node. // the expressions in that block are supposed to run before the node can execute - since they are greedy. // note: for backwards compatibility this also works for the condition pin on the if node and the index pin // on the select node TArray Parents; TArray GrandParents; for(const FRigVMBlockExprAST* Block : Blocks) { const FRigVMExprAST* Parent = Block->GetParent(); Parents.AddUnique(Parent); if(Parent) { GrandParents.AddUnique(Parent->GetParent()); } } // if any of the parents is nullptr - it means that the block didn't // have a parent and thus is the top level block. in that case we cannot // bubble up further. if(!Parents.Contains(nullptr)) { // if all blocks here are part of the same grandparent // parent == pin, grandparent == node if(GrandParents.Num() == 1 && !GrandParents.Contains(nullptr)) { if(GrandParents[0]->IsA(FRigVMExprAST::EType::CallExtern)) { const FRigVMNodeExprAST* NodeExpr = GrandParents[0]->To(); // find a pin on the node which is either an input or an io and not lazy const URigVMNode* Node = NodeExpr->GetNode(); const FRigVMExprAST* TopLevelBlockExpression = nullptr; for(const URigVMPin* Pin : Node->GetPins()) { if(Pin->IsLazy()) { continue; } if(Pin->GetDirection() == ERigVMPinDirection::Input || Pin->GetDirection() == ERigVMPinDirection::IO) { if(const FRigVMExprAST* PinExpr = NodeExpr->FindExprWithPinName(Pin->GetFName())) { TopLevelBlockExpression = PinExpr->GetFirstChildOfType(FRigVMExprAST::Block); } } if(TopLevelBlockExpression) { break; } } if(TopLevelBlockExpression) { OuterBlock = (FRigVMBlockExprAST*)TopLevelBlockExpression->To(); OuterBlock->Children.Add(Expression); Expression->Parents.Insert(OuterBlock, 0); continue; } } } } // this expression is part of multiple blocks, and it needs to be bubbled up. // for this we'll walk up the block tree and find the first block which contains all of them for (int32 BlockCandidateIndex = 0; BlockCandidateIndex < BlockCandidates.Num(); BlockCandidateIndex++) { FRigVMBlockExprAST* BlockCandidate = BlockCandidates[BlockCandidateIndex]; bool bFoundCandidate = true; TMap ContainedExpressionsCache; for (int32 BlockIndex = 0; BlockIndex < Blocks.Num(); BlockIndex++) { FRigVMBlockExprAST* Block = Blocks[BlockIndex]; if (!BlockCandidate->Contains(Block, &ContainedExpressionsCache)) { bFoundCandidate = false; break; } } if (bFoundCandidate) { OuterBlock = BlockCandidate; break; } BlockCandidates.AddUnique((FRigVMBlockExprAST*)BlockCandidate->GetBlock()); } // we found a block which contains all of our blocks. // we are now going to inject this block as the first parent // of the cached value, so that the traverser sees it earlier if (OuterBlock) { MinIndexOfChildWithinParent.Reset(); int32 ChildIndex = Expression->GetMinChildIndexWithinParent(OuterBlock); if (ChildIndex != INDEX_NONE) { OuterBlock->Children.Insert(Expression, ChildIndex); Expression->Parents.Insert(OuterBlock, 0); } } } } } void FRigVMParserAST::RefreshExprIndices() { for (int32 Index = 0; Index < Expressions.Num(); Index++) { Expressions[Index]->Index = Index; } } void FRigVMParserAST::FoldNoOps() { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() for (FRigVMExprAST* Expression : Expressions) { if (Expression->IsA(FRigVMExprAST::EType::NoOp)) { if (URigVMNode* Node = Expression->To()->GetNode()) { if (URigVMVariableNode* VariableNode = Cast(Node)) { if (!VariableNode->IsGetter()) { continue; } } if (LibraryNodeBeingCompiled != nullptr) { if (URigVMFunctionEntryNode* EntryNode = Cast(Node)) { if (EntryNode->GetTypedOuter() == LibraryNodeBeingCompiled && EntryNode->IsMutable()) { continue; } } if (URigVMFunctionReturnNode* ReturnNode = Cast(Node)) { if (ReturnNode->GetTypedOuter() == LibraryNodeBeingCompiled && !ReturnNode->IsMutable()) { continue; } } } } // copy since we are changing the content during iteration below TArray Children = Expression->Children; TArray Parents = Expression->Parents; for (FRigVMExprAST* Parent : Parents) { Expression->RemoveParent(Parent); } for (FRigVMExprAST* Child : Children) { Child->RemoveParent(Expression); for (FRigVMExprAST* Parent : Parents) { Child->AddParent(Parent); } } } } } void FRigVMParserAST::FoldAssignments() { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() TArray ExpressionsToRemove; // first - Fold all assignment chains for (FRigVMExprAST* Expression : Expressions) { if (Expression->Parents.Num() == 0) { continue; } if (Expression->GetType() != FRigVMExprAST::EType::Assign) { continue; } FRigVMAssignExprAST* AssignExpr = Expression->To(); if (AssignExpr->Parents.Num() != 1 || AssignExpr->Children.Num() != 1) { continue; } URigVMPin* SourcePin = AssignExpr->GetSourcePin(); URigVMPin* TargetPin = AssignExpr->GetTargetPin(); // it's possible that we'll see copies from an element onto an array // here. we'll need to ignore these links and leave them since they // represent copies. if(SourcePin->IsArray() != TargetPin->IsArray()) { continue; } // in case the assign has different types for left and right - we need to avoid folding // since this assign represents a cast operation if(SourcePin->GetCPPTypeObject() != TargetPin->GetCPPTypeObject()) { continue; } else if(SourcePin->GetCPPTypeObject() == nullptr) { if(SourcePin->GetCPPType() != TargetPin->GetCPPType()) { continue; } } // non-input pins on anything but a reroute / array node should be skipped if (TargetPin->GetDirection() != ERigVMPinDirection::Input && (Cast(TargetPin->GetNode()) == nullptr)) { continue; } // if this node is a loop node - let's skip the folding if (const URigVMNode* ModelNode = TargetPin->GetNode()) { if (ModelNode->IsControlFlowNode()) { continue; } } // if this node is a variable node and the pin requires a watch... skip this if (Cast(SourcePin->GetNode())) { if(SourcePin->RequiresWatch(true)) { continue; } } FRigVMExprAST* Parent = AssignExpr->Parents[0]; if (!Parent->IsA(FRigVMExprAST::EType::Var)) { continue; } // To prevent bad assignments in LWC for VMs compiled in non LWC, we do not allow folding of assignments // to/from external variables of type float { if (SourcePin->GetCPPType() == TEXT("float") || SourcePin->GetCPPType() == TEXT("TArray")) { if (URigVMVariableNode* SourceVariableNode = Cast(SourcePin->GetNode())) { if (!SourceVariableNode->IsInputArgument() && !SourceVariableNode->IsLocalVariable()) { continue; } } } if (TargetPin->GetCPPType() == TEXT("float") || TargetPin->GetCPPType() == TEXT("TArray")) { if (URigVMVariableNode* TargetVariableNode = Cast(TargetPin->GetNode())) { if (!TargetVariableNode->IsInputArgument() && !TargetVariableNode->IsLocalVariable()) { continue; } } } } FRigVMExprAST* Child = AssignExpr->Children[0]; AssignExpr->RemoveParent(Parent); Child->RemoveParent(AssignExpr); TArray GrandParents = Parent->Parents; for (FRigVMExprAST* GrandParent : GrandParents) { GrandParent->ReplaceChild(Parent, Child); if (GrandParent->IsA(FRigVMExprAST::EType::Assign)) { FRigVMAssignExprAST* GrandParentAssign = GrandParent->To(); GrandParentAssign->Link = FRigVMASTLinkDescription(AssignExpr->GetSourceProxy(), GrandParentAssign->GetTargetProxy(), FString()); GrandParentAssign->Name = *GetLinkAsString(GrandParentAssign->GetLink()); } } ExpressionsToRemove.AddUnique(AssignExpr); if (Parent->Parents.Num() == 0) { ExpressionsToRemove.AddUnique(Parent); } } RemoveExpressions(ExpressionsToRemove); } void FRigVMParserAST::FoldLiterals() { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() TMap ValueToLiteral; TArray ExpressionsToRemove; for (int32 ExpressionIndex = 0; ExpressionIndex < Expressions.Num(); ExpressionIndex++) { FRigVMExprAST* Expression = Expressions[ExpressionIndex]; if (Expression->Parents.Num() == 0) { continue; } if (Expression->GetType() == FRigVMExprAST::EType::Literal) { ensure(Expression->Children.Num() == 0); FRigVMLiteralExprAST* LiteralExpr = Expression->To(); FString DefaultValue = LiteralExpr->GetDefaultValue(); if (DefaultValue.IsEmpty()) { if (LiteralExpr->GetCPPType() == TEXT("bool")) { DefaultValue = TEXT("False"); } else if (LiteralExpr->GetCPPType() == TEXT("float")) { DefaultValue = TEXT("0.000000"); } else if (LiteralExpr->GetCPPType() == TEXT("double")) { DefaultValue = TEXT("0.000000"); } else if (LiteralExpr->GetCPPType() == TEXT("int32")) { DefaultValue = TEXT("0"); } else { continue; } } FString Hash = FString::Printf(TEXT("[%s] %s"), *LiteralExpr->GetCPPType(), *DefaultValue); FRigVMLiteralExprAST* const* MappedExpr = ValueToLiteral.Find(Hash); if (MappedExpr) { TArray Parents = Expression->Parents; for (FRigVMExprAST* Parent : Parents) { Parent->ReplaceChild(Expression, *MappedExpr); } ExpressionsToRemove.AddUnique(Expression); } else { ValueToLiteral.Add(Hash, LiteralExpr); } } } RemoveExpressions(ExpressionsToRemove); } const FRigVMExprAST* FRigVMParserAST::GetExprForSubject(const FRigVMASTProxy& InProxy) const { if (FRigVMExprAST* const* ExpressionPtr = SubjectToExpression.Find(InProxy)) { return *ExpressionPtr; } return nullptr; } TArray FRigVMParserAST::GetExpressionsForSubject(UObject* InSubject) const { TArray ExpressionsForSubject; for (TPair Pair : SubjectToExpression) { if(Pair.Key.GetCallstack().Last() == InSubject) { ExpressionsForSubject.Add(Pair.Value); } } return ExpressionsForSubject; } void FRigVMParserAST::PrepareCycleChecking(URigVMPin* InPin) { if (InPin == nullptr) { LastCycleCheckExpr = nullptr; CycleCheckFlags.Reset(); return; } FRigVMASTProxy NodeProxy = FRigVMASTProxy::MakeFromUObject(InPin->GetNode()); const FRigVMExprAST* Expression = nullptr; if (FRigVMExprAST* const* ExpressionPtr = SubjectToExpression.Find(NodeProxy)) { Expression = *ExpressionPtr; } else { return; } if (LastCycleCheckExpr != Expression) { LastCycleCheckExpr = Expression; CycleCheckFlags.SetNumZeroed(Expressions.Num()); CycleCheckFlags[LastCycleCheckExpr->GetIndex()] = ETraverseRelationShip_Self; } } bool FRigVMParserAST::CanLink(URigVMPin* InSourcePin, URigVMPin* InTargetPin, FString* OutFailureReason) { if (InSourcePin == nullptr || InTargetPin == nullptr || InSourcePin == InTargetPin) { if (OutFailureReason) { *OutFailureReason = FString(TEXT("Provided objects contain nullptr.")); } return false; } URigVMNode* SourceNode = InSourcePin->GetNode(); URigVMNode* TargetNode = InTargetPin->GetNode(); if (SourceNode == TargetNode) { if (OutFailureReason) { *OutFailureReason = FString(TEXT("Source and Target Nodes are identical.")); } return false; } if(SourceNode->IsA()) { TArray SourcePins; for (URigVMPin* Pin : SourceNode->GetPins()) { SourcePins.Append(Pin->GetLinkedSourcePins(true)); } for (URigVMPin* SourcePin : SourcePins) { if (!CanLink(SourcePin, InTargetPin, OutFailureReason)) { return false; } } return true; } if(TargetNode->IsA()) { TArray TargetPins; for (URigVMPin* Pin : TargetNode->GetPins()) { TargetPins.Append(Pin->GetLinkedTargetPins(true)); } for (URigVMPin* TargetPin : TargetPins) { if (!CanLink(InSourcePin, TargetPin, OutFailureReason)) { return false; } } return true; } const FRigVMASTProxy SourceNodeProxy = FRigVMASTProxy::MakeFromUObject(SourceNode); const FRigVMASTProxy TargetNodeProxy = FRigVMASTProxy::MakeFromUObject(TargetNode); const FRigVMExprAST* SourceExpression = nullptr; if (FRigVMExprAST* const* SourceExpressionPtr = SubjectToExpression.Find(SourceNodeProxy)) { SourceExpression = *SourceExpressionPtr; } else { if (OutFailureReason) { *OutFailureReason = FString(TEXT("Source node is not part of AST.")); } return false; } const FRigVMVarExprAST* SourceVarExpression = nullptr; if (FRigVMExprAST* const* SourceVarExpressionPtr = SubjectToExpression.Find(SourceNodeProxy.GetSibling(InSourcePin->GetRootPin()))) { if ((*SourceVarExpressionPtr)->IsA(FRigVMExprAST::EType::Var)) { SourceVarExpression = (*SourceVarExpressionPtr)->To(); } } const FRigVMExprAST* TargetExpression = nullptr; if (FRigVMExprAST* const* TargetExpressionPtr = SubjectToExpression.Find(TargetNodeProxy)) { TargetExpression = *TargetExpressionPtr; } else { if (OutFailureReason) { *OutFailureReason = FString(TEXT("Target node is not part of AST.")); } return false; } const FRigVMBlockExprAST* SourceBlock = SourceExpression->GetBlock(); const FRigVMBlockExprAST* TargetBlock = TargetExpression->GetBlock(); if (SourceBlock == nullptr || TargetBlock == nullptr) { return false; } // If the source node is an entry of a function, no need to cycle check if (SourceNode->IsA() && SourceNode->GetTypedOuter() != nullptr) { return true; } const FRigVMBlockExprAST* SourceRoot = SourceBlock->GetRootBlock(); const FRigVMBlockExprAST* TargetRoot = TargetBlock->GetRootBlock(); bool bNeedsCycleChecking = (SourceBlock == TargetBlock); // check if source block/root contains the target if (!bNeedsCycleChecking) { TMap SourceCache; // check root first bNeedsCycleChecking = SourceRoot->Contains(TargetBlock, &SourceCache); // check block if needed (avoid doing it twice) if (!bNeedsCycleChecking && SourceBlock != SourceRoot) { bNeedsCycleChecking = SourceBlock->Contains(TargetBlock, &SourceCache); } } // check if target block/root contains the source if (!bNeedsCycleChecking) { TMap TargetCache; // check root first bNeedsCycleChecking = TargetRoot->Contains(SourceBlock, &TargetCache); // check block if needed (avoid doing it twice) if (!bNeedsCycleChecking && TargetBlock != TargetRoot) { bNeedsCycleChecking = TargetBlock->Contains(SourceBlock, &TargetCache); } } if (bNeedsCycleChecking) { if (SourceVarExpression && SourceVarExpression->SupportsSoftLinks()) { return true; } if (LastCycleCheckExpr != SourceExpression && LastCycleCheckExpr != TargetExpression) { PrepareCycleChecking(InSourcePin); } TArray& Flags = CycleCheckFlags; TraverseParents(LastCycleCheckExpr, [&Flags](const FRigVMExprAST* InExpr) -> bool { if (Flags[InExpr->GetIndex()] == ETraverseRelationShip_Self) { return true; } if (Flags[InExpr->GetIndex()] != ETraverseRelationShip_Unknown) { return false; } if (InExpr->IsA(FRigVMExprAST::EType::Var)) { if (InExpr->To()->SupportsSoftLinks()) { return false; } } Flags[InExpr->GetIndex()] = ETraverseRelationShip_Parent; return true; }); TraverseChildren(LastCycleCheckExpr, [&Flags](const FRigVMExprAST* InExpr) -> bool { if (Flags[InExpr->GetIndex()] == ETraverseRelationShip_Self) { return true; } if (Flags[InExpr->GetIndex()] != ETraverseRelationShip_Unknown) { return false; } if (InExpr->IsA(FRigVMExprAST::EType::Var)) { if (InExpr->To()->SupportsSoftLinks()) { return false; } } Flags[InExpr->GetIndex()] = ETraverseRelationShip_Child; return true; }); bool bFoundCycle = false; if (LastCycleCheckExpr == SourceExpression) { bFoundCycle = Flags[TargetExpression->GetIndex()] == ETraverseRelationShip_Child; } else { bFoundCycle = Flags[SourceExpression->GetIndex()] == ETraverseRelationShip_Parent; } if (bFoundCycle) { if (OutFailureReason) { *OutFailureReason = FString(TEXT("Cycles are not allowed.")); } return false; } } else { // if one of the blocks is not part of the current // execution - that's fine. if (SourceRoot->ContainsEntry() != TargetRoot->ContainsEntry()) { return true; } if (OutFailureReason) { *OutFailureReason = FString::Printf(TEXT("You cannot combine nodes from \"%s\" and \"%s\"."), *SourceBlock->GetName().ToString(), *TargetBlock->GetName().ToString()); } return false; } return true; } FString FRigVMParserAST::DumpText() const { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() FString Result; for (FRigVMExprAST* RootExpr : RootExpressions) { if(RootExpr == GetObsoleteBlock(false /* create */)) { continue; } Result += TEXT("\n") + RootExpr->DumpText(); } return Result; } FString FRigVMParserAST::DumpDot() const { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() TArray OutExpressionDefined; OutExpressionDefined.AddZeroed(Expressions.Num()); FVisualGraph VisualGraph(TEXT("AST")); VisualGraph.AddSubGraph(TEXT("AST"), FName(TEXT("AST"))); VisualGraph.AddSubGraph(TEXT("unused"), FName(TEXT("Unused"))); struct Local { static TArray VisitChildren(const FRigVMExprAST* InExpr, int32 InSubGraphIndex, FVisualGraph& OutGraph) { TArray ChildNodeIndices; for (FRigVMExprAST* Child : InExpr->Children) { ChildNodeIndices.Add(VisitExpr(Child, InSubGraphIndex, OutGraph)); } return ChildNodeIndices; } static int32 VisitExpr(const FRigVMExprAST* InExpr, int32 InSubGraphIndex, FVisualGraph& OutGraph) { const FName NodeName = *FString::Printf(TEXT("node_%d"), InExpr->GetIndex()); int32 NodeIndex = OutGraph.FindNode(NodeName); if(NodeIndex != INDEX_NONE) { return NodeIndex; } FString Label = InExpr->GetName().ToString(); TOptional Shape = EVisualGraphShape::Ellipse; int32 SubGraphIndex = InSubGraphIndex; switch (InExpr->GetType()) { case FRigVMExprAST::EType::Literal: { Label = FString::Printf(TEXT("%s(Literal)"), *InExpr->To()->GetPin()->GetName()); break; } case FRigVMExprAST::EType::ExternalVar: { Label = FString::Printf(TEXT("%s(ExternalVar)"), *InExpr->To()->GetPin()->GetBoundVariableName()); break; } case FRigVMExprAST::EType::Var: { if (InExpr->To()->IsGraphVariable()) { URigVMVariableNode* VariableNode = Cast(InExpr->To()->GetPin()->GetNode()); check(VariableNode); Label = FString::Printf(TEXT("Variable %s"), *VariableNode->GetVariableName().ToString()); } else if (InExpr->To()->IsEnumValue()) { URigVMEnumNode* EnumNode = Cast(InExpr->To()->GetPin()->GetNode()); check(EnumNode); Label = FString::Printf(TEXT("Enum %s"), *EnumNode->GetCPPType()); } else { Label = InExpr->To()->GetPin()->GetPinPath(true); } if (InExpr->To()->IsExecuteContext()) { Shape = EVisualGraphShape::House; } break; } case FRigVMExprAST::EType::Block: { if (InExpr->GetParent() == nullptr) { Label = TEXT("Unused"); SubGraphIndex = OutGraph.FindSubGraph(TEXT("unused"));; } else { Label = TEXT("Block"); } break; } case FRigVMExprAST::EType::Assign: { Label = TEXT("="); break; } case FRigVMExprAST::EType::Copy: { Label = TEXT("Copy"); break; } case FRigVMExprAST::EType::CachedValue: { Label = TEXT("Cache"); break; } case FRigVMExprAST::EType::CallExtern: { if (URigVMUnitNode* Node = Cast(InExpr->To()->GetNode())) { Label = Node->GetScriptStruct()->GetName(); } break; } case FRigVMExprAST::EType::InlineFunction: { if (URigVMFunctionReferenceNode* Node = Cast(InExpr->To()->GetNode())) { Label = FString::Printf(TEXT("Inline %s"), *Node->GetReferencedFunctionHeader().Name.ToString()); } break; } case FRigVMExprAST::EType::NoOp: { Label = TEXT("NoOp"); break; } case FRigVMExprAST::EType::Exit: { Label = TEXT("Exit"); break; } case FRigVMExprAST::EType::Entry: { SubGraphIndex = OutGraph.FindSubGraph(InExpr->GetName()); if(SubGraphIndex == INDEX_NONE) { const int32 ASTGraphIndex = OutGraph.FindSubGraph(TEXT("AST")); FString SanitizedNameString = InExpr->GetName().ToString(); SanitizedNameString.RemoveSpacesInline(); SubGraphIndex = OutGraph.AddSubGraph(*SanitizedNameString, InExpr->GetName(), ASTGraphIndex); } break; } default: { break; } } switch (InExpr->GetType()) { case FRigVMExprAST::EType::Entry: case FRigVMExprAST::EType::Exit: case FRigVMExprAST::EType::Block: { Shape = EVisualGraphShape::Diamond; break; } case FRigVMExprAST::EType::Assign: case FRigVMExprAST::EType::Copy: case FRigVMExprAST::EType::CallExtern: case FRigVMExprAST::EType::InlineFunction: case FRigVMExprAST::EType::NoOp: { Shape = EVisualGraphShape::Box; break; } default: { break; } } if (!Label.IsEmpty()) { const TOptional DisplayName = FName(*Label); NodeIndex = OutGraph.AddNode(NodeName, DisplayName, TOptional(), Shape); OutGraph.AddNodeToSubGraph(NodeIndex, SubGraphIndex); } TArray ChildNodeIndices = VisitChildren(InExpr, SubGraphIndex, OutGraph); if(NodeIndex != INDEX_NONE) { for(const int32 ChildNodeIndex : ChildNodeIndices) { if(ChildNodeIndex != INDEX_NONE) { OutGraph.AddEdge(ChildNodeIndex, NodeIndex, EVisualGraphEdgeDirection::SourceToTarget); } } } return NodeIndex; } }; for (FRigVMExprAST* Expr : RootExpressions) { if (Expr == GetObsoleteBlock(false)) { continue; } Local::VisitExpr(Expr, INDEX_NONE, VisualGraph); } return VisualGraph.DumpDot(); } FRigVMBlockExprAST* FRigVMParserAST::GetObsoleteBlock(bool bCreateIfMissing) { if (ObsoleteBlock == nullptr && bCreateIfMissing) { ObsoleteBlock = MakeExpr(FRigVMExprAST::EType::Block, FRigVMASTProxy()); ObsoleteBlock->bIsObsolete = true; RootExpressions.Add(ObsoleteBlock); } return ObsoleteBlock; } const FRigVMBlockExprAST* FRigVMParserAST::GetObsoleteBlock(bool bCreateIfMissing) const { if (ObsoleteBlock == nullptr && bCreateIfMissing) { FRigVMParserAST* MutableThis = (FRigVMParserAST*)this; MutableThis->ObsoleteBlock = MutableThis->MakeExpr(FRigVMExprAST::EType::Block, FRigVMASTProxy()); MutableThis->ObsoleteBlock->bIsObsolete = true; MutableThis->RootExpressions.Add(MutableThis->ObsoleteBlock); } return ObsoleteBlock; } void FRigVMParserAST::RemoveExpressions(TArray InExprs) { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() if(InExprs.IsEmpty()) { return; } RefreshExprIndices(); TArray ExpressionsToRemove; ExpressionsToRemove.Append(InExprs); TArray NumRemainingParents; NumRemainingParents.AddUninitialized(Expressions.Num()); for(int32 ExpressionIndex = 0; ExpressionIndex < Expressions.Num(); ExpressionIndex++) { NumRemainingParents[ExpressionIndex] = Expressions[ExpressionIndex]->Parents.Num(); } TArray bRemoveExpression; bRemoveExpression.AddZeroed(Expressions.Num()); for(int32 ExpressionIndex = 0; ExpressionIndex < ExpressionsToRemove.Num(); ExpressionIndex++) { FRigVMExprAST* Expr = ExpressionsToRemove[ExpressionIndex]; bRemoveExpression[Expr->GetIndex()] = true; for (FRigVMExprAST* Child : Expr->Children) { if(--NumRemainingParents[Child->GetIndex()] == 0) { ExpressionsToRemove.Add(Child); } } } TArray RemainingExpressions; RemainingExpressions.Reserve(Expressions.Num() - ExpressionsToRemove.Num()); for(int32 ExpressionIndex = 0; ExpressionIndex < Expressions.Num(); ExpressionIndex++) { if(!bRemoveExpression[ExpressionIndex]) { FRigVMExprAST* Expr = Expressions[ExpressionIndex]; RemainingExpressions.Add(Expr); for(int32 ChildIndex = Expr->Children.Num() - 1; ChildIndex >= 0; ChildIndex--) { FRigVMExprAST* ChildExpr = Expr->Children[ChildIndex]; if(bRemoveExpression[ChildExpr->GetIndex()]) { Expr->Children.RemoveAt(ChildIndex); } } for(int32 ParentIndex = Expr->Parents.Num() - 1; ParentIndex >= 0; ParentIndex--) { FRigVMExprAST* ParentExpr = Expr->Parents[ParentIndex]; if(bRemoveExpression[ParentExpr->GetIndex()]) { Expr->Parents.RemoveAt(ParentIndex); } } } } Expressions = RemainingExpressions; TArray KeysToRemove; for (TPair Pair : SubjectToExpression) { if (bRemoveExpression[Pair.Value->GetIndex()]) { KeysToRemove.Add(Pair.Key); } } for (const FRigVMASTProxy& KeyToRemove : KeysToRemove) { SubjectToExpression.Remove(KeyToRemove); } for(int32 ExpressionIndex = ExpressionsToRemove.Num() - 1; ExpressionIndex >= 0; ExpressionIndex--) { ExpressionsToRemove[ExpressionIndex]->Index = INDEX_NONE; DeletedExpressions.Add(ExpressionsToRemove[ExpressionIndex]); } RefreshExprIndices(); } void FRigVMParserAST::TraverseParents(const FRigVMExprAST* InExpr, TFunctionRef InContinuePredicate) { if (!InContinuePredicate(InExpr)) { return; } for (const FRigVMExprAST* ParentExpr : InExpr->Parents) { TraverseParents(ParentExpr, InContinuePredicate); } } void FRigVMParserAST::TraverseChildren(const FRigVMExprAST* InExpr, TFunctionRef InContinuePredicate) { if (!InContinuePredicate(InExpr)) { return; } for (const FRigVMExprAST* ChildExpr : InExpr->Children) { TraverseChildren(ChildExpr, InContinuePredicate); } } TArray FRigVMParserAST::GetSourceLinkIndices(const FRigVMASTProxy& InPinProxy, bool bRecursive) const { return GetLinkIndices(InPinProxy, true, bRecursive); } TArray FRigVMParserAST::GetTargetLinkIndices(const FRigVMASTProxy& InPinProxy, bool bRecursive) const { return GetLinkIndices(InPinProxy, false, bRecursive); } TArray FRigVMParserAST::GetLinkIndices(const FRigVMASTProxy& InPinProxy, bool bGetSource, bool bRecursive) const { const static TArray EmptyIntArray; TArray LinkIndices; if(const TArray* LinkIndicesPtr = (bGetSource ? SourceLinkIndices : TargetLinkIndices).Find(InPinProxy)) { LinkIndices = *LinkIndicesPtr; } if (bRecursive) { URigVMPin* Pin = InPinProxy.GetSubjectChecked(); for (URigVMPin* SubPin : Pin->GetSubPins()) { FRigVMASTProxy SubPinProxy = InPinProxy.GetSibling(SubPin); LinkIndices.Append(GetLinkIndices(SubPinProxy, bGetSource, true)); } } return LinkIndices; } const FRigVMASTLinkDescription& FRigVMParserAST::GetLink(int32 InLinkIndex) const { return Links[InLinkIndex]; } void FRigVMParserAST::Inline(TArray InGraphs) { TArray LocalNodeProxies; for(URigVMGraph* Graph : InGraphs) { for (URigVMNode* LocalNode : Graph->GetNodes()) { LocalNodeProxies.Add(FRigVMASTProxy::MakeFromUObject(LocalNode)); } } Inline(InGraphs, LocalNodeProxies); } void FRigVMParserAST::Inline(TArray InGraphs, const TArray& InNodeProxies) { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() struct LocalPinTraversalInfo { URigVMPin::FPinOverrideMap* PinOverrides; TMap* SourcePins; TMap>* TargetLinkIndices; TMap>* SourceLinkIndices; TArray* Links; TArray LibraryNodeCallstack; FRigVMParserASTSettings* Settings; URigVMLibraryNode* LibraryNodeBeingCompiled; static bool ShouldRecursePin(const FRigVMASTProxy& InPinProxy, LocalPinTraversalInfo& OutTraversalInfo) { URigVMPin* Pin = InPinProxy.GetSubjectChecked(); URigVMNode* Node = Pin->GetNode(); if (URigVMVariableNode* VarNode = Cast(Node)) { return VarNode->IsInputArgument(); } // If its an interface node of the library we are compiling, don't recurse if (OutTraversalInfo.LibraryNodeBeingCompiled != nullptr) { if (Node->IsA() || Node->IsA()) { if (Node->GetTypedOuter() == OutTraversalInfo.LibraryNodeBeingCompiled) { return false; } } } // If its a function reference, don't recurse if (Node->IsA()) { return false; } return Node->IsA() || Node->IsA() || Node->IsA() || Node->IsA(); } static bool IsValidPinForAST(const FRigVMASTProxy& InPinProxy, LocalPinTraversalInfo& OutTraversalInfo) { return !ShouldRecursePin(InPinProxy, OutTraversalInfo); } static bool IsValidLinkForAST(const FRigVMASTProxy& InSourcePinProxy, const FRigVMASTProxy& InTargetPinProxy, LocalPinTraversalInfo& OutTraversalInfo) { return IsValidPinForAST(InSourcePinProxy, OutTraversalInfo) && IsValidPinForAST(InTargetPinProxy, OutTraversalInfo); } static FRigVMASTProxy FindSourcePin(const FRigVMASTProxy& InPinProxy, LocalPinTraversalInfo& OutTraversalInfo) { return FindSourcePin(InPinProxy, InPinProxy, OutTraversalInfo); } static FRigVMASTProxy FindSourcePin(const FRigVMASTProxy& InPinProxy, const FRigVMASTProxy& InPinProxyForMap, LocalPinTraversalInfo& OutTraversalInfo) { URigVMPin* Pin = InPinProxy.GetSubjectChecked(); const bool bStoreSourcePinOnMap = InPinProxy == InPinProxyForMap; // if this pin is a root on a library if (Pin->GetParentPin() == nullptr) { if (Pin->GetDirection() == ERigVMPinDirection::Output || Pin->GetDirection() == ERigVMPinDirection::IO) { URigVMNode* Node = Pin->GetNode(); if (URigVMCollapseNode* CollapseNode = Cast(Node)) { FRigVMASTProxy CollapseNodeProxy = InPinProxy.GetSibling(CollapseNode); if (!OutTraversalInfo.LibraryNodeCallstack.Contains(CollapseNodeProxy)) { if (URigVMFunctionReturnNode* ReturnNode = CollapseNode->GetReturnNode()) { if (URigVMPin* ReturnPin = ReturnNode->FindPin(Pin->GetName())) { OutTraversalInfo.LibraryNodeCallstack.Push(CollapseNodeProxy); FRigVMASTProxy ReturnPinProxy = CollapseNodeProxy.GetChild(ReturnPin); FRigVMASTProxy SourcePinProxy = FindSourcePin(ReturnPinProxy, OutTraversalInfo); SourcePinProxy = SourcePinProxy.IsValid() ? SourcePinProxy : ReturnPinProxy; if(bStoreSourcePinOnMap) { OutTraversalInfo.SourcePins->FindOrAdd(InPinProxyForMap) = SourcePinProxy; } OutTraversalInfo.LibraryNodeCallstack.Pop(); return SourcePinProxy; } } } } else if(URigVMVariableNode* VariableNode = Cast(Node)) { if (VariableNode->IsInputArgument()) { if (URigVMFunctionEntryNode* EntryNode = VariableNode->GetGraph()->GetEntryNode()) { if (URigVMPin* EntryPin = EntryNode->FindPin(VariableNode->GetVariableName().ToString())) { FRigVMASTProxy EntryPinProxy = InPinProxy.GetSibling(EntryPin); FRigVMASTProxy SourcePinProxy = FindSourcePin(EntryPinProxy, OutTraversalInfo); SourcePinProxy = SourcePinProxy.IsValid() ? SourcePinProxy : EntryPinProxy; if(bStoreSourcePinOnMap) { OutTraversalInfo.SourcePins->FindOrAdd(InPinProxyForMap) = SourcePinProxy; } return SourcePinProxy; } } } } else if (URigVMFunctionEntryNode* EntryNode = Cast(Node)) { if (EntryNode->GetGraph()->GetOuter() == OutTraversalInfo.LibraryNodeBeingCompiled) { return FRigVMASTProxy(); } for (int32 LibraryNodeIndex = OutTraversalInfo.LibraryNodeCallstack.Num() - 1; LibraryNodeIndex >= 0; LibraryNodeIndex--) { FRigVMASTProxy LibraryNodeProxy = OutTraversalInfo.LibraryNodeCallstack[LibraryNodeIndex]; URigVMLibraryNode* LastLibraryNode = LibraryNodeProxy.GetSubject(); if (LastLibraryNode == nullptr) { continue; } if(LastLibraryNode->GetEntryNode() == EntryNode) { if (URigVMPin* LibraryPin = LastLibraryNode->FindPin(Pin->GetName())) { FRigVMASTProxy LibraryPinProxy = LibraryNodeProxy.GetSibling(LibraryPin); FRigVMASTProxy SourcePinProxy = FindSourcePin(LibraryPinProxy, OutTraversalInfo); SourcePinProxy = SourcePinProxy.IsValid() ? SourcePinProxy : LibraryPinProxy; if(bStoreSourcePinOnMap) { OutTraversalInfo.SourcePins->FindOrAdd(InPinProxyForMap) = SourcePinProxy; } return SourcePinProxy; } } } } } } if (Pin->GetDirection() != ERigVMPinDirection::Input && Pin->GetDirection() != ERigVMPinDirection::IO && Pin->GetDirection() != ERigVMPinDirection::Output) { return FRigVMASTProxy(); } bool bIOPinOnLeftOfLibraryNode = false; if (Pin->GetDirection() == ERigVMPinDirection::IO) { if (URigVMLibraryNode* LibraryNode = Cast(Pin->GetNode())) { bIOPinOnLeftOfLibraryNode = OutTraversalInfo.LibraryNodeCallstack.Contains(InPinProxy.GetSibling(LibraryNode)); } } if (!bIOPinOnLeftOfLibraryNode && bStoreSourcePinOnMap) { // note: this map isn't going to work for functions which are referenced. // (since the pin objects are shared between multiple invocation nodes) if (const FRigVMASTProxy* SourcePinProxy = OutTraversalInfo.SourcePins->Find(InPinProxyForMap)) { return *SourcePinProxy; } } TArray SegmentPath; FRigVMASTProxy SourcePinProxy; URigVMPin* ChildPin = Pin; while (ChildPin != nullptr) { if (ChildPin->GetDirection() == ERigVMPinDirection::Output && ChildPin->GetParentPin() == nullptr) { if (URigVMFunctionEntryNode* EntryNode = Cast(ChildPin->GetNode())) { if (EntryNode->GetGraph()->GetOuter() == OutTraversalInfo.LibraryNodeBeingCompiled) { return FRigVMASTProxy(); } // rather than relying on the other we are going to query what's in the call stack. // for collapse nodes that's not a different, but for function ref nodes the outer // node is the definition and not the reference node - which sits in the callstack. if (URigVMLibraryNode* OuterNode = (InPinProxy.GetParent().GetSubject())) { if (URigVMPin* OuterPin = OuterNode->FindPin(ChildPin->GetName())) { FRigVMASTProxy OuterPinProxy = InPinProxy.GetParent().GetSibling(OuterPin); SourcePinProxy = FindSourcePin(OuterPinProxy, OutTraversalInfo); SourcePinProxy = SourcePinProxy.IsValid() ? SourcePinProxy : OuterPinProxy; break; } } } else if(URigVMLibraryNode* LibraryNode = Cast(ChildPin->GetNode())) { if(ChildPin != InPinProxy.GetSubject()) { const FRigVMASTProxy ChildPinProxy = InPinProxy.GetSibling(ChildPin); if (ShouldRecursePin(ChildPinProxy, OutTraversalInfo)) { FRigVMASTProxy SourceSourcePinProxy = FindSourcePin(ChildPinProxy, OutTraversalInfo); if(SourceSourcePinProxy.IsValid()) { SourcePinProxy = SourceSourcePinProxy; } } } } } TArray SourceLinks = ChildPin->GetSourceLinks(false /* recursive */); URigVMPin* SourcePin = nullptr; if (SourceLinks.Num() > 0) { SourcePin = SourceLinks[0]->GetSourcePin(); } else if(ChildPin->IsBoundToInputArgument()) { if (URigVMGraph* Graph = ChildPin->GetGraph()) { if (URigVMFunctionEntryNode* EntryNode = Graph->GetEntryNode()) { SourcePin = EntryNode->FindPin(ChildPin->GetBoundVariableName()); } } } else if(URigVMVariableNode* VariableNode = Cast(ChildPin->GetNode())) { if (VariableNode->IsInputArgument()) { if (URigVMGraph* Graph = ChildPin->GetGraph()) { if (URigVMFunctionEntryNode* EntryNode = Graph->GetEntryNode()) { SourcePin = EntryNode->FindPin(VariableNode->GetVariableName().ToString()); if (ChildPin->GetParentPin()) { SourcePin = SourcePin->FindSubPin(ChildPin->GetSegmentPath()); } } } } } if(SourcePin) { SourcePinProxy = InPinProxy.GetSibling(SourcePin); if (ShouldRecursePin(SourcePinProxy, OutTraversalInfo)) { FRigVMASTProxy SourceSourcePinProxy = FindSourcePin(SourcePinProxy, OutTraversalInfo); if(SourceSourcePinProxy.IsValid()) { SourcePinProxy = SourceSourcePinProxy; } } break; } URigVMPin* ParentPin = ChildPin->GetParentPin(); if (ParentPin) { FRigVMASTProxy ParentPinProxy = InPinProxy.GetSibling(ParentPin); // if we found a parent pin which has a source that is not a reroute if (FRigVMASTProxy* ParentSourcePinProxyPtr = OutTraversalInfo.SourcePins->Find(ParentPinProxy)) { FRigVMASTProxy& ParentSourcePinProxy = *ParentSourcePinProxyPtr; if (ParentSourcePinProxy.IsValid()) { if (!ShouldRecursePin(ParentSourcePinProxy, OutTraversalInfo)) { // only discard results here if we haven't crossed a collapse node boundary if(ParentSourcePinProxy.GetSubjectChecked()->GetGraph() == ChildPin->GetGraph()) { SourcePinProxy = FRigVMASTProxy(); break; } } } } SegmentPath.Push(ChildPin->GetName()); } ChildPin = ParentPin; } if (SourcePinProxy.IsValid()) { while (!SegmentPath.IsEmpty()) { FString Segment = SegmentPath.Pop(); URigVMPin* SourcePin = SourcePinProxy.GetSubjectChecked(); if (URigVMPin* SourceSubPin = SourcePin->FindSubPin(Segment)) { SourcePinProxy = SourcePinProxy.GetSibling(SourceSubPin); if (ShouldRecursePin(SourcePinProxy, OutTraversalInfo)) { FRigVMASTProxy SourceSourceSubPinProxy = FindSourcePin(SourcePinProxy, OutTraversalInfo); if (SourceSourceSubPinProxy.IsValid()) { SourcePinProxy = SourceSourceSubPinProxy; } } } else { SourcePinProxy = FRigVMASTProxy(); break; } } } if (!bIOPinOnLeftOfLibraryNode && bStoreSourcePinOnMap) { OutTraversalInfo.SourcePins->FindOrAdd(InPinProxyForMap) = SourcePinProxy; } return SourcePinProxy; } static void VisitPin(const FRigVMASTProxy& InPinProxy, LocalPinTraversalInfo& OutTraversalInfo) { return VisitPin(InPinProxy, InPinProxy, OutTraversalInfo, FString()); } static void VisitPin(const FRigVMASTProxy& InPinProxy, const FRigVMASTProxy& InPinProxyForMap, LocalPinTraversalInfo& OutTraversalInfo, const FString& InSegmentPath) { const FRigVMASTProxy SourcePinProxy = FindSourcePin(InPinProxy, InPinProxyForMap, OutTraversalInfo); if (SourcePinProxy.IsValid()) { // The source pin is the final determined source pin, since // FindSourcePin is recursive. // If the source pin is on a reroute node, this means that // we only care about the default value - since it is a // "hanging" reroute without any live input. // same goes for library nodes or return nodes - we'll // just use the default pin value in that case. URigVMPin* SourcePin = SourcePinProxy.GetSubjectChecked(); URigVMNode* SourceNode = SourcePin->GetNode(); if (SourceNode->IsA() || SourceNode->IsA() || SourceNode->IsA()) { // for arrays - if there are sub-pins on the determined source, we need to walk those as well if(SourcePin->IsArray()) { TArray SourceSubPins = SourcePin->GetSubPins(); for (int32 SourceSubPinIndex = 0; SourceSubPinIndex < SourceSubPins.Num(); SourceSubPinIndex++) { URigVMPin* SourceSubPin = SourceSubPins[SourceSubPinIndex]; const FRigVMASTProxy SubPinProxy = SourcePinProxy.GetSibling(SourceSubPin); const FString SegmentPath = SourceSubPin->GetSubPinPath(SourcePin, false); VisitPin(SubPinProxy, InPinProxy, OutTraversalInfo, SegmentPath); SourceSubPins.Append(SourceSubPin->GetSubPins()); } } // when asking for the default value of the array - we may need to get the previously stored override const URigVMPin::FPinOverride SourceOverride(SourcePinProxy, *OutTraversalInfo.PinOverrides); // we query the default value now since the potential array elements have been visited and their // potential default value override has been stored to the map. OutTraversalInfo.PinOverrides->FindOrAdd(InPinProxy) = URigVMPin::FPinOverrideValue(SourcePin, SourceOverride); } else { if (IsValidLinkForAST(SourcePinProxy, InPinProxyForMap, OutTraversalInfo)) { FRigVMASTLinkDescription Link(SourcePinProxy, InPinProxyForMap, InSegmentPath); Link.LinkIndex = OutTraversalInfo.Links->Num(); OutTraversalInfo.Links->Add(Link); OutTraversalInfo.SourceLinkIndices->FindOrAdd(InPinProxyForMap).Add(Link.LinkIndex); OutTraversalInfo.TargetLinkIndices->FindOrAdd(SourcePinProxy).Add(Link.LinkIndex); } } } // If the pin is an array, and it has a source pin, the subpins can be safely ignored URigVMPin* Pin = InPinProxy.GetSubjectChecked(); if (!Pin->IsArray() || !SourcePinProxy.IsValid()) { for (URigVMPin* SubPin : Pin->GetSubPins()) { FRigVMASTProxy SubPinProxy = InPinProxy.GetSibling(SubPin); VisitPin(SubPinProxy, OutTraversalInfo); } } } static void VisitNode(const FRigVMASTProxy& InNodeProxy, LocalPinTraversalInfo& OutTraversalInfo) { if (InNodeProxy.IsA()) { return; } const bool bIsCompilingFunction = OutTraversalInfo.LibraryNodeBeingCompiled != nullptr; if (bIsCompilingFunction) { if (InNodeProxy.IsA() || InNodeProxy.IsA()) { URigVMNode* Node = InNodeProxy.GetSubjectChecked(); if (Node->GetTypedOuter() != OutTraversalInfo.LibraryNodeBeingCompiled) { return; } } } else { if (InNodeProxy.IsA() || InNodeProxy.IsA()) { return; } } if (URigVMCollapseNode* LibraryNode = InNodeProxy.GetSubject()) { if (LibraryNode->GetContainedGraph() == nullptr) { OutTraversalInfo.Settings->Reportf( EMessageSeverity::Error, LibraryNode, TEXT("Library Node '%s' doesn't contain a subgraph."), *LibraryNode->GetName()); return; } OutTraversalInfo.LibraryNodeCallstack.Push(InNodeProxy); TArray ContainedNodes = LibraryNode->GetContainedNodes(); for (URigVMNode* ContainedNode : ContainedNodes) { // create a proxy which uses the previous node as a callstack FRigVMASTProxy ContainedNodeProxy = InNodeProxy.GetChild(ContainedNode); VisitNode(ContainedNodeProxy, OutTraversalInfo); } OutTraversalInfo.LibraryNodeCallstack.Pop(); } else if (URigVMFunctionReferenceNode* FuncRefNode = InNodeProxy.GetSubject()) { if (FuncRefNode->GetReferencedFunctionData() == nullptr) { FString FunctionPath = FuncRefNode->GetReferencedFunctionHeader().GetHash(); OutTraversalInfo.Settings->Reportf( EMessageSeverity::Error, FuncRefNode, TEXT("Function Reference '%s' references a missing function (%s)."), *FuncRefNode->GetName(), *FunctionPath); } for (URigVMPin* Pin : FuncRefNode->GetPins()) { FRigVMASTProxy PinProxy = InNodeProxy.GetSibling(Pin); LocalPinTraversalInfo::VisitPin(PinProxy, OutTraversalInfo); } } else { URigVMNode* Node = InNodeProxy.GetSubjectChecked(); for (URigVMPin* Pin : Node->GetPins()) { FRigVMASTProxy PinProxy = InNodeProxy.GetSibling(Pin); LocalPinTraversalInfo::VisitPin(PinProxy, OutTraversalInfo); } } } }; NodeProxies.Reset(); SourceLinkIndices.Reset(); TargetLinkIndices.Reset(); // a) find all of the relevant nodes, // inline and traverse into library nodes NodeProxies = InNodeProxies; // c) flatten links from an entry node / to a return node // also traverse links along reroutes and flatten them LocalPinTraversalInfo TraversalInfo; TraversalInfo.PinOverrides = &PinOverrides; TraversalInfo.SourcePins = &SharedOperandPins; TraversalInfo.TargetLinkIndices = &TargetLinkIndices; TraversalInfo.SourceLinkIndices = &SourceLinkIndices; TraversalInfo.Links = &Links; TraversalInfo.Settings = &Settings; TraversalInfo.LibraryNodeBeingCompiled = this->LibraryNodeBeingCompiled; for (const FRigVMASTProxy& NodeProxy : NodeProxies) { LocalPinTraversalInfo::VisitNode(NodeProxy, TraversalInfo); } // once we are done with the inlining we may need to clean up pin value overrides for pins // that also have overrides on sub pins TArray PinOverridesToRemove; for(const TPair& Override : PinOverrides) { if(URigVMPin* Pin = Override.Key.GetSubject()) { for(URigVMPin* SubPin : Pin->GetSubPins()) { const FRigVMASTProxy SubPinProxy = Override.Key.GetSibling(SubPin); if(PinOverrides.Contains(SubPinProxy)) { PinOverridesToRemove.Add(Override.Key); } } } } for(const FRigVMASTProxy& ProxyToRemove : PinOverridesToRemove) { PinOverrides.Remove(ProxyToRemove); } } bool FRigVMParserAST::ShouldLinkBeSkipped(const FRigVMASTLinkDescription& InLink) const { const URigVMPin* SourcePin = InLink.SourceProxy.GetSubjectChecked(); const URigVMPin* TargetPin = InLink.TargetProxy.GetSubjectChecked(); for (URigVMLink* LinkToSkip : LinksToSkip) { if (LinkToSkip->GetSourcePin() == SourcePin && LinkToSkip->GetTargetPin() == TargetPin) { return true; } } return false; } FString FRigVMParserAST::GetLinkAsString(const FRigVMASTLinkDescription& InLink) { const URigVMPin* SourcePin = InLink.SourceProxy.GetSubjectChecked(); const URigVMPin* TargetPin = InLink.TargetProxy.GetSubjectChecked(); static const FString EmptyString; static const FString PeriodString = TEXT("."); return URigVMLink::GetPinPathRepresentation(SourcePin->GetPinPath(), FString::Printf(TEXT("%s%s%s"), *TargetPin->GetPinPath(), *(InLink.SegmentPath.IsEmpty() ? EmptyString : PeriodString), *InLink.SegmentPath)); }