// 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 "Algo/Sort.h" #include "RigVMFunctions/RigVMDispatch_Constant.h" #include "RigVMFunctions/RigVMDispatch_MakeStruct.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMAST) FRigVMExprAST::FRigVMExprAST(EType InType, const FRigVMASTProxy& InProxy) : Name(NAME_None) , Type(InType) , Index(INDEX_NONE) , ParserPtr(nullptr) { } 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 { if(const FRigVMParserAST* Parser = GetParser()) { if(FirstParentOfTypeCacheVersion.Get(INDEX_NONE) == Parser->CacheVersion) { if(const FRigVMExprAST* const* ExistingFirstParent = CachedFirstParentOfType.Find(InExprType)) { return *ExistingFirstParent; } } else { // if the cache version no longer matches, // clear the map to make sure we re-retrieve the first parent accordingly. CachedFirstParentOfType.Reset(); } } const FRigVMExprAST* FirstParent = nullptr; for(const FRigVMExprAST* Parent : Parents) { if (Parent->IsA(InExprType)) { FirstParent = Parent; break; } } if(FirstParent == nullptr) { for (const FRigVMExprAST* Parent : Parents) { if (const FRigVMExprAST* GrandParent = Parent->GetFirstParentOfType(InExprType)) { FirstParent = GrandParent; break; } } } if(const FRigVMParserAST* Parser = GetParser()) { FirstParentOfTypeCacheVersion = Parser->CacheVersion; CachedFirstParentOfType.FindOrAdd(InExprType, nullptr) = FirstParent; } return FirstParent; } bool FRigVMExprAST::IsParentedTo(const FRigVMExprAST* InParentExpr) const { check(InParentExpr); for(const FRigVMExprAST* Parent : Parents) { if(Parent == InParentExpr) { return true; } if(Parent->IsParentedTo(InParentExpr)) { return true; } } return false; } bool FRigVMExprAST::IsParentOf(const FRigVMExprAST* InChildExpr) const { check(InChildExpr); return InChildExpr->IsParentedTo(this); } const FRigVMExprAST* FRigVMExprAST::GetFirstChildOfType(EType InExprType) const { if(const FRigVMParserAST* Parser = GetParser()) { if(FirstChildOfTypeCacheVersion.Get(INDEX_NONE) == Parser->CacheVersion) { if(const FRigVMExprAST* const* ExistingFirstChild = CachedFirstChildOfType.Find(InExprType)) { return *ExistingFirstChild; } } else { // if the cache version no longer matches, // clear the map to make sure we re-retrieve the first child accordingly. CachedFirstChildOfType.Reset(); } } const FRigVMExprAST* FirstChild = nullptr; for (const FRigVMExprAST* Child : Children) { if (Child->IsA(InExprType)) { FirstChild = Child; break; } } if(FirstChild == nullptr) { for (const FRigVMExprAST* Child : Children) { if (const FRigVMExprAST* GrandChild = Child->GetFirstChildOfType(InExprType)) { FirstChild = GrandChild; break; } } } if(const FRigVMParserAST* Parser = GetParser()) { FirstChildOfTypeCacheVersion = Parser->CacheVersion; CachedFirstChildOfType.FindOrAdd(InExprType, nullptr) = FirstChild; } return FirstChild; } 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; } FRigVMExprAST::FRigVMBlockArray FRigVMExprAST::GetBlocks(bool bSortByDepth) const { FRigVMBlockArray Blocks; GetBlocksImpl(Blocks); if(bSortByDepth) { auto SortByDepth = [](const FRigVMExprAST* A, const FRigVMExprAST* B) -> bool { if(A->GetMaximumDepth() > B->GetMaximumDepth()) { return true; } return A->Index < B->Index; }; Algo::Sort(Blocks, SortByDepth); } // remove the obsolete block to avoid combining expressions on // a branch which are supposed to be obsolete. if(const FRigVMParserAST* Parser = GetParser()) { Blocks.RemoveAll([](const FRigVMBlockExprAST* Block) -> bool { return Block->IsObsolete(); }); } return Blocks; } TOptional FRigVMExprAST::GetBlockCombinationHash() const { if(IsA(EType::Literal)) { return TOptional(); } // if this expression is a node which is mutable, we are not part of a lazy block if(IsNode()) { const FRigVMNodeExprAST* NodeExpr = To(); check(NodeExpr); if(const URigVMNode* Node = NodeExpr->GetNode()) { if(Node->IsMutable()) { return TOptional(); } } } // if this expression is a var expression on a mutable node - we are not part of a lazy block if(IsVar()) { const FRigVMVarExprAST* VarExpr = To(); check(VarExpr); if(const URigVMPin* Pin = VarExpr->GetPin()) { if(const URigVMNode* Node = Pin->GetNode()) { if(Node->IsMutable()) { return TOptional(); } // variable node output pins may be shared across many blocks, // since they represent data which is not considered scoped. if(Node->IsA()) { if(Pin->GetName() == URigVMVariableNode::ValueName) { if(Pin->GetDirection() == ERigVMPinDirection::Output) { return TOptional(); } } } } } } if(BlockCombinationHash.IsSet()) { return BlockCombinationHash.GetValue(); } // only consider cached / computed values for lazy blocks if (IsA(FRigVMExprAST::CachedValue)) { // find the first node expr child FRigVMExprAST* const* NodeChildPtr = Children.FindByPredicate([](const FRigVMExprAST* Child) { return Child->IsNode(); }); if(NodeChildPtr) { const FRigVMExprAST* NodeExpression = *NodeChildPtr; // collect all of the blocks this expression is in const FRigVMExprAST::FRigVMBlockArray Blocks = NodeExpression->GetBlocks(true); if(Blocks.Num() > 1) { // compute a hash for the blocks uint32 ComputedHash = 0; for(const FRigVMBlockExprAST* Block : Blocks) { ComputedHash = HashCombine(ComputedHash, GetTypeHash(Block->Index)); } BlockCombinationHash = TOptional(ComputedHash); if(const FRigVMParserAST* Parser = GetParser()) { // compute a unique name for the block combination if(!Parser->BlockCombinationHashToName.Contains(ComputedHash)) { FString BlockCombinationName; for(const FRigVMBlockExprAST* Block : Blocks) { static constexpr TCHAR BlockNameFormat[] = TEXT("%s%d"); const FString BlockNameAndIndex = FString::Printf(BlockNameFormat, *Block->GetName().ToString(), Block->Index); if(BlockCombinationName.IsEmpty()) { BlockCombinationName = BlockNameAndIndex; } else { static constexpr TCHAR JoinFormat[] = TEXT("%s_%s"); BlockCombinationName = FString::Printf(JoinFormat, *BlockCombinationName, *BlockNameAndIndex); } } Parser->BlockCombinationHashToName.Add(ComputedHash, BlockCombinationName); } } return BlockCombinationHash.GetValue(); } } } // if we still don't have a hash - rely on the parents if(!BlockCombinationHash.IsSet()) { for(int32 ParentIndex = 0; ParentIndex < NumParents(); ParentIndex++) { const TOptional ParentHash = ParentAt(ParentIndex)->GetBlockCombinationHash(); if(ParentHash.IsSet()) { BlockCombinationHash = ParentHash; return ParentHash; } } } BlockCombinationHash = TOptional(); return BlockCombinationHash.GetValue(); } const FString& FRigVMExprAST::GetBlockCombinationName() const { const TOptional CombinationHash = GetBlockCombinationHash(); if(CombinationHash.IsSet()) { if(const FRigVMParserAST* Parser = GetParser()) { if(const FString* CombinationName = Parser->BlockCombinationHashToName.Find(CombinationHash.GetValue())) { return *CombinationName; } } } static const FString EmptyString; return EmptyString; } void FRigVMExprAST::GetBlocksImpl(FRigVMBlockArray& InOutBlocks) const { if (IsA(EType::Block)) { InOutBlocks.AddUnique(this->To()); return; } const FRigVMParserAST* Parser = GetParser(); check(Parser); if(BlocksCacheVersion.Get(INDEX_NONE) != Parser->CacheVersion) { CachedBlocks.Reset(); for(int32 ParentIndex = 0; ParentIndex < NumParents(); ParentIndex++) { const FRigVMExprAST* ParentExpression = ParentAt(ParentIndex); ParentExpression->GetBlocksImpl(CachedBlocks); } BlocksCacheVersion = Parser->CacheVersion; } if(InOutBlocks.IsEmpty()) { InOutBlocks = CachedBlocks; } else { for(const FRigVMBlockExprAST* Block : CachedBlocks) { InOutBlocks.AddUnique(Block); } } } 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); ensure(!InParent->IsA(FRigVMExprAST::EType::Literal)); if (Parents.Contains(InParent)) { return; } InParent->Children.Add(this); Parents.Add(InParent); InvalidateCaches(); } 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); } InvalidateCaches(); } void FRigVMExprAST::RemoveChild(FRigVMExprAST* InChild) { ensure(IsValid()); ensure(InChild->IsValid()); InChild->RemoveParent(this); } void FRigVMExprAST::ReplaceParent(FRigVMExprAST* InCurrentParent, FRigVMExprAST* InNewParent, TArray* OutParentIndices) { 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); InvalidateCaches(); if (OutParentIndices) { OutParentIndices->Add(ParentIndex); } } } } void FRigVMExprAST::ReplaceChild(FRigVMExprAST* InCurrentChild, FRigVMExprAST* InNewChild, TArray* OutChildIndices) { 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); TArray InvalidateCachesProcessed; InvalidateCachesProcessed.AddZeroed(GetParser()->Expressions.Num()); InCurrentChild->InvalidateCachesImpl(InvalidateCachesProcessed); InNewChild->InvalidateCachesImpl(InvalidateCachesProcessed); InvalidateCachesImpl(InvalidateCachesProcessed); if (OutChildIndices) { OutChildIndices->Add(ChildIndex); } } } } 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; } int32 FRigVMExprAST::GetMaximumDepth() const { if(MaximumDepth.IsSet()) { return MaximumDepth.GetValue(); } int32 Depth = 0; for(int32 ParentIndex = 0; ParentIndex < NumParents(); ParentIndex++) { const FRigVMExprAST* ParentExpression = ParentAt(ParentIndex); Depth = FMath::Max(Depth, ParentExpression->GetMaximumDepth() + 1); } MaximumDepth = Depth; return Depth; } 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; } void FRigVMExprAST::InvalidateCaches() { TArray Processed; Processed.AddZeroed(GetParser()->Expressions.Num()); InvalidateCachesImpl(Processed); } void FRigVMExprAST::InvalidateCachesImpl(TArray& OutProcessed) { if(OutProcessed[Index]) { return; } BlockCombinationHash.Reset(); BlocksCacheVersion.Reset(); FirstChildOfTypeCacheVersion.Reset(); FirstParentOfTypeCacheVersion.Reset(); MaximumDepth.Reset(); OutProcessed[Index] = true; if(const FRigVMParserAST* Parser = GetParser()) { Parser->IncrementCacheVersion(); } for(FRigVMExprAST* ChildExpression : Children) { ChildExpression->InvalidateCachesImpl(OutProcessed); } } 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 FRigVMBlockExprAST::IsObsolete() const { if(bIsObsolete) { return true; } struct Local { static bool HasOnlyObsoleteParents(const FRigVMExprAST* InExpr) { for(int32 ParentIndex = 0; ParentIndex < InExpr->NumParents(); ParentIndex++) { const FRigVMExprAST* ParentExpr = InExpr->ParentAt(ParentIndex); if(!IsObsoleteExpr(ParentExpr)) { return false; } } return true; } static bool IsObsoleteExpr(const FRigVMExprAST* InExpr) { if(InExpr->IsA(EType::Block)) { const FRigVMBlockExprAST* Block = InExpr->To(); if(!Block->bIsObsolete) // avoid further recursion here { return false; } } return HasOnlyObsoleteParents(InExpr); } }; if(NumParents() == 0) { return Local::IsObsoleteExpr(this); } return Local::HasOnlyObsoleteParents(this); } 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 && Child->IsA(FRigVMExprAST::Var)) { return Child->To(); } return nullptr; } const FName& FRigVMNodeExprAST::GetPinNameForChildIndex(int32 InChildIndex) const { if (const FName* PinName = PinNameToChildIndex.FindKey(InChildIndex)) { return *PinName; } static const FName EmptyString = NAME_None; return EmptyString; } void FRigVMNodeExprAST::SetSegmentPathForChild(int32 InChildIndex, const FString& InSegmentPath) { check(Children.IsValidIndex(InChildIndex)); SegmentPathForChild.FindOrAdd(InChildIndex) = InSegmentPath; } const FString& FRigVMNodeExprAST::GetSegmentPathForChild(int32 InChildIndex) const { check(Children.IsValidIndex(InChildIndex)); if(const FString* ExistingSegmentPath = SegmentPathForChild.Find(InChildIndex)) { return *ExistingSegmentPath; } static const FString EmptySegmentPath; return EmptySegmentPath; } 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) : CacheVersion(0) , 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(); if (InSettings.bFoldAssignments) { FoldAssignments(); } if (InSettings.bFoldSubPinCopies) { FoldSubPinCopies(); } if (InSettings.bFoldLiterals) { FoldLiterals(); } } 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; } // if we hit a mutable node here that hasn't been traversed yet, // it means that the node is not wired up correctly. if(Node->IsMutable()) { struct Local { static bool IsNodeWiredToEvent(const FRigVMASTProxy& InNodeProxy) { const URigVMNode* Node = InNodeProxy.GetSubjectChecked(); if(Node->IsEvent()) { return true; } for(const URigVMPin* Pin : Node->GetPins()) { if(Pin->GetDirection() != ERigVMPinDirection::Input && Pin->GetDirection() != ERigVMPinDirection::IO) { continue; } if(!Pin->IsExecuteContext()) { continue; } const TArray SourcePins = Pin->GetLinkedSourcePins(); for(const URigVMPin* SourcePin : SourcePins) { const FRigVMASTProxy SourceNodeProxy = InNodeProxy.GetSibling(SourcePin->GetNode()); if(IsNodeWiredToEvent(SourceNodeProxy)) { return true; } } } // if we hit an entry node - we need to continue the search a level up. // events cannot be placed inside of a function / collapse node so // there's no need to dive into library nodes. if(Node->IsA()) { return IsNodeWiredToEvent(InNodeProxy.GetParent()); } return false; } }; if(!Local::IsNodeWiredToEvent(InNodeProxy)) { bool bIsInObsoleteBlock = false; if(InParentExpr) { if(InParentExpr->GetBlock() == GetObsoleteBlock()) { bIsInObsoleteBlock = true; } } if(!bIsInObsoleteBlock) { Settings.Report( EMessageSeverity::Error, Node, TEXT("Node @@ is not linked to execution.")); } } } 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() || IsConstantDispatchNode(Node) || IsMakeStructDispatchNode(Node) || IsBreakStructDispatchNode(Node)) { 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); } } } } // We might have extra non-native pins, add them afterwards if (UnitNode->HasNonNativePins()) { for (URigVMPin* Pin : Node->GetPins()) { // We skip trait pins as we don't want to traverse them if (!Pin->IsTraitPin() || Settings.bSetupTraits) { Pins.AddUnique(Pin); } } } } if (Pins.IsEmpty()) { // Just grab whatever pins we have 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]); } ); } bool bForceUseOfLiteralExpression = false; if(IsConstantDispatchValuePin(Pin) || IsMakeStructDispatchElementsPin(Pin) || IsBreakStructDispatchStructPin(Pin)) { // if this is a constant with no input links at all - just use a literal for it const TArray AllLinkedPins = Pin->GetRootPin()->GetLinkedSourcePins(true); if(AllLinkedPins.IsEmpty()) { bForceUseOfLiteralExpression = true; } } 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->IsTraitPin()) { PinExpr = MakeExpr(FRigVMExprAST::EType::Var, InPinProxy); if(Settings.bSetupTraits) { // Traits can generate their own programmatic pins via FRigVMTrait::GetProgrammaticPins. We account for these as additional expressions if the // pin is not part of the set of sub-pins exposed on the struct for(URigVMPin* SubPin : Pin->SubPins) { if(SubPin->IsProgrammaticPin()) { // Not a pin from the struct - add a synthetic var for this parent too FRigVMASTProxy SubPinProxy = InPinProxy.GetSibling(SubPin); FRigVMExprAST* SubPinExpr = TraversePin(SubPinProxy, InParentExpr); const int32 ChildIndex = InParentExpr->Children.Find(SubPinExpr); InParentExpr->PinNameToChildIndex.FindOrAdd(SubPinExpr->GetName()) = ChildIndex; } } } } else if (bForceUseOfLiteralExpression) { PinExpr = MakeExpr(InPinProxy); } else if ((Pin->GetDirection() == ERigVMPinDirection::Input || Pin->GetDirection() == ERigVMPinDirection::Visible) && LinkIndices.Num() == 0) { if (Cast(Pin->GetNode()) || (LibraryNodeBeingCompiled && 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() && !PinExpr->IsA(FRigVMExprAST::EType::Literal)) { 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 && (Pin->IsArray() || Pin->GetNode()->IsA())))) { 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; } } if (!bRequiresCopy) { // Connections between entry and return in a function requires a copy if (LibraryNodeBeingCompiled) { if (SourceRootPin->GetNode()->IsA() && TargetRootPin->GetNode()->IsA()) { bRequiresCopy = true; } } } if (!bRequiresCopy) { if (SourcePin->GetTypeIndex() != TargetPin->GetTypeIndex()) { bRequiresCopy = true; } } if (!bRequiresCopy) { // Due to the unpredictability of lazy branches, we need to make sure that non-lazy pins are not // affected by the execution of lazy evaluation. if (!TargetRootPin->IsLazy() && TargetRootPin->GetNode()->HasLazyPin(true)) { bRequiresCopy = true; } } if (!bRequiresCopy) { // Programmatic pins always require a copy if(TargetPin->IsProgrammaticPin()) { 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::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; } } } // for make struct and break struct nodes we'll always refer to the input // pin's expression since the nodes are considered no-op. if(IsMakeStructDispatchNode(Node) || IsBreakStructDispatchNode(Node)) { check(Expression->Children.Num() == 2); FRigVMExprAST* ChildToKeep = Expression->Children[0]; FRigVMExprAST* ChildToRemove = Expression->Children[1]; TArray Parents = ChildToRemove->Parents; for(FRigVMExprAST* Parent : Parents) { if(Parent != Expression) { Parent->ReplaceChild(ChildToRemove, ChildToKeep); } } } } // 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; } } // if we copy onto a sub pin - let's not fold this copy expression. // we also don't allow to assign onto variable nodes if(AssignExpr->GetType() == FRigVMExprAST::EType::Copy) { if(!TargetPin->IsRootPin()) { continue; } if(TargetPin->GetNode()->IsA()) { continue; } } // non-input pins on anything but a reroute (passthrough or literal) or array node should be skipped const bool bIsReroute = TargetPin->GetNode()->IsA(); // a copy / assign to a constant node root pin can be folded const bool bIsConstantValueTarget = IsConstantDispatchValuePin(TargetPin); if (TargetPin->GetDirection() != ERigVMPinDirection::Input && !bIsReroute && !bIsConstantValueTarget) { 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; } } // Skip folding for programmatic pins as we expect them to always exist in work memory if(TargetPin->IsProgrammaticPin()) { 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(IsFloatOrFloatArrayPinOnVariable(SourcePin) || IsFloatOrFloatArrayPinOnVariable(TargetPin)) { 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(); const URigVMPin* OldSourcePin = GrandParentAssign->GetSourcePin(); const FString SourceSegmentPath = OldSourcePin->GetSegmentPath(); URigVMPin* NewSourcePin = AssignExpr->GetSourcePin(); if(IsMakeStructDispatchStructPin(NewSourcePin)) { NewSourcePin = NewSourcePin->GetNode()->FindRootPinByName(FRigVMDispatch_MakeStruct::ElementsName.Resolve()); check(NewSourcePin); } if(!SourceSegmentPath.IsEmpty()) { NewSourcePin = NewSourcePin->FindSubPin(SourceSegmentPath); if(NewSourcePin == nullptr) { Settings.Reportf( EMessageSeverity::Error, AssignExpr->GetSourcePin()->GetNode(), TEXT("Cannot resolve segment path '%s' on pin '%s'."), *SourceSegmentPath, *AssignExpr->GetSourcePin()->GetPinPath()); continue; } } GrandParentAssign->Link = FRigVMASTLinkDescription( GrandParentAssign->GetSourceProxy().GetSibling(NewSourcePin), GrandParentAssign->GetTargetProxy(), FString()); GrandParentAssign->Name = *GetLinkAsString(GrandParentAssign->GetLink()); } } ExpressionsToRemove.AddUnique(AssignExpr); if (Parent->Parents.Num() == 0) { ExpressionsToRemove.AddUnique(Parent); } } RemoveExpressions(ExpressionsToRemove); } void FRigVMParserAST::FoldSubPinCopies() { 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::Copy) { continue; } FRigVMCopyExprAST* CopyExpr = Expression->To(); if (CopyExpr->Parents.Num() != 1 || CopyExpr->Children.Num() != 1 || !CopyExpr->Link.SegmentPath.IsEmpty()) { continue; } URigVMPin* SourcePin = CopyExpr->GetSourcePin(); URigVMPin* TargetPin = CopyExpr->GetTargetPin(); // we only consider copies from a sub pin to a root pin in this phase if(SourcePin->IsRootPin() || !TargetPin->IsRootPin()) { continue; } // if the target pin is lazy - don't fold the copy if(TargetPin->IsLazy()) { 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; } if(SourcePin->GetCPPTypeObject() == nullptr) { if(SourcePin->GetCPPType() != TargetPin->GetCPPType()) { continue; } } // if the source is a sub-pin of a fixed size array, don't fold the copy if (SourcePin->GetRootPin()->IsFixedSizeArray()) { continue; } // non-input pins on anything but a reroute (passthrough or literal) const bool bIsReroute = TargetPin->GetNode()->IsA(); if (TargetPin->GetDirection() != ERigVMPinDirection::Input && !bIsReroute) { continue; } // if this node is a loop node - let's skip the folding if (const URigVMNode* ModelNode = TargetPin->GetNode()) { if (ModelNode->IsControlFlowNode()) { continue; } } // if the source or target node is a variable node, don't fold this copy if (Cast(SourcePin->GetNode()) || Cast(TargetPin->GetNode())) { continue; } // if the source node is a reroute - skip this if (Cast(SourcePin->GetNode())) { continue; } // Skip folding for programmatic pins as we expect them to always exist in work memory if(TargetPin->IsProgrammaticPin()) { continue; } // Skip folding onto a return node if (TargetPin->GetNode()->IsA()) { continue; } FRigVMExprAST* Parent = CopyExpr->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(IsFloatOrFloatArrayPinOnVariable(SourcePin) || IsFloatOrFloatArrayPinOnVariable(TargetPin)) { continue; } // avoid referencing elements of arrays - since the array may change in the interim. const URigVMPin* SourceArrayPin = SourcePin; bool bIsArrayPin = false; while (SourceArrayPin && !SourceArrayPin->IsRootPin()) { if (SourceArrayPin->IsArray() || SourceArrayPin->IsArrayElement()) { bIsArrayPin = true; break; } SourceArrayPin = SourceArrayPin->GetParentPin(); } if (bIsArrayPin) { continue; } FRigVMExprAST* Child = CopyExpr->Children[0]; CopyExpr->RemoveParent(Parent); Child->RemoveParent(CopyExpr); TArray GrandParents = Parent->Parents; for (FRigVMExprAST* GrandParent : GrandParents) { TArray ChildIndices; GrandParent->ReplaceChild(Parent, Child, &ChildIndices); if (GrandParent->IsA(FRigVMExprAST::EType::CallExtern) || GrandParent->IsA(FRigVMExprAST::EType::InlineFunction)) { const URigVMPin* CopySourcePin = CopyExpr->GetSourcePin(); check(!CopySourcePin->IsRootPin()); FRigVMNodeExprAST* GrandParentNodeExpr = GrandParent->To(); if(ensure(GrandParentNodeExpr)) { for (int32 ChildIndex : ChildIndices) { GrandParentNodeExpr->SetSegmentPathForChild(ChildIndex, CopySourcePin->GetSegmentPath()); } } } } ExpressionsToRemove.AddUnique(CopyExpr); 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 { const TArray BlockCombinationColors = { FColor(255,235,205), FColor(220,20,60), FColor(46,139,87), FColor(0,0,205), FColor(255,228,225), FColor(255,218,185), FColor(255,140,0), FColor(255,228,181), FColor(216,191,216), FColor(210,105,30), FColor(240,248,255), FColor(30,144,255), FColor(47,79,79), FColor(245,245,220), FColor(165,42,42), FColor(255,245,238), FColor(112,128,144), FColor(220,220,220), FColor(123,104,238), FColor(139,0,139), FColor(255,182,193), FColor(250,128,114), FColor(148,0,211), FColor(224,255,255), FColor(255,165,0), FColor(255,250,240), FColor(0,128,128), FColor(175,238,238), FColor(147,112,219), FColor(255,160,122), FColor(0,255,127), FColor(255,240,245), FColor(211,211,211), FColor(173,255,47), FColor(0,100,0), FColor(0,128,0), FColor(32,178,170), FColor(123,104,238), FColor(240,230,140), FColor(139,69,19), FColor(153,50,204), FColor(219,112,147), FColor(138,43,226), FColor(245,245,245), FColor(255,255,240), FColor(255,69,0), FColor(135,206,235), FColor(240,255,255), FColor(205,92,92), FColor(255,250,205), FColor(105,105,105), FColor(255,250,250), FColor(72,61,139), FColor(255,248,220), FColor(255,192,203), FColor(222,184,135), FColor(245,222,179), FColor(0,0,128), FColor(245,255,250), FColor(25,25,112), FColor(244,164,96), FColor(238,130,238), FColor(240,255,240), FColor(34,139,34), }; int32 BlockCombinationColorIndex = 0; TMap BlockCombinationHashToColor; TArray VisitChildren(const FRigVMExprAST* InExpr, int32 InSubGraphIndex, FVisualGraph& OutGraph) { TArray ChildNodeIndices; for (FRigVMExprAST* Child : InExpr->Children) { ChildNodeIndices.Add(VisitExpr(Child, InSubGraphIndex, OutGraph)); } return ChildNodeIndices; } 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()) { TOptional Color; const TOptional BlockCombinationHash = InExpr->GetBlockCombinationHash(); if(BlockCombinationHash.IsSet()) { const uint32 Hash = BlockCombinationHash.GetValue(); if(!BlockCombinationHashToColor.Contains(Hash)) { BlockCombinationHashToColor.Add(Hash, BlockCombinationColorIndex++); if(BlockCombinationColorIndex >= BlockCombinationColors.Num()) { BlockCombinationColorIndex = 0; } } Color = BlockCombinationColors[BlockCombinationHashToColor.FindChecked(Hash)]; } const TOptional DisplayName = FName(*Label); NodeIndex = OutGraph.AddNode(NodeName, DisplayName, Color, 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; } }; Local LocalStruct; for (FRigVMExprAST* Expr : RootExpressions) { if (Expr == GetObsoleteBlock(false)) { continue; } LocalStruct.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(); IncrementCacheVersion(); } 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()) { // We dont get links to programmatic pins as links are traversed manually in TraversePin rather than // using links from parent traits. This ensures we dont double-up on programmatic pin traversal. if(!SubPin->IsProgrammaticPin()) { 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(const TArray& InGraphs) { TArray LocalNodeProxies; for(URigVMGraph* Graph : InGraphs) { for (URigVMNode* LocalNode : Graph->GetNodes()) { if (IsValid(LocalNode)) { LocalNodeProxies.Add(FRigVMASTProxy::MakeFromUObject(LocalNode)); } } } Inline(InGraphs, LocalNodeProxies); } void FRigVMParserAST::Inline(const 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()) { if (Node->GetTypedOuter() == OutTraversalInfo.LibraryNodeBeingCompiled) { return false; } } } // If its a function reference, don't recurse if (Node->IsA()) { return false; } if (Cast(Node)) { return true; } if (OutTraversalInfo.Settings->bFoldSubPinCopies) { if (IsBreakStructDispatchNode(Node)) { return true; } } return 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; } } } } else if (IsBreakStructDispatchNode(ChildPin->GetNode())) { if (OutTraversalInfo.Settings->bFoldSubPinCopies) { if (URigVMPin* StructPin = ChildPin->GetNode()->FindRootPinByName(FRigVMDispatch_BreakStruct::StructName.Resolve())) { SourcePinProxy = InPinProxy.GetSibling(StructPin); if (ShouldRecursePin(SourcePinProxy, OutTraversalInfo)) { FRigVMASTProxy SourceSourcePinProxy = FindSourcePin(SourcePinProxy, 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)) { if (URigVMFunctionReferenceNode* FunctionReferenceNode = Cast(SourceNode)) { if (const FRigVMGraphFunctionData* FunctionData = FunctionReferenceNode->GetReferencedFunctionData()) { const FString PinName = FRigVMPropertyDescription::SanitizeName(SourcePin->GetFName()).ToString(); if (const FRigVMFunctionCompilationPropertyDescription* Description = FunctionData->CompilationData.WorkPropertyDescriptions.FindByPredicate([PinName](const FRigVMFunctionCompilationPropertyDescription& Description) { return Description.Name.ToString().EndsWith(PinName); })) { // when asking for the default value of the array - we may need to get the previously stored override URigVMPin::FPinOverrideValue OverrideValue; OverrideValue.DefaultValue = Description->DefaultValue; // 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(SourcePinProxy) = OverrideValue; } } } 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.GetSubject()->IsA()) { return; } const bool bIsCompilingFunction = OutTraversalInfo.LibraryNodeBeingCompiled != nullptr; if (bIsCompilingFunction) { if (InNodeProxy.IsA()) { URigVMNode* Node = InNodeProxy.GetSubjectChecked(); if (Node->GetTypedOuter() != OutTraversalInfo.LibraryNodeBeingCompiled) { return; } } } else { if (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)); } void FRigVMParserAST::IncrementCacheVersion() const { CacheVersion++; } bool FRigVMParserAST::IsConstantDispatchNode(const URigVMNode* InNode) { if(const URigVMDispatchNode* DispatchNode = Cast(InNode)) { if(const FRigVMDispatchFactory* Factory = DispatchNode->GetFactory()) { return Factory->GetScriptStruct() == FRigVMDispatch_Constant::StaticStruct(); } } return false; } bool FRigVMParserAST::IsConstantDispatchValuePin(const URigVMPin* InPin) { if(InPin && InPin->IsRootPin() && IsConstantDispatchNode(InPin->GetNode())) { return InPin->GetFName() == FRigVMDispatch_Constant::ValueName.Resolve(); } return false; } bool FRigVMParserAST::IsMakeStructDispatchNode(const URigVMNode* InNode) { if(const URigVMDispatchNode* DispatchNode = Cast(InNode)) { if(const FRigVMDispatchFactory* Factory = DispatchNode->GetFactory()) { return Factory->GetScriptStruct() == FRigVMDispatch_MakeStruct::StaticStruct(); } } return false; } bool FRigVMParserAST::IsMakeStructDispatchElementsPin(const URigVMPin* InPin) { if(InPin && InPin->IsRootPin() && IsMakeStructDispatchNode(InPin->GetNode())) { return InPin->GetFName() == FRigVMDispatch_MakeStruct::ElementsName.Resolve(); } return false; } bool FRigVMParserAST::IsMakeStructDispatchStructPin(const URigVMPin* InPin) { if(InPin && InPin->IsRootPin() && IsMakeStructDispatchNode(InPin->GetNode())) { return InPin->GetFName() == FRigVMDispatch_MakeStruct::StructName.Resolve(); } return false; } bool FRigVMParserAST::IsBreakStructDispatchNode(const URigVMNode* InNode) { if(const URigVMDispatchNode* DispatchNode = Cast(InNode)) { if(const FRigVMDispatchFactory* Factory = DispatchNode->GetFactory()) { return Factory->GetScriptStruct() == FRigVMDispatch_BreakStruct::StaticStruct(); } } return false; } bool FRigVMParserAST::IsBreakStructDispatchStructPin(const URigVMPin* InPin) { if(InPin && InPin->IsRootPin() && IsBreakStructDispatchNode(InPin->GetNode())) { return InPin->GetFName() == FRigVMDispatch_MakeStruct::StructName.Resolve(); } return false; } bool FRigVMParserAST::IsBreakStructDispatchElementsPin(const URigVMPin* InPin) { if(InPin && InPin->IsRootPin() && IsBreakStructDispatchNode(InPin->GetNode())) { return InPin->GetFName() == FRigVMDispatch_MakeStruct::ElementsName.Resolve(); } return false; } bool FRigVMParserAST::IsFloatOrFloatArrayPinOnVariable(const URigVMPin* InPin) { // 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 (InPin->GetCPPType() == TEXT("float") || InPin->GetCPPType() == TEXT("TArray")) { if (const URigVMVariableNode* VariableNode = Cast(InPin->GetNode())) { if (!VariableNode->IsInputArgument() && !VariableNode->IsLocalVariable()) { return true; } } } return false; }