Files
UnrealEngineUWP/Engine/Source/Editor/AnimGraph/Private/AnimBlueprintExtension_Base.h
Thomas Sarkanen 3e92b56aa0 Animation: thread-safe execution and property access improvements
"Call function" anim node:
- Adds the ability to call functions at different points in the anim graph execution (and under different conditions, e.g. when a branch has started blending in). Only thread-safe functions are allowed to be called.
- Adds a thread-safe override point BlueprintThreadSafeUpdateAnimation, called on worker threads for the main instance and for linked instances when they are relevant in the graph (only one call per frame for linked layer instances).

Subsystems:
- Added new override points pre/post event graph(s) (moved override point for worker thread work to around the thread safe update function call).

Improves property access integration:
- Property access now shows (and allows the user to override) the call site of accesses. This is to allow users to see when their property access calls will be made, hopefully making its use less confusing for power users.
- Tweaked UX for property access nodes and dropdowns.
- Anim node pins now have property access bindings in-line on the pin.

Also adds the abilility for the anim graph to opt-in (via a config flag) to more stringent thread safety checks. Disabled by default for now as this requires content fixup.

#jira UE-115745 - Anim Blueprint Encapsulation
#rb Jurre.deBaare

[CL 16434092 by Thomas Sarkanen in ue5-main branch]
2021-05-24 04:47:52 -04:00

258 lines
9.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AnimBlueprintExtension.h"
#include "AnimBlueprintExtension_PropertyAccess.h"
#include "Animation/AnimNodeBase.h"
#include "IPropertyAccessCompiler.h"
#include "Animation/AnimSubsystem_Base.h"
#include "AnimBlueprintExtension_Base.generated.h"
class UAnimGraphNode_Base;
struct FAnimGraphNodePropertyBinding;
class UK2Node_CustomEvent;
class IAnimBlueprintCompilationContext;
class IAnimBlueprintGeneratedClassCompiledData;
class IAnimBlueprintCompilerCreationContext;
class IAnimBlueprintCompilationBracketContext;
class IAnimBlueprintPostExpansionStepContext;
class IAnimBlueprintCopyTermDefaultsContext;
UCLASS(MinimalAPI)
class UAnimBlueprintExtension_Base : public UAnimBlueprintExtension
{
GENERATED_BODY()
public:
// Processes a node's pins:
// - Processes pose pins, building a map of pose links for later processing
// - Adds a map of struct eval handlers for the specified node
// - Builds property binding data
void ProcessNodePins(UAnimGraphNode_Base* InNode, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData);
// Create an 'expanded' evaluation handler for the specified node, called in the compiler's node expansion step
void CreateEvaluationHandlerForNode(IAnimBlueprintCompilationContext& InCompilationContext, UAnimGraphNode_Base* InNode);
private:
// UAnimBlueprintExtension interface
virtual void HandleStartCompilingClass(const UClass* InClass, IAnimBlueprintCompilationBracketContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData) override;
virtual void HandleFinishCompilingClass(const UClass* InClass, IAnimBlueprintCompilationBracketContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData) override;
virtual void HandlePostExpansionStep(const UEdGraph* InGraph, IAnimBlueprintPostExpansionStepContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData) override;
virtual void HandleCopyTermDefaultsToDefaultObject(UObject* InDefaultObject, IAnimBlueprintCopyTermDefaultsContext& InCompilationContext, IAnimBlueprintExtensionCopyTermDefaultsContext& InPerExtensionContext) override;
// Patch all node's evaluation handlers
void PatchEvaluationHandlers(IAnimBlueprintCompilationBracketContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData);
private:
/** Record of a single copy operation */
struct FPropertyCopyRecord
{
FPropertyCopyRecord(UEdGraphPin* InDestPin, FProperty* InDestProperty, int32 InDestArrayIndex, TArray<FString>&& InDestPropertyPath)
: DestPin(InDestPin)
, DestProperty(InDestProperty)
, DestArrayIndex(InDestArrayIndex)
, DestPropertyPath(MoveTemp(InDestPropertyPath))
, BindingContextId(NAME_None)
, Operation(EPostCopyOperation::None)
, bIsFastPath(true)
{}
FPropertyCopyRecord(const TArray<FString>& InSourcePropertyPath, const TArray<FString>& InDestPropertyPath)
: DestPin(nullptr)
, DestProperty(nullptr)
, DestArrayIndex(INDEX_NONE)
, SourcePropertyPath(InSourcePropertyPath)
, DestPropertyPath(InDestPropertyPath)
, BindingContextId(NAME_None)
, Operation(EPostCopyOperation::None)
, bIsFastPath(true)
{}
bool IsFastPath() const
{
return SourcePropertyPath.Num() > 0 && bIsFastPath;
}
void InvalidateFastPath()
{
bIsFastPath = false;
}
/** The destination pin we are copying to */
UEdGraphPin* DestPin;
/** The destination property we are copying to (on an animation node) */
FProperty* DestProperty;
/** The array index we use if the destination property is an array */
int32 DestArrayIndex;
/** The property path relative to the class */
TArray<FString> SourcePropertyPath;
/** The property path relative to the class */
TArray<FString> DestPropertyPath;
/** The handle of the copy in the property access compiler */
FPropertyAccessHandle LibraryHandle;
/** The handle of the copy in the property access library */
FCompiledPropertyAccessHandle LibraryCompiledHandle;
// Context in which a property binding occurs
FName BindingContextId;
/** Any operation we want to perform post-copy on the destination data */
EPostCopyOperation Operation;
/** Fast-path flag */
bool bIsFastPath;
};
// Context used to build fast-path copy records
struct FCopyRecordGraphCheckContext
{
FCopyRecordGraphCheckContext(FPropertyCopyRecord& InCopyRecord, TArray<FPropertyCopyRecord>& InAdditionalCopyRecords, FCompilerResultsLog& InMessageLog)
: CopyRecord(&InCopyRecord)
, AdditionalCopyRecords(InAdditionalCopyRecords)
, MessageLog(InMessageLog)
{}
// Copy record we are operating on
FPropertyCopyRecord* CopyRecord;
// Things like split input pins can add additional copy records
TArray<FPropertyCopyRecord>& AdditionalCopyRecords;
// Message log used to recover original nodes
FCompilerResultsLog& MessageLog;
};
// Wireup record for a single anim node property (which might be an array)
struct FAnimNodeSinglePropertyHandler
{
/** Copy records */
TArray<FPropertyCopyRecord> CopyRecords;
// If the anim instance is the container target instead of the node.
bool bInstanceIsTarget;
FAnimNodeSinglePropertyHandler()
: bInstanceIsTarget(false)
{
}
};
/** BP execution handler for Anim node */
struct FEvaluationHandlerRecord
{
public:
// The Node this record came from
UAnimGraphNode_Base* AnimGraphNode;
// The node variable that the handler is in
FStructProperty* NodeVariableProperty;
// The specific evaluation handler inside the specified node
int32 EvaluationHandlerIdx;
// Whether or not our serviced properties are actually on the anim node
bool bServicesNodeProperties;
// Whether or not our serviced properties are actually on the instance instead of the node
bool bServicesInstanceProperties;
// Set of properties serviced by this handler (Map from property name to the record for that property)
TMap<FName, FAnimNodeSinglePropertyHandler> ServicedProperties;
// The generated custom event node
TArray<UEdGraphNode*> CustomEventNodes;
// The resulting function name
FName HandlerFunctionName;
public:
FEvaluationHandlerRecord()
: AnimGraphNode(nullptr)
, NodeVariableProperty(nullptr)
, EvaluationHandlerIdx(INDEX_NONE)
, bServicesNodeProperties(false)
, bServicesInstanceProperties(false)
, HandlerFunctionName(NAME_None)
{}
bool IsFastPath() const
{
for(TMap<FName, FAnimNodeSinglePropertyHandler>::TConstIterator It(ServicedProperties); It; ++It)
{
const FAnimNodeSinglePropertyHandler& AnimNodeSinglePropertyHandler = It.Value();
for (const FPropertyCopyRecord& CopyRecord : AnimNodeSinglePropertyHandler.CopyRecords)
{
if (!CopyRecord.IsFastPath())
{
return false;
}
}
}
return true;
}
bool IsValid() const
{
return NodeVariableProperty != nullptr;
}
void PatchFunctionNameAndCopyRecordsInto(FExposedValueHandler& Handler) const;
void RegisterPin(UEdGraphPin* DestPin, FProperty* AssociatedProperty, int32 AssociatedPropertyArrayIndex);
void RegisterPropertyBinding(FProperty* InProperty, const FAnimGraphNodePropertyBinding& InBinding);
FStructProperty* GetHandlerNodeProperty() const { return NodeVariableProperty; }
void BuildFastPathCopyRecords(IAnimBlueprintPostExpansionStepContext& InCompilationContext);
private:
bool CheckForVariableGet(FCopyRecordGraphCheckContext& Context, UEdGraphPin* DestPin);
bool CheckForLogicalNot(FCopyRecordGraphCheckContext& Context, UEdGraphPin* DestPin);
bool CheckForStructMemberAccess(FCopyRecordGraphCheckContext& Context, UEdGraphPin* DestPin);
bool CheckForMemberOnlyAccess(FPropertyCopyRecord& Context, UEdGraphPin* DestPin);
bool CheckForSplitPinAccess(FCopyRecordGraphCheckContext& Context, UEdGraphPin* DestPin);
bool CheckForArrayAccess(FCopyRecordGraphCheckContext& Context, UEdGraphPin* DestPin);
};
// Create an evaluation handler for the specified node/record
void CreateEvaluationHandler(IAnimBlueprintCompilationContext& InCompilationContext, UAnimGraphNode_Base* InNode, FEvaluationHandlerRecord& Record);
// Redirect any property accesses that are affected by constant folding
void RedirectPropertyAccesses(IAnimBlueprintCompilationContext& InCompilationContext, UAnimGraphNode_Base* InNode, FEvaluationHandlerRecord& InRecord);
private:
// Records of pose pins for later patchup with an associated evaluation handler
TMap<UAnimGraphNode_Base*, FEvaluationHandlerRecord> PerNodeStructEvalHandlers;
// List of successfully created evaluation handlers
TArray<FEvaluationHandlerRecord> ValidEvaluationHandlerList;
TMap<UAnimGraphNode_Base*, int32> ValidEvaluationHandlerMap;
// Set of used handler function names
TSet<FName> HandlerFunctionNames;
// Delegate handle for registering against library pre/post-compilation
FDelegateHandle PreLibraryCompiledDelegateHandle;
FDelegateHandle PostLibraryCompiledDelegateHandle;
// Base subsystem data containing eval handlers
UPROPERTY()
FAnimSubsystem_Base Subsystem;
};