Files
UnrealEngineUWP/Engine/Source/Developer/RigVMDeveloper/Private/RigVMModel/RigVMNode.cpp
helge mathee 3acbeeb2d3 RigVM: Store a hash along side each graph identifying the structural state
Avoid rebuilding graphs if we don't need to. hashes contain the structure of each node, the structure of any used data type and the structure of each used template.

#rb sara.schvartzman
#preflight https://horde.devtools.epicgames.com/job/63ee1c6fc9692d7c04423941

[CL 24280391 by helge mathee in ue5-main branch]
2023-02-17 08:46:05 -05:00

741 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RigVMModel/RigVMNode.h"
#include "RigVMStringUtils.h"
#include "RigVMModel/Nodes/RigVMUnitNode.h"
#include "RigVMModel/RigVMGraph.h"
#include "RigVMModel/Nodes/RigVMLibraryNode.h"
#include "RigVMModel/RigVMFunctionLibrary.h"
#include "RigVMCore/RigVMExecuteContext.h"
#include "RigVMCore/RigVMStruct.h"
#include "RigVMUserWorkflowRegistry.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMNode)
const FString URigVMNode::NodeColorName = TEXT("NodeColor");
#if WITH_EDITOR
TArray<int32> URigVMNode::EmptyInstructionArray;
#endif
URigVMNode::URigVMNode()
: UObject()
, Position(FVector2D::ZeroVector)
, Size(FVector2D::ZeroVector)
, NodeColor(FLinearColor::White)
, bHasBreakpoint(false)
, bHaltedAtThisNode(false)
#if WITH_EDITOR
, ProfilingHash(0)
#endif
{
}
URigVMNode::~URigVMNode()
{
}
FString URigVMNode::GetNodePath(bool bRecursive) const
{
if (bRecursive)
{
if(URigVMGraph* Graph = GetGraph())
{
const FString ParentNodePath = Graph->GetNodePath();
if (!ParentNodePath.IsEmpty())
{
return JoinNodePath(ParentNodePath, GetName());
}
}
}
return GetName();
}
bool URigVMNode::SplitNodePathAtStart(const FString& InNodePath, FString& LeftMost, FString& Right)
{
return RigVMStringUtils::SplitNodePathAtStart(InNodePath, LeftMost, Right);
}
bool URigVMNode::SplitNodePathAtEnd(const FString& InNodePath, FString& Left, FString& RightMost)
{
return RigVMStringUtils::SplitNodePathAtEnd(InNodePath, Left, RightMost);
}
bool URigVMNode::SplitNodePath(const FString& InNodePath, TArray<FString>& Parts)
{
return RigVMStringUtils::SplitNodePath(InNodePath, Parts);
}
FString URigVMNode::JoinNodePath(const FString& Left, const FString& Right)
{
return RigVMStringUtils::JoinNodePath(Left, Right);
}
FString URigVMNode::JoinNodePath(const TArray<FString>& InParts)
{
return RigVMStringUtils::JoinNodePath(InParts);
}
int32 URigVMNode::GetNodeIndex() const
{
int32 Index = INDEX_NONE;
URigVMGraph* Graph = GetGraph();
if (Graph != nullptr)
{
Graph->GetNodes().Find((URigVMNode*)this, Index);
}
return Index;
}
const TArray<URigVMPin*>& URigVMNode::GetPins() const
{
return Pins;
}
TArray<URigVMPin*> URigVMNode::GetAllPinsRecursively() const
{
struct Local
{
static void VisitPinRecursively(URigVMPin* InPin, TArray<URigVMPin*>& OutPins)
{
OutPins.Add(InPin);
for (URigVMPin* SubPin : InPin->GetSubPins())
{
VisitPinRecursively(SubPin, OutPins);
}
}
};
TArray<URigVMPin*> Result;
for (URigVMPin* Pin : GetPins())
{
Local::VisitPinRecursively(Pin, Result);
}
return Result;
}
URigVMPin* URigVMNode::FindPin(const FString& InPinPath) const
{
FString Left, Right;
if (!URigVMPin::SplitPinPathAtStart(InPinPath, Left, Right))
{
Left = InPinPath;
}
for (URigVMPin* Pin : GetPins())
{
if (Pin->NameEquals(Left, true))
{
if (Right.IsEmpty())
{
return Pin;
}
return Pin->FindSubPin(Right);
}
}
if(Left.StartsWith(URigVMPin::OrphanPinPrefix))
{
for (URigVMPin* Pin : OrphanedPins)
{
if (Pin->GetName() == Left)
{
if (Right.IsEmpty())
{
return Pin;
}
return Pin->FindSubPin(Right);
}
}
}
return nullptr;
}
const TArray<URigVMPin*>& URigVMNode::GetOrphanedPins() const
{
return OrphanedPins;
}
URigVMGraph* URigVMNode::GetGraph() const
{
if (URigVMGraph* Graph = Cast<URigVMGraph>(GetOuter()))
{
return Graph;
}
if (URigVMInjectionInfo* InjectionInfo = GetInjectionInfo())
{
return InjectionInfo->GetGraph();
}
return nullptr;
}
URigVMGraph* URigVMNode::GetRootGraph() const
{
if (URigVMGraph* Graph = GetGraph())
{
return Graph->GetRootGraph();
}
return nullptr;
}
URigVMInjectionInfo* URigVMNode::GetInjectionInfo() const
{
return Cast<URigVMInjectionInfo>(GetOuter());
}
FString URigVMNode::GetNodeTitle() const
{
if (!NodeTitle.IsEmpty())
{
return NodeTitle;
}
return GetName();
}
FVector2D URigVMNode::GetPosition() const
{
return Position;
}
FVector2D URigVMNode::GetSize() const
{
return Size;
}
FLinearColor URigVMNode::GetNodeColor() const
{
return NodeColor;
}
FText URigVMNode::GetToolTipText() const
{
return FText::FromName(GetFName());
}
FText URigVMNode::GetToolTipTextForPin(const URigVMPin* InPin) const
{
return FText::FromName(InPin->GetFName());
}
bool URigVMNode::IsSelected() const
{
URigVMGraph* Graph = GetGraph();
if (Graph)
{
return Graph->IsNodeSelected(GetFName());
}
return false;
}
bool URigVMNode::IsInjected() const
{
return Cast<URigVMInjectionInfo>(GetOuter()) != nullptr;
}
bool URigVMNode::IsVisibleInUI() const
{
return !IsInjected();
}
bool URigVMNode::IsPure() const
{
if(IsMutable())
{
return false;
}
for (URigVMPin* Pin : GetPins())
{
if(Pin->GetDirection() == ERigVMPinDirection::Hidden)
{
return false;
}
}
return true;
}
bool URigVMNode::IsMutable() const
{
for (const URigVMPin* Pin : GetPins())
{
if(const UScriptStruct* ScriptStruct = Pin->GetScriptStruct())
{
return ScriptStruct->IsChildOf(FRigVMExecuteContext::StaticStruct());
}
}
return false;
}
bool URigVMNode::HasWildCardPin() const
{
for (const URigVMPin* Pin : GetPins())
{
if (Pin->IsWildCard())
{
return true;
}
}
return false;
}
bool URigVMNode::IsEvent() const
{
return IsMutable() && !GetEventName().IsNone();
}
FName URigVMNode::GetEventName() const
{
return NAME_None;
}
bool URigVMNode::CanOnlyExistOnce() const
{
return false;
}
bool URigVMNode::HasInputPin(bool bIncludeIO) const
{
if (HasPinOfDirection(ERigVMPinDirection::Input))
{
return true;
}
if (bIncludeIO)
{
return HasPinOfDirection(ERigVMPinDirection::IO);
}
return false;
}
bool URigVMNode::HasIOPin() const
{
return HasPinOfDirection(ERigVMPinDirection::IO);
}
bool URigVMNode::HasLazyPin(bool bOnlyConsiderPinsWithLinks) const
{
return Pins.ContainsByPredicate([bOnlyConsiderPinsWithLinks](const URigVMPin* Pin) -> bool
{
if(Pin->IsLazy())
{
if(bOnlyConsiderPinsWithLinks)
{
return Pin->GetLinkedSourcePins(true).Num() > 0;
}
return true;
}
return false;
});
}
bool URigVMNode::HasOutputPin(bool bIncludeIO) const
{
if (HasPinOfDirection(ERigVMPinDirection::Output))
{
return true;
}
if (bIncludeIO)
{
return HasPinOfDirection(ERigVMPinDirection::IO);
}
return false;
}
bool URigVMNode::HasPinOfDirection(ERigVMPinDirection InDirection) const
{
for (URigVMPin* Pin : GetPins())
{
if (Pin->GetDirection() == InDirection)
{
return true;
}
}
return false;
}
bool URigVMNode::IsLinkedTo(URigVMNode* InNode) const
{
if (InNode == nullptr)
{
return false;
}
if (InNode == this)
{
return false;
}
if (GetGraph() != InNode->GetGraph())
{
return false;
}
for (URigVMPin* Pin : GetPins())
{
if (IsLinkedToRecursive(Pin, InNode))
{
return true;
}
}
return false;
}
uint32 URigVMNode::GetStructureHash() const
{
uint32 Hash = GetTypeHash(GetName());
for(const URigVMPin* Pin : Pins)
{
const uint32 PinHash = Pin->GetStructureHash();
Hash = HashCombine(Hash, PinHash);
}
return Hash;
}
URigVMLibraryNode* URigVMNode::FindFunctionForNode()
{
UObject* Subject = this;
while (Subject->GetOuter() && !Subject->GetOuter()->IsA<URigVMFunctionLibrary>())
{
Subject = Subject->GetOuter();
if(Subject == nullptr)
{
return nullptr;
}
}
return Cast<URigVMLibraryNode>(Subject);
}
bool URigVMNode::IsLinkedToRecursive(URigVMPin* InPin, URigVMNode* InNode) const
{
for (URigVMPin* LinkedPin : InPin->GetLinkedSourcePins())
{
if (LinkedPin->GetNode() == InNode)
{
return true;
}
}
for (URigVMPin* LinkedPin : InPin->GetLinkedTargetPins())
{
if (LinkedPin->GetNode() == InNode)
{
return true;
}
}
for (URigVMPin* SubPin : InPin->GetSubPins())
{
if (IsLinkedToRecursive(SubPin, InNode))
{
return true;
}
}
return false;
}
TArray<URigVMLink*> URigVMNode::GetLinks() const
{
TArray<URigVMLink*> Links;
struct Local
{
static void Traverse(URigVMPin* InPin, TArray<URigVMLink*>& Links)
{
Links.Append(InPin->GetLinks());
for (URigVMPin* SubPin : InPin->GetSubPins())
{
Local::Traverse(SubPin, Links);
}
}
};
for (URigVMPin* Pin : GetPins())
{
Local::Traverse(Pin, Links);
}
return Links;
}
TArray<URigVMNode*> URigVMNode::GetLinkedSourceNodes() const
{
TArray<URigVMNode*> Nodes;
for (URigVMPin* Pin : GetPins())
{
GetLinkedNodesRecursive(Pin, true, Nodes);
}
return Nodes;
}
TArray<URigVMNode*> URigVMNode::GetLinkedTargetNodes() const
{
TArray<URigVMNode*> Nodes;
for (URigVMPin* Pin : GetPins())
{
GetLinkedNodesRecursive(Pin, false, Nodes);
}
return Nodes;
}
void URigVMNode::GetLinkedNodesRecursive(URigVMPin* InPin, bool bLookForSources, TArray<URigVMNode*>& OutNodes) const
{
TArray<URigVMPin*> LinkedPins = bLookForSources ? InPin->GetLinkedSourcePins() : InPin->GetLinkedTargetPins();
for (URigVMPin* LinkedPin : LinkedPins)
{
OutNodes.AddUnique(LinkedPin->GetNode());
}
for (URigVMPin* SubPin : InPin->GetSubPins())
{
GetLinkedNodesRecursive(SubPin, bLookForSources, OutNodes);
}
}
const TArray<int32>& URigVMNode::GetInstructionsForVM(URigVM* InVM, const FRigVMASTProxy& InProxy) const
{
if(const FProfilingCache* Cache = UpdateProfilingCacheIfNeeded(InVM, InProxy))
{
return Cache->Instructions;
}
return EmptyInstructionArray;
}
TArray<int32> URigVMNode::GetInstructionsForVMImpl(URigVM* InVM, const FRigVMASTProxy& InProxy) const
{
TArray<int32> Instructions;
#if WITH_EDITOR
if(InVM == nullptr)
{
return Instructions;
}
if(InProxy.IsValid())
{
const FRigVMASTProxy Proxy = InProxy.GetChild((UObject*)this);
return InVM->GetByteCode().GetAllInstructionIndicesForCallstack(Proxy.GetCallstack().GetStack());
}
else
{
return InVM->GetByteCode().GetAllInstructionIndicesForSubject((URigVMNode*)this);
}
#endif
return Instructions;
}
int32 URigVMNode::GetInstructionVisitedCount(URigVM* InVM, const FRigVMASTProxy& InProxy) const
{
#if WITH_EDITOR
if(InVM)
{
if(const FProfilingCache* Cache = UpdateProfilingCacheIfNeeded(InVM, InProxy))
{
return Cache->VisitedCount;
}
}
#endif
return 0;
}
double URigVMNode::GetInstructionMicroSeconds(URigVM* InVM, const FRigVMASTProxy& InProxy) const
{
#if WITH_EDITOR
if(InVM)
{
if(const FProfilingCache* Cache = UpdateProfilingCacheIfNeeded(InVM, InProxy))
{
return Cache->MicroSeconds;
}
}
#endif
return -1.0;
}
bool URigVMNode::IsLoopNode() const
{
if(IsControlFlowNode())
{
static const TArray<FName> ExpectedLoopBlocks = {FRigVMStruct::ExecuteContextName, FRigVMStruct::ForLoopCompletedPinName};
const TArray<FName>& Blocks = GetControlFlowBlocks();
if(Blocks.Num() == ExpectedLoopBlocks.Num())
{
return Blocks[0] == ExpectedLoopBlocks[0] && Blocks[1] == ExpectedLoopBlocks[1];
}
}
return false;
}
bool URigVMNode::IsControlFlowNode() const
{
return !GetControlFlowBlocks().IsEmpty();
}
const TArray<FName>& URigVMNode::GetControlFlowBlocks() const
{
static const TArray<FName> EmptyArray;
return EmptyArray;
}
const bool URigVMNode::IsControlFlowBlockSliced(const FName& InBlockName) const
{
return false;
}
TArray<FRigVMUserWorkflow> URigVMNode::GetSupportedWorkflows(ERigVMUserWorkflowType InType, const UObject* InSubject) const
{
if(InSubject == nullptr)
{
InSubject = this;
}
const UScriptStruct* Struct = nullptr;
if(const URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(this))
{
Struct = UnitNode->GetScriptStruct();
}
return URigVMUserWorkflowRegistry::Get()->GetWorkflows(InType, Struct, InSubject);
}
bool URigVMNode::IsAggregate() const
{
#if UE_RIGVM_AGGREGATE_NODES_ENABLED
const TArray<URigVMPin*> AggregateInputs = GetAggregateInputs();
const TArray<URigVMPin*> AggregateOutputs = GetAggregateOutputs();
if ((AggregateInputs.Num() == 2 && AggregateOutputs.Num() == 1) ||
(AggregateInputs.Num() == 1 && AggregateOutputs.Num() == 2))
{
TArray<URigVMPin*> AggregateAll = AggregateInputs;
AggregateAll.Append(AggregateOutputs);
for (int32 i = 1; i < 3; ++i)
{
if (AggregateAll[0]->GetCPPType() != AggregateAll[i]->GetCPPType() ||
AggregateAll[0]->GetCPPTypeObject() != AggregateAll[i]->GetCPPTypeObject())
{
return false;
}
}
return true;
}
#endif
return false;
}
URigVMPin* URigVMNode::GetFirstAggregatePin() const
{
#if UE_RIGVM_AGGREGATE_NODES_ENABLED
const TArray<URigVMPin*> Inputs = GetAggregateInputs();
const TArray<URigVMPin*> Outputs = GetAggregateOutputs();
if (Inputs.Num() == 2 && Outputs.Num() == 1)
{
return Inputs[0];
}
if (Inputs.Num() == 1 && Outputs.Num() == 2)
{
return Outputs[0];
}
#endif
return nullptr;
}
URigVMPin* URigVMNode::GetSecondAggregatePin() const
{
#if UE_RIGVM_AGGREGATE_NODES_ENABLED
const TArray<URigVMPin*> Inputs = GetAggregateInputs();
const TArray<URigVMPin*> Outputs = GetAggregateOutputs();
if (Inputs.Num() == 2 && Outputs.Num() == 1)
{
return Inputs[1];
}
if (Inputs.Num() == 1 && Outputs.Num() == 2)
{
return Outputs[1];
}
#endif
return nullptr;
}
URigVMPin* URigVMNode::GetOppositeAggregatePin() const
{
#if UE_RIGVM_AGGREGATE_NODES_ENABLED
const TArray<URigVMPin*> Inputs = GetAggregateInputs();
const TArray<URigVMPin*> Outputs = GetAggregateOutputs();
if (Inputs.Num() == 2 && Outputs.Num() == 1)
{
return Outputs[0];
}
if (Inputs.Num() == 1 && Outputs.Num() == 2)
{
return Inputs[0];
}
#endif
return nullptr;
}
bool URigVMNode::IsInputAggregate() const
{
return GetAggregateInputs().Num() == 2;
}
#if WITH_EDITOR
const URigVMNode::FProfilingCache* URigVMNode::UpdateProfilingCacheIfNeeded(URigVM* InVM, const FRigVMASTProxy& InProxy) const
{
if(InVM == nullptr)
{
return nullptr;
}
const uint32 VMHash = HashCombine(GetTypeHash(InVM), GetTypeHash(InVM->GetNumExecutions()));
if(VMHash != ProfilingHash)
{
ProfilingCache.Reset();
}
ProfilingHash = VMHash;
const uint32 ProxyHash = InProxy.IsValid() ? GetTypeHash(InProxy) : GetTypeHash(this);
const TSharedPtr<FProfilingCache>* ExistingCache = ProfilingCache.Find(ProxyHash);
if(ExistingCache)
{
return ExistingCache->Get();
}
TSharedPtr<FProfilingCache> Cache(new FProfilingCache);
Cache->Instructions = GetInstructionsForVMImpl(InVM, InProxy);
Cache->VisitedCount = 0;
Cache->MicroSeconds = -1.0;
if(Cache->Instructions.Num() > 0)
{
for(const int32 Instruction : Cache->Instructions)
{
const int32 CountPerInstruction = InVM->GetInstructionVisitedCount(Instruction);
Cache->VisitedCount += CountPerInstruction;
const double MicroSecondsPerInstruction = InVM->GetInstructionMicroSeconds(Instruction);
if(MicroSecondsPerInstruction >= 0.0)
{
if(Cache->MicroSeconds < 0.0)
{
Cache->MicroSeconds = MicroSecondsPerInstruction;
}
else
{
Cache->MicroSeconds += MicroSecondsPerInstruction;
}
}
}
}
ProfilingCache.Add(ProxyHash, Cache);
return Cache.Get();;
}
#endif