2019-12-26 15:33:43 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-08-08 15:35:56 -04:00
# include "BlueprintNodeTemplateCache.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "Engine/Blueprint.h"
# include "UObject/UObjectHash.h"
# include "UObject/MetaData.h"
# include "GameFramework/Actor.h"
# include "Animation/AnimBlueprint.h"
# include "Engine/BlueprintGeneratedClass.h"
# include "Animation/AnimBlueprintGeneratedClass.h"
# include "EdGraph/EdGraphSchema.h"
2014-08-08 15:35:56 -04:00
# include "EdGraph/EdGraph.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "Kismet2/BlueprintEditorUtils.h"
2016-06-16 11:54:44 -04:00
# include "Animation/AnimInstance.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "Kismet2/KismetEditorUtilities.h"
# include "BlueprintEditorSettings.h"
2014-09-13 15:13:16 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogBlueprintNodeCache , Log , All ) ;
2014-08-08 15:35:56 -04:00
/*******************************************************************************
* Static FBlueprintNodeTemplateCache Helpers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
namespace BlueprintNodeTemplateCacheImpl
{
2014-09-15 15:52:55 -04:00
/** */
static int32 ActiveMemFootprint = 0 ;
/** Used to track the average blueprint size (so that we can try to predict when a blueprint would fail to be cached) */
static int32 MadeBlueprintCount = 0 ;
static int32 AverageBlueprintSize = 0 ;
/** Metadata tag used to identify graphs created by this system. */
static const FName TemplateGraphMetaTag ( TEXT ( " NodeTemplateCache_Graph " ) ) ;
2014-08-08 15:35:56 -04:00
/**
2014-09-15 15:52:55 -04:00
* Checks to see if this node is compatible with the given graph ( to know if
* a node template can be spawned within it ) .
2014-08-08 15:35:56 -04:00
*
2014-09-15 15:52:55 -04:00
* @ param NodeObj The CDO of the node you want to spawn .
* @ param Graph The graph you want to check compatibility for .
* @ return True if the node and graph are compatible ( for templating purposes ) .
2014-08-08 15:35:56 -04:00
*/
static bool IsCompatible ( UEdGraphNode * NodeObj , UEdGraph * Graph ) ;
/**
2014-09-15 15:52:55 -04:00
* Looks through a blueprint for compatible graphs ( ones that the specified
* node can spawn into ) .
2014-08-08 15:35:56 -04:00
*
2014-09-15 15:52:55 -04:00
* @ param BlueprintOuter The blueprint to search through .
* @ param NodeObj The CDO of the node you want to spawn .
* @ param IsCompatibleFunc An option callback function to further filter out incompatible nodes .
* @ return The first compatible graph found ( null if no graph was found ) .
2014-08-08 15:35:56 -04:00
*/
2014-09-15 15:52:55 -04:00
static UEdGraph * FindCompatibleGraph ( UBlueprint * BlueprintOuter , UEdGraphNode * NodeObj , bool ( * IsCompatibleFunc ) ( UEdGraph * ) = nullptr ) ;
2014-08-08 15:35:56 -04:00
/**
2014-09-15 15:52:55 -04:00
* Creates a transient , temporary blueprint . Intended to be used as a
* template - node ' s outer ( grandparent ) .
2014-08-08 15:35:56 -04:00
*
2014-09-15 15:52:55 -04:00
* @ param BlueprintClass The class of blueprint to make .
* @ param ParentClass The class type of blueprint to make ( actor , object , etc . ) .
* @ param GeneratedClassType The type of class that the blueprint should generate .
* @ return A newly spawned ( transient ) blueprint .
2014-08-08 15:35:56 -04:00
*/
static UBlueprint * MakeCompatibleBlueprint ( TSubclassOf < UBlueprint > BlueprintClass , UClass * ParentClass , TSubclassOf < UBlueprintGeneratedClass > GeneratedClassType ) ;
/**
2014-09-15 15:52:55 -04:00
* Creates a new transient graph , for template node use ( meant to be used as
* a template node ' s outer ) .
2014-08-08 15:35:56 -04:00
*
2014-09-15 15:52:55 -04:00
* @ param BlueprintOuter The blueprint to nest the new graph under .
* @ param SchemaClass The schema to assign the new graph .
* @ return A newly created graph .
2014-08-08 15:35:56 -04:00
*/
static UEdGraph * AddGraph ( UBlueprint * BlueprintOuter , TSubclassOf < UEdGraphSchema > SchemaClass ) ;
2014-09-13 15:13:16 -04:00
/**
2014-09-15 15:52:55 -04:00
* Adds metadata to the supplied graph , flagging it as a graph belonging to
* the BlueprintNodeTemplateCache ( so we can easily identify it later on ) .
2014-09-13 15:13:16 -04:00
*
2014-09-15 15:52:55 -04:00
* @ param NewGraph The graph to flag .
2014-09-13 15:13:16 -04:00
*/
2014-09-15 15:52:55 -04:00
static void MarkGraphForTemplateUse ( UEdGraph * NewGraph ) ;
2014-09-13 15:13:16 -04:00
/**
2014-09-15 15:52:55 -04:00
* Determines if the specified graph is one that was allocated by
* BlueprintNodeTemplateCache ( to house template nodes ) .
2014-09-13 15:13:16 -04:00
*
2014-09-15 15:52:55 -04:00
* @ param ParentGraph The graph you want checked .
* @ return True if this graph belongs to a BlueprintNodeTemplateCache , false if not .
*/
static bool IsTemplateOuter ( UEdGraph * ParentGraph ) ;
/**
* Converts the cache memory cap into bytes ( form user settings ) .
2014-09-13 15:13:16 -04:00
*
2014-09-15 15:52:55 -04:00
* @ return The user defined cache cap ( in bytes ) .
2014-09-13 15:13:16 -04:00
*/
static int32 GetCacheCapSize ( ) ;
2014-09-15 15:52:55 -04:00
/**
* Totals the size of the specified object , along with any other objects
* that have it in their outer chain . Does not account for any allocated
* memory that belongs to the object ( s ) .
*
* @ param Object The object you want an estimated byte size for .
* @ return An estimated size ( in bytes ) . . . currently does not account for any allocated memory that the object may be responsible for .
*/
static int32 ApproximateMemFootprint ( UObject const * Object ) ;
2014-08-08 15:35:56 -04:00
}
//------------------------------------------------------------------------------
static bool BlueprintNodeTemplateCacheImpl : : IsCompatible ( UEdGraphNode * NodeObj , UEdGraph * Graph )
{
2016-01-22 08:13:18 -05:00
const UEdGraphSchema * Schema = Graph - > GetSchema ( ) ;
return ensureMsgf ( Schema ! = nullptr , TEXT ( " PROTO_BP graph with invalid schema: %s " ) , * Graph - > GetName ( ) ) & &
NodeObj - > CanCreateUnderSpecifiedSchema ( Schema ) ;
2014-08-08 15:35:56 -04:00
}
//------------------------------------------------------------------------------
2014-09-15 15:52:55 -04:00
static UEdGraph * BlueprintNodeTemplateCacheImpl : : FindCompatibleGraph ( UBlueprint * BlueprintOuter , UEdGraphNode * NodeObj , bool ( * IsCompatibleFunc ) ( UEdGraph * ) )
2014-08-08 15:35:56 -04:00
{
UEdGraph * FoundGraph = nullptr ;
TArray < UObject * > BlueprintChildObjs ;
2022-02-02 02:21:12 -05:00
GetObjectsWithOuter ( BlueprintOuter , BlueprintChildObjs , /*bIncludeNestedObjects =*/ false , /*ExclusionFlags =*/ RF_NoFlags , /** InternalExcludeFlags */ EInternalObjectFlags : : Garbage ) ;
2014-08-08 15:35:56 -04:00
for ( UObject * Child : BlueprintChildObjs )
{
UEdGraph * ChildGraph = Cast < UEdGraph > ( Child ) ;
2014-09-15 15:52:55 -04:00
bool bIsCompatible = ( ChildGraph ! = nullptr ) & & IsCompatible ( NodeObj , ChildGraph ) ;
if ( bIsCompatible & & ( IsCompatibleFunc ! = nullptr ) )
{
bIsCompatible = IsCompatibleFunc ( ChildGraph ) ;
}
if ( bIsCompatible )
2014-08-08 15:35:56 -04:00
{
FoundGraph = ChildGraph ;
break ;
}
}
return FoundGraph ;
}
//------------------------------------------------------------------------------
static UBlueprint * BlueprintNodeTemplateCacheImpl : : MakeCompatibleBlueprint ( TSubclassOf < UBlueprint > BlueprintClass , UClass * ParentClass , TSubclassOf < UBlueprintGeneratedClass > GeneratedClassType )
{
EBlueprintType BlueprintType = BPTYPE_Normal ;
// @TODO: BPTYPE_LevelScript requires a level outer, which we don't want to have here... can we get away without it?
// if (BlueprintClass->IsChildOf<ULevelScriptBlueprint>())
// {
// BlueprintType = BPTYPE_LevelScript;
// }
if ( GeneratedClassType = = nullptr )
{
GeneratedClassType = UBlueprintGeneratedClass : : StaticClass ( ) ;
}
UPackage * BlueprintOuter = GetTransientPackage ( ) ;
FString const DesiredName = FString : : Printf ( TEXT ( " PROTO_BP_%s " ) , * BlueprintClass - > GetName ( ) ) ;
FName const BlueprintName = MakeUniqueObjectName ( BlueprintOuter , BlueprintClass , FName ( * DesiredName ) ) ;
BlueprintClass = FBlueprintEditorUtils : : FindFirstNativeClass ( BlueprintClass ) ;
UBlueprint * NewBlueprint = FKismetEditorUtilities : : CreateBlueprint ( ParentClass , BlueprintOuter , BlueprintName , BlueprintType , BlueprintClass , GeneratedClassType ) ;
NewBlueprint - > SetFlags ( RF_Transient ) ;
2014-09-15 19:08:11 -04:00
+ + MadeBlueprintCount ;
2014-09-15 15:52:55 -04:00
float const AproxBlueprintSize = ApproximateMemFootprint ( NewBlueprint ) ;
// track the average blueprint size, so that we can attempt to predict
// whether a blueprint will fail to be cached (when the cache is near full)
2014-09-15 19:08:11 -04:00
AverageBlueprintSize = AverageBlueprintSize * ( ( float ) ( MadeBlueprintCount - 1 ) / MadeBlueprintCount ) +
2014-09-15 15:52:55 -04:00
( AproxBlueprintSize / MadeBlueprintCount ) + 0.5f ;
2014-08-08 15:35:56 -04:00
return NewBlueprint ;
}
//------------------------------------------------------------------------------
static UEdGraph * BlueprintNodeTemplateCacheImpl : : AddGraph ( UBlueprint * BlueprintOuter , TSubclassOf < UEdGraphSchema > SchemaClass )
{
UClass * GraphClass = UEdGraph : : StaticClass ( ) ;
FName const GraphName = MakeUniqueObjectName ( BlueprintOuter , GraphClass , FName ( TEXT ( " TEMPLATE_NODE_OUTER " ) ) ) ;
2015-02-03 05:40:57 -05:00
UEdGraph * NewGraph = NewObject < UEdGraph > ( BlueprintOuter , GraphClass , GraphName , RF_Transient ) ;
2014-08-08 15:35:56 -04:00
NewGraph - > Schema = SchemaClass ;
2014-09-15 15:52:55 -04:00
MarkGraphForTemplateUse ( NewGraph ) ;
2014-08-08 15:35:56 -04:00
return NewGraph ;
}
2014-09-13 15:13:16 -04:00
//------------------------------------------------------------------------------
2014-09-15 15:52:55 -04:00
static void BlueprintNodeTemplateCacheImpl : : MarkGraphForTemplateUse ( UEdGraph * NewGraph )
{
UPackage * TemplatePackage = NewGraph - > GetOutermost ( ) ;
UMetaData * PackageMetadata = TemplatePackage - > GetMetaData ( ) ;
PackageMetadata - > SetValue ( NewGraph , TemplateGraphMetaTag , TEXT ( " true " ) ) ;
}
//------------------------------------------------------------------------------
bool BlueprintNodeTemplateCacheImpl : : IsTemplateOuter ( UEdGraph * ParentGraph )
{
2014-09-23 13:59:55 -04:00
if ( ParentGraph - > HasAnyFlags ( RF_Transactional ) )
2014-09-15 15:52:55 -04:00
{
UPackage * GraphPackage = ParentGraph - > GetOutermost ( ) ;
UMetaData * PackageMetadata = GraphPackage - > GetMetaData ( ) ;
return PackageMetadata - > HasValue ( ParentGraph , TemplateGraphMetaTag ) ;
}
return false ;
}
//------------------------------------------------------------------------------
static int32 BlueprintNodeTemplateCacheImpl : : GetCacheCapSize ( )
{
UBlueprintEditorSettings const * BpSettings = GetDefault < UBlueprintEditorSettings > ( ) ;
// have to convert from MB to bytes
return ( BpSettings - > NodeTemplateCacheCapMB * 1024.f * 1024.f ) + 0.5f ;
}
//------------------------------------------------------------------------------
static int32 BlueprintNodeTemplateCacheImpl : : ApproximateMemFootprint ( UObject const * Object )
2014-09-13 15:13:16 -04:00
{
TArray < UObject * > ChildObjs ;
2014-09-15 15:52:55 -04:00
GetObjectsWithOuter ( Object , ChildObjs , /*bIncludeNestedObjects =*/ true ) ;
2014-09-13 15:13:16 -04:00
int32 ApproimateDataSize = sizeof ( * Object ) ;
for ( UObject * ChildObj : ChildObjs )
{
// @TODO: doesn't account for any internal allocated memory (for member TArrays, etc.)
ApproimateDataSize + = sizeof ( * ChildObj ) ;
}
return ApproimateDataSize ;
}
2014-09-15 15:52:55 -04:00
/*******************************************************************************
* FBlueprintNodeTemplateCache
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-13 15:13:16 -04:00
2014-08-08 15:35:56 -04:00
//------------------------------------------------------------------------------
FBlueprintNodeTemplateCache : : FBlueprintNodeTemplateCache ( )
2014-09-15 15:52:55 -04:00
: ApproximateObjectMem ( 0 )
2014-08-08 15:35:56 -04:00
{
using namespace BlueprintNodeTemplateCacheImpl ; // for MakeCompatibleBlueprint()
2014-09-13 15:13:16 -04:00
UBlueprint * StandardBlueprint = MakeCompatibleBlueprint ( UBlueprint : : StaticClass ( ) , AActor : : StaticClass ( ) , UBlueprintGeneratedClass : : StaticClass ( ) ) ;
2014-09-15 15:52:55 -04:00
CacheBlueprintOuter ( StandardBlueprint ) ;
2014-09-13 15:13:16 -04:00
UBlueprint * AnimBlueprint = MakeCompatibleBlueprint ( UAnimBlueprint : : StaticClass ( ) , UAnimInstance : : StaticClass ( ) , UAnimBlueprintGeneratedClass : : StaticClass ( ) ) ;
2014-09-15 15:52:55 -04:00
CacheBlueprintOuter ( AnimBlueprint ) ;
2014-08-08 15:35:56 -04:00
}
//------------------------------------------------------------------------------
UEdGraphNode * FBlueprintNodeTemplateCache : : GetNodeTemplate ( UBlueprintNodeSpawner const * NodeSpawner , UEdGraph * TargetGraph )
{
using namespace BlueprintNodeTemplateCacheImpl ;
2014-09-15 15:52:55 -04:00
bool bIsOverMemCap = false ;
auto LogCacheFullMsg = [ & bIsOverMemCap ] ( )
{
if ( ! bIsOverMemCap )
{
static int32 LoggedCapSize = - 1 ;
int32 const CurrentCacheSize = GetCacheCapSize ( ) ;
// log only once for each cap size change
if ( LoggedCapSize ! = CurrentCacheSize )
{
UE_LOG ( LogBlueprintNodeCache , Display , TEXT ( " The blueprint template-node cache is full. As a result, you may experience interactions which are slower than normal. To avoid this, increase the cache's cap in the blueprint editor prefences. " ) ) ;
LoggedCapSize = CurrentCacheSize ;
}
bIsOverMemCap = true ;
}
} ;
2014-08-08 15:35:56 -04:00
UEdGraphNode * TemplateNode = nullptr ;
if ( UEdGraphNode * * FoundNode = NodeTemplateCache . Find ( NodeSpawner ) )
{
TemplateNode = * FoundNode ;
}
else if ( NodeSpawner - > NodeClass ! = nullptr )
{
UEdGraphNode * NodeCDO = NodeSpawner - > NodeClass - > GetDefaultObject < UEdGraphNode > ( ) ;
check ( NodeCDO ! = nullptr ) ;
UBlueprint * TargetBlueprint = nullptr ;
TSubclassOf < UBlueprint > BlueprintClass ;
bool const bHasTargetGraph = ( TargetGraph ! = nullptr ) ;
if ( bHasTargetGraph )
{
// by the time we're asking for a prototype for this spawner, we should
// be sure that it is compatible with the TargetGraph
2014-08-21 18:50:33 -04:00
//check(IsCompatible(NodeCDO, TargetGraph));
2014-08-08 15:35:56 -04:00
2014-08-21 18:50:33 -04:00
TargetBlueprint = FBlueprintEditorUtils : : FindBlueprintForGraph ( TargetGraph ) ;
2014-08-08 15:35:56 -04:00
check ( TargetBlueprint ! = nullptr ) ;
2014-08-21 18:50:33 -04:00
BlueprintClass = TargetBlueprint - > GetClass ( ) ;
2014-09-15 15:52:55 -04:00
// check used to help identify user interactable graphs (as opposed to
// intermediate/transient graphs).
auto IsCompatibleUserGraph = [ ] ( UEdGraph * Graph ) - > bool
{
return ! Graph - > HasAnyFlags ( RF_Transient ) ;
} ;
TargetGraph = FindCompatibleGraph ( TargetBlueprint , NodeCDO , IsCompatibleUserGraph ) ;
2014-08-21 18:50:33 -04:00
check ( TargetGraph ! = nullptr ) ;
2014-08-08 15:35:56 -04:00
}
UBlueprint * CompatibleBlueprint = nullptr ;
UEdGraph * CompatibleOuter = nullptr ;
// find a compatible outer (don't want to have to create a new one if we don't have to)
for ( UBlueprint * Blueprint : TemplateOuters )
{
CompatibleOuter = FindCompatibleGraph ( Blueprint , NodeCDO ) ;
2014-09-15 15:52:55 -04:00
if ( CompatibleOuter ! = nullptr )
{
MarkGraphForTemplateUse ( CompatibleOuter ) ;
}
2014-08-08 15:35:56 -04:00
if ( CompatibleOuter ! = nullptr )
{
CompatibleBlueprint = Blueprint ;
break ;
}
else if ( ( BlueprintClass ! = nullptr ) & & Blueprint - > GetClass ( ) - > IsChildOf ( BlueprintClass ) )
{
CompatibleBlueprint = Blueprint ;
}
}
2014-09-15 15:52:55 -04:00
// reset ActiveMemFootprint, so calls to CacheBlueprintOuter()/CacheTemplateNode()
// use the most up-to-date value (users could have since modified the
// nodes, so they could have grown in size... like with AllocateDefaultPins)
//
// @TODO: GetEstimateCacheSize() is (most likely) inaccurate, seeing as
// external systems mutate template-nodes (such as calling
// AllocateDefaultPins), and this returns a size estimate from
// when the node was first spawned (it is too slow to recalculate
// the size of the object hierarchy here)
ActiveMemFootprint = GetEstimateCacheSize ( ) ;
int32 const CacheCapSize = GetCacheCapSize ( ) ;
if ( ActiveMemFootprint > CacheCapSize )
{
LogCacheFullMsg ( ) ;
// @TODO: evict nodes until we're back under the cap (in case the cap
// was changed at runtime, or external user modified node sizes)
}
2014-08-08 15:35:56 -04:00
// if a TargetGraph was supplied, and we couldn't find a suitable outer
// for this template-node, then attempt to emulate that graph
if ( bHasTargetGraph )
{
if ( CompatibleBlueprint = = nullptr )
{
2014-09-15 15:52:55 -04:00
// if the cache is near full, attempt to predict if this
// impending cache will fail (if so, we don't want to waste the
// cycles on allocating a temp blueprint)
if ( ! bIsOverMemCap & & ( ( AverageBlueprintSize = = 0 ) | |
( ActiveMemFootprint + AverageBlueprintSize < = CacheCapSize ) ) )
2014-08-08 15:35:56 -04:00
{
2014-09-15 15:52:55 -04:00
TSubclassOf < UBlueprintGeneratedClass > GeneratedClassType = UBlueprintGeneratedClass : : StaticClass ( ) ;
if ( TargetBlueprint - > GeneratedClass ! = nullptr )
{
GeneratedClassType = TargetBlueprint - > GeneratedClass - > GetClass ( ) ;
}
2014-08-08 15:35:56 -04:00
2014-09-15 15:52:55 -04:00
CompatibleBlueprint = MakeCompatibleBlueprint ( BlueprintClass , TargetBlueprint - > ParentClass , GeneratedClassType ) ;
if ( ! CacheBlueprintOuter ( CompatibleBlueprint ) )
{
LogCacheFullMsg ( ) ;
}
// this graph may come default with a compatible graph
CompatibleOuter = FindCompatibleGraph ( CompatibleBlueprint , NodeCDO ) ;
if ( CompatibleOuter ! = nullptr )
{
MarkGraphForTemplateUse ( CompatibleOuter ) ;
}
}
else
2014-09-13 15:13:16 -04:00
{
2014-09-15 15:52:55 -04:00
CompatibleBlueprint = TargetBlueprint ;
CompatibleOuter = FindCompatibleGraph ( TargetBlueprint , NodeCDO , & BlueprintNodeTemplateCacheImpl : : IsTemplateOuter ) ;
2014-08-08 15:35:56 -04:00
2014-09-15 15:52:55 -04:00
LogCacheFullMsg ( ) ;
}
2014-08-08 15:35:56 -04:00
}
if ( CompatibleOuter = = nullptr )
{
CompatibleOuter = AddGraph ( CompatibleBlueprint , TargetGraph - > Schema ) ;
2016-01-22 08:13:18 -05:00
ensureMsgf ( CompatibleOuter - > Schema ! = nullptr , TEXT ( " Invalid schema for template graph (from '%s :: %s'). " ) ,
* TargetBlueprint - > GetName ( ) , * TargetGraph - > GetName ( ) ) ;
2014-09-15 15:52:55 -04:00
if ( CompatibleBlueprint ! = TargetBlueprint )
{
int32 const ApproxGraphSize = ApproximateMemFootprint ( CompatibleOuter ) ;
ActiveMemFootprint + = ApproxGraphSize ;
ApproximateObjectMem + = ApproxGraphSize ;
}
2014-08-08 15:35:56 -04:00
}
}
if ( CompatibleOuter ! = nullptr )
{
2014-09-10 16:14:07 -04:00
TemplateNode = NodeSpawner - > Invoke ( CompatibleOuter , IBlueprintNodeBinder : : FBindingSet ( ) , FVector2D : : ZeroVector ) ;
2014-09-15 15:52:55 -04:00
if ( ! bIsOverMemCap & & ! CacheTemplateNode ( NodeSpawner , TemplateNode ) )
2014-09-13 15:13:16 -04:00
{
2014-09-15 15:52:55 -04:00
LogCacheFullMsg ( ) ;
2014-09-13 15:13:16 -04:00
}
2014-08-08 15:35:56 -04:00
}
}
return TemplateNode ;
}
2014-09-12 12:56:24 -04:00
//------------------------------------------------------------------------------
UEdGraphNode * FBlueprintNodeTemplateCache : : GetNodeTemplate ( UBlueprintNodeSpawner const * NodeSpawner , ENoInit ) const
{
UEdGraphNode * TemplateNode = nullptr ;
if ( UEdGraphNode * const * FoundNode = NodeTemplateCache . Find ( NodeSpawner ) )
{
return * FoundNode ;
}
return nullptr ;
}
2014-08-08 15:35:56 -04:00
//------------------------------------------------------------------------------
void FBlueprintNodeTemplateCache : : ClearCachedTemplate ( UBlueprintNodeSpawner const * NodeSpawner )
{
2014-09-15 15:52:55 -04:00
NodeTemplateCache . Remove ( NodeSpawner ) ;
// GC should take care of the rest
2014-08-08 15:35:56 -04:00
}
2014-09-13 15:13:16 -04:00
//------------------------------------------------------------------------------
2014-09-15 15:52:55 -04:00
int32 FBlueprintNodeTemplateCache : : GetEstimateCacheSize ( ) const
2014-09-13 15:13:16 -04:00
{
2014-09-15 15:52:55 -04:00
int32 TotalEstimatedSize = ApproximateObjectMem ;
2014-09-13 15:13:16 -04:00
TotalEstimatedSize + = TemplateOuters . GetAllocatedSize ( ) ;
2014-09-15 15:52:55 -04:00
TotalEstimatedSize + = NodeTemplateCache . GetAllocatedSize ( ) ;
TotalEstimatedSize + = sizeof ( * this ) ;
2014-09-13 15:13:16 -04:00
return TotalEstimatedSize ;
}
2014-09-15 15:52:55 -04:00
//------------------------------------------------------------------------------
int32 FBlueprintNodeTemplateCache : : RecalculateCacheSize ( )
{
ApproximateObjectMem = 0 ;
for ( UBlueprint * Blueprint : TemplateOuters )
{
// if we didn't run garbage collection at the top, then this could also
// account for nodes that were never stored (because the cache was too full)
ApproximateObjectMem + = BlueprintNodeTemplateCacheImpl : : ApproximateMemFootprint ( Blueprint ) ;
}
return ApproximateObjectMem ;
}
//------------------------------------------------------------------------------
bool FBlueprintNodeTemplateCache : : IsTemplateOuter ( UEdGraph * ParentGraph )
{
return BlueprintNodeTemplateCacheImpl : : IsTemplateOuter ( ParentGraph ) ;
}
2014-08-08 15:35:56 -04:00
//------------------------------------------------------------------------------
void FBlueprintNodeTemplateCache : : AddReferencedObjects ( FReferenceCollector & Collector )
{
for ( auto & TemplateEntry : NodeTemplateCache )
{
Collector . AddReferencedObject ( TemplateEntry . Value ) ;
}
Collector . AddReferencedObjects ( TemplateOuters ) ;
}
2014-09-13 15:13:16 -04:00
2019-03-25 14:40:23 -04:00
FString FBlueprintNodeTemplateCache : : GetReferencerName ( ) const
{
return TEXT ( " FBlueprintNodeTemplateCache " ) ;
}
2014-09-13 15:13:16 -04:00
//------------------------------------------------------------------------------
2014-09-15 15:52:55 -04:00
bool FBlueprintNodeTemplateCache : : CacheBlueprintOuter ( UBlueprint * Blueprint )
2014-09-13 15:13:16 -04:00
{
2014-09-15 15:52:55 -04:00
using namespace BlueprintNodeTemplateCacheImpl ;
int32 const ApproxBlueprintSize = ApproximateMemFootprint ( Blueprint ) ;
2014-09-13 15:13:16 -04:00
2014-09-15 15:52:55 -04:00
if ( ActiveMemFootprint + ApproxBlueprintSize > GetCacheCapSize ( ) )
2014-09-13 15:13:16 -04:00
{
return false ;
}
else
{
2014-09-15 15:52:55 -04:00
ApproximateObjectMem + = ApproxBlueprintSize ;
2014-09-13 15:13:16 -04:00
TemplateOuters . Add ( Blueprint ) ;
return true ;
}
}
//------------------------------------------------------------------------------
bool FBlueprintNodeTemplateCache : : CacheTemplateNode ( UBlueprintNodeSpawner const * NodeSpawner , UEdGraphNode * NewNode )
{
2014-09-15 15:52:55 -04:00
using namespace BlueprintNodeTemplateCacheImpl ;
2014-09-13 15:13:16 -04:00
if ( NewNode = = nullptr )
{
return true ;
}
int32 const ApproxNodeSize = BlueprintNodeTemplateCacheImpl : : ApproximateMemFootprint ( NewNode ) ;
2014-09-15 15:52:55 -04:00
if ( ActiveMemFootprint + ApproxNodeSize > GetCacheCapSize ( ) )
2014-09-13 15:13:16 -04:00
{
return false ;
}
else
{
2014-09-15 15:52:55 -04:00
ApproximateObjectMem + = ApproxNodeSize ;
2014-09-13 15:13:16 -04:00
NodeTemplateCache . Add ( NodeSpawner , NewNode ) ;
return true ;
}
}