EM_Task/CoreUObject/Public/UObject/UObjectBaseUtility.h
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

932 lines
30 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
UObjectBaseUtility.h: Unreal UObject functions that only depend on UObjectBase
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "Stats/Stats.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectBase.h"
#include "UObject/UObjectArray.h"
#include "UObject/UObjectMarks.h"
#if defined(_MSC_VER) && _MSC_VER == 1900
#ifdef PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS
PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS
#endif
#endif
/**
* Enum which specifies the mode in which full object names are constructed
*/
enum class EObjectFullNameFlags
{
// Standard object full name (i.e. "Type PackageName.ObjectName:SubobjectName")
None = 0,
// Adds package to the type portion (i.e. "TypePackage.TypeName PackageName.ObjectName:SubobjectName")
IncludeClassPackage = 1,
};
ENUM_CLASS_FLAGS(EObjectFullNameFlags);
/**
* Provides utility functions for UObject, this class should not be used directly
*/
class COREUOBJECT_API UObjectBaseUtility: public UObjectBase
{
public:
// Constructors.
UObjectBaseUtility() {}
UObjectBaseUtility(EObjectFlags InFlags)
: UObjectBase(InFlags)
{
}
/*-------------------
Flags
-------------------*/
/** Modifies object flags for a specific object */
FORCEINLINE void SetFlags(EObjectFlags NewFlags)
{
checkSlow(!(NewFlags & (RF_MarkAsNative | RF_MarkAsRootSet))); // These flags can't be used outside of constructors / internal code
SetFlagsTo(GetFlags() | NewFlags);
}
/** Clears subset of flags for a specific object */
FORCEINLINE void ClearFlags(EObjectFlags NewFlags)
{
checkSlow(!(NewFlags & (RF_MarkAsNative | RF_MarkAsRootSet)) || NewFlags == RF_AllFlags); // These flags can't be used outside of constructors / internal code
SetFlagsTo(GetFlags() & ~NewFlags);
}
/**
* Used to safely check whether any of the passed in flags are set.
*
* @param FlagsToCheck Object flags to check for.
* @return true if any of the passed in flags are set, false otherwise (including no flags passed in).
*/
FORCEINLINE bool HasAnyFlags(EObjectFlags FlagsToCheck) const
{
checkSlow(!(FlagsToCheck & (RF_MarkAsNative | RF_MarkAsRootSet)) || FlagsToCheck == RF_AllFlags); // These flags can't be used outside of constructors / internal code
return (GetFlags() & FlagsToCheck) != 0;
}
/**
* Used to safely check whether all of the passed in flags are set.
*
* @param FlagsToCheck Object flags to check for
* @return true if all of the passed in flags are set (including no flags passed in), false otherwise
*/
FORCEINLINE bool HasAllFlags(EObjectFlags FlagsToCheck) const
{
checkSlow(!(FlagsToCheck & (RF_MarkAsNative | RF_MarkAsRootSet)) || FlagsToCheck == RF_AllFlags); // These flags can't be used outside of constructors / internal code
return ((GetFlags() & FlagsToCheck) == FlagsToCheck);
}
/**
* Returns object flags that are both in the mask and set on the object.
*
* @param Mask Mask to mask object flags with
* @param Objects flags that are set in both the object and the mask
*/
FORCEINLINE EObjectFlags GetMaskedFlags(EObjectFlags Mask = RF_AllFlags) const
{
return EObjectFlags(GetFlags() & Mask);
}
/*----------------------------------------------------
Marks, implemented in UObjectMarks.cpp
----------------------------------------------------*/
/**
* Adds marks to an object
*
* @param Marks Logical OR of OBJECTMARK_'s to apply
*/
FORCEINLINE void Mark(EObjectMark Marks) const
{
MarkObject(this, Marks);
}
/**
* Removes marks from and object
*
* @param Marks Logical OR of OBJECTMARK_'s to remove
*/
FORCEINLINE void UnMark(EObjectMark Marks) const
{
UnMarkObject(this, Marks);
}
/**
* Tests an object for having ANY of a set of marks
*
* @param Marks Logical OR of OBJECTMARK_'s to test
* @return true if the object has any of the given marks.
*/
FORCEINLINE bool HasAnyMarks(EObjectMark Marks) const
{
return ObjectHasAnyMarks(this, Marks);
}
/**
* Tests an object for having ALL of a set of marks
*
* @param Marks Logical OR of OBJECTMARK_'s to test
* @return true if the object has any of the given marks.
*/
FORCEINLINE bool HasAllMarks(EObjectMark Marks) const
{
return ObjectHasAllMarks(this, Marks);
}
/**
* Returns all of the object marks on a specific object
*
* @param Object Object to get marks for
* @return all Marks for an object
*/
FORCEINLINE EObjectMark GetAllMarks() const
{
return ObjectGetAllMarks(this);
}
/**
* Checks the PendingKill flag to see if it is dead but memory still valid
*/
FORCEINLINE bool IsPendingKill() const
{
return GUObjectArray.IndexToObject(InternalIndex)->IsPendingKill();
}
/**
* Marks this object as RF_PendingKill.
*/
FORCEINLINE void MarkPendingKill()
{
check(!IsRooted());
GUObjectArray.IndexToObject(InternalIndex)->SetPendingKill();
}
/**
* Unmarks this object as PendingKill.
*/
FORCEINLINE void ClearPendingKill()
{
GUObjectArray.IndexToObject(InternalIndex)->ClearPendingKill();
}
/**
* Add an object to the root set. This prevents the object and all
* its descendants from being deleted during garbage collection.
*/
FORCEINLINE void AddToRoot()
{
GUObjectArray.IndexToObject(InternalIndex)->SetRootSet();
}
/** Remove an object from the root set. */
FORCEINLINE void RemoveFromRoot()
{
GUObjectArray.IndexToObject(InternalIndex)->ClearRootSet();
}
/**
* Returns true if this object is explicitly rooted
*
* @return true if the object was explicitly added as part of the root set.
*/
FORCEINLINE bool IsRooted() const
{
return GUObjectArray.IndexToObject(InternalIndex)->IsRootSet();
}
/**
* Atomically clear the unreachable flag
*
* @return true if we are the thread that cleared RF_Unreachable
*/
FORCEINLINE bool ThisThreadAtomicallyClearedRFUnreachable()
{
return GUObjectArray.IndexToObject(InternalIndex)->ThisThreadAtomicallyClearedRFUnreachable();
}
/** Checks if the object is unreachable. */
FORCEINLINE bool IsUnreachable() const
{
return GUObjectArray.IndexToObject(InternalIndex)->IsUnreachable();
}
/** Checks if the object is pending kill or unreachable. */
FORCEINLINE bool IsPendingKillOrUnreachable() const
{
return GUObjectArray.IndexToObject(InternalIndex)->HasAnyFlags(EInternalObjectFlags::PendingKill | EInternalObjectFlags::Unreachable);
}
/** Checks if the object is native. */
FORCEINLINE bool IsNative() const
{
return GUObjectArray.IndexToObject(InternalIndex)->HasAnyFlags(EInternalObjectFlags::Native);
}
/**
* Clears passed in internal flags.
*
* @param FlagsToClear Object flags to clear.
* @return true if any of the passed in flags are set, false otherwise (including no flags passed in).
*/
FORCEINLINE void SetInternalFlags(EInternalObjectFlags FlagsToSet) const
{
GUObjectArray.IndexToObject(InternalIndex)->SetFlags(FlagsToSet);
}
/**
* Gets internal flags.
*
* @param FlagsToClear Object flags to clear.
* @return true if any of the passed in flags are set, false otherwise (including no flags passed in).
*/
FORCEINLINE EInternalObjectFlags GetInternalFlags() const
{
return GUObjectArray.IndexToObject(InternalIndex)->GetFlags();
}
/**
* Used to safely check whether any of the passed in internal flags are set.
*
* @param FlagsToCheck Object flags to check for.
* @return true if any of the passed in flags are set, false otherwise (including no flags passed in).
*/
FORCEINLINE bool HasAnyInternalFlags(EInternalObjectFlags FlagsToCheck) const
{
return GUObjectArray.IndexToObject(InternalIndex)->HasAnyFlags(FlagsToCheck);
}
/**
* Clears passed in internal flags.
*
* @param FlagsToClear Object flags to clear.
* @return true if any of the passed in flags are set, false otherwise (including no flags passed in).
*/
FORCEINLINE void ClearInternalFlags(EInternalObjectFlags FlagsToClear) const
{
GUObjectArray.IndexToObject(InternalIndex)->ClearFlags(FlagsToClear);
}
/**
* Atomically clears passed in internal flags.
*
* @param FlagsToClear Object flags to clear.
* @return true if any of the passed in flags are set, false otherwise (including no flags passed in).
*/
FORCEINLINE bool AtomicallyClearInternalFlags(EInternalObjectFlags FlagsToClear) const
{
return GUObjectArray.IndexToObject(InternalIndex)->ThisThreadAtomicallyClearedFlag(FlagsToClear);
}
/*-------------------
Names
-------------------*/
/**
* Returns the fully qualified pathname for this object as well as the name of the class, in the format:
* 'ClassName Outermost[.Outer].Name'.
*
* @param StopOuter if specified, indicates that the output string should be relative to this object. if StopOuter
* does not exist in this object's Outer chain, the result would be the same as passing NULL.
* @param Flags flags that control the behavior of full name generation
*
* @note safe to call on NULL object pointers!
*/
FString GetFullName(const UObject* StopOuter = NULL, EObjectFullNameFlags Flags = EObjectFullNameFlags::None) const;
/**
* Version of GetFullName() that eliminates unnecessary copies.
*/
void GetFullName(const UObject* StopOuter, FString& ResultString, EObjectFullNameFlags Flags = EObjectFullNameFlags::None) const;
/**
* Returns the fully qualified pathname for this object, in the format:
* 'Outermost[.Outer].Name'
*
* @param StopOuter if specified, indicates that the output string should be relative to this object. if StopOuter
* does not exist in this object's Outer chain, the result would be the same as passing NULL.
*
* @note safe to call on NULL object pointers!
*/
FString GetPathName(const UObject* StopOuter = NULL) const;
/**
* Versions of GetPathName() that eliminates unnecessary copies and allocations.
*/
void GetPathName(const UObject* StopOuter, FString& ResultString) const;
void GetPathName(const UObject* StopOuter, FStringBuilderBase& ResultString) const;
public:
/**
* Called after load to determine if the object can be a cluster root
*
* @return true if this object can be a cluster root
*/
virtual bool CanBeClusterRoot() const
{
return false;
}
/**
* Called during cluster construction if the object can be added to a cluster
*
* @return true if this object can be inside of a cluster
*/
virtual bool CanBeInCluster() const;
/**
* Called after PostLoad to create UObject cluster
*/
virtual void CreateCluster();
/**
* Called during Garbage Collection to perform additional cleanup when the cluster is about to be destroyed due to PendingKill flag being set on it.
*/
virtual void OnClusterMarkedAsPendingKill() {}
/**
* Adds this objects to a GC cluster that already exists
* @param ClusterRootOrObjectFromCluster Object that belongs to the cluster we want to add this object to.
* @param Add this object to the target cluster as a mutable object without adding this object's references.
*/
void AddToCluster(UObjectBaseUtility* ClusterRootOrObjectFromCluster, bool bAddAsMutableObject = false);
protected:
/** Helper function to create a cluster from UObject */
static void CreateClusterFromObject(UObjectBaseUtility* ClusterRootObject, UObjectBaseUtility* ReferencingObject);
public:
/**
* Walks up the chain of packages until it reaches the top level, which it ignores.
*
* @param bStartWithOuter whether to include this object's name in the returned string
* @return string containing the path name for this object, minus the outermost-package's name
*/
FString GetFullGroupName(bool bStartWithOuter) const;
/**
* Returns the name of this object (with no path information)
*
* @return Name of the object.
*/
FORCEINLINE FString GetName() const
{
return GetFName().ToString();
}
/** Optimized version of GetName that overwrites an existing string */
FORCEINLINE void GetName(FString& ResultString) const
{
GetFName().ToString(ResultString);
}
/** Optimized version of GetName that appends to an existing string */
FORCEINLINE void AppendName(FString& ResultString) const
{
GetFName().AppendString(ResultString);
}
/*-------------------
Outer & Package
-------------------*/
/**
* Utility function to temporarily detach the object external package, if any
* GetPackage will report the outer's package once detached
*/
void DetachExternalPackage();
/**
* Utility function to reattach the object external package, if any
* GetPackage will report the object external package if set after this call
*/
void ReattachExternalPackage();
/**
* Walks up the list of outers until it finds the top-level one that isn't a package.
* Will return null if called on a package
* @return outermost non package Outer.
*/
UObject* GetOutermostObject() const;
/**
* Walks up the list of outers until it finds a package directly associated with the object.
*
* @return the package the object is in.
*/
UPackage* GetPackage() const;
/**
* Legacy function, has the same behavior as GetPackage
* use GetPackage instead.
* @return the package the object is in.
* @see GetPackage
*/
UPackage* GetOutermost() const;
/**
* Finds the outermost package and marks it dirty.
* The editor suppresses this behavior during load as it is against policy to dirty packages simply by loading them.
*
* @return false if the request to mark the package dirty was suppressed by the editor and true otherwise.
*/
bool MarkPackageDirty() const;
/**
* Determines whether this object is a template object
*
* @return true if this object is a template object (owned by a UClass)
*/
bool IsTemplate(EObjectFlags TemplateTypes = RF_ArchetypeObject | RF_ClassDefaultObject) const;
/**
* Traverses the outer chain searching for the next object of a certain type. (T must be derived from UObject)
*
* @param Target class to search for
* @return a pointer to the first object in this object's Outer chain which is of the correct type.
*/
UObject* GetTypedOuter(UClass* Target) const;
/**
* Traverses the outer chain searching for the next object of a certain type. (T must be derived from UObject)
*
* @return a pointer to the first object in this object's Outer chain which is of the correct type.
*/
template <typename T>
T* GetTypedOuter() const
{
return (T*)GetTypedOuter(T::StaticClass());
}
/**
* Return the dispatch to `IsInOuter` or `IsInPackage` depending on SomeOuter's class.
* Legacy function, preferably use IsInOuter or IsInPackage depending on use case.
*/
bool IsIn(const UObject* SomeOuter) const;
/**
* Overload to determine if an object is in the specified package which can now be different than its outer chain.
* Calls IsInPackage.
*/
bool IsIn(const UPackage* SomePackage) const;
/** Returns true if the object is contained in the specified outer. */
bool IsInOuter(const UObject* SomeOuter) const;
/** Returns true if the object is contained in the specified package. */
bool IsInPackage(const UPackage* SomePackage) const;
/**
* Find out if this object is inside (has an outer) that is of the specified class
* @param SomeBaseClass The base class to compare against
* @return True if this object is in an object of the given type.
*/
bool IsInA(const UClass* SomeBaseClass) const;
/**
* Checks whether this object's top-most package has any of the specified flags
*
* @param CheckFlagMask a bitmask of EPackageFlags values to check for
*
* @return true if the PackageFlags member of this object's top-package has any bits from the mask set.
*/
bool RootPackageHasAnyFlags(uint32 CheckFlagMask) const;
/*-------------------
Class
-------------------*/
private:
template <typename ClassType>
static FORCEINLINE bool IsChildOfWorkaround(const ClassType* ObjClass, const ClassType* TestCls)
{
return ObjClass->IsChildOf(TestCls);
}
public:
/** Returns true if this object is of the specified type. */
template <typename OtherClassType>
FORCEINLINE bool IsA(OtherClassType SomeBase) const
{
// We have a cyclic dependency between UObjectBaseUtility and UClass,
// so we use a template to allow inlining of something we haven't yet seen, because it delays compilation until the function is called.
// 'static_assert' that this thing is actually a UClass pointer or convertible to it.
const UClass* SomeBaseClass = SomeBase;
(void)SomeBaseClass;
checkfSlow(SomeBaseClass, TEXT("IsA(NULL) cannot yield meaningful results"));
const UClass* ThisClass = GetClass();
// Stop the compiler doing some unnecessary branching for nullptr checks
UE_ASSUME(SomeBaseClass);
UE_ASSUME(ThisClass);
return IsChildOfWorkaround(ThisClass, SomeBaseClass);
}
/** Returns true if this object is of the template type. */
template <class T>
bool IsA() const
{
return IsA(T::StaticClass());
}
/**
* Finds the most-derived class which is a parent of both TestClass and this object's class.
*
* @param TestClass the class to find the common base for
*/
const UClass* FindNearestCommonBaseClass(const UClass* TestClass) const;
/**
* Returns a pointer to this object safely converted to a pointer of the specified interface class.
*
* @param InterfaceClass the interface class to use for the returned type
*
* @return a pointer that can be assigned to a variable of the interface type specified, or NULL if this object's
* class doesn't implement the interface indicated. Will be the same value as 'this' if the interface class
* isn't native.
*/
void* GetInterfaceAddress(UClass* InterfaceClass);
/**
* Returns a pointer to the I* native interface object that this object implements.
* Returns NULL if this object does not implement InterfaceClass, or does not do so natively.
*/
void* GetNativeInterfaceAddress(UClass* InterfaceClass);
/**
* Returns a pointer to the const I* native interface object that this object implements.
* Returns NULL if this object does not implement InterfaceClass, or does not do so natively.
*/
const void* GetNativeInterfaceAddress(UClass* InterfaceClass) const
{
return const_cast<UObjectBaseUtility*>(this)->GetNativeInterfaceAddress(InterfaceClass);
}
/**
* Returns whether this component was instanced from a component/subobject template, or if it is a component/subobject template.
* This is based on a name comparison with the outer class instance lookup table
*
* @return true if this component was instanced from a template. false if this component was created manually at runtime.
*/
bool IsDefaultSubobject() const;
/*--------------------------------------------------
Linker, defined in UObjectLinker.cpp
--------------------------------------------------*/
/**
* Returns the linker for this object.
*
* @return a pointer to the linker for this object, or NULL if this object has no linker
*/
class FLinkerLoad* GetLinker() const;
/**
* Returns this object's LinkerIndex.
*
* @return the index into my linker's ExportMap for the FObjectExport
* corresponding to this object.
*/
int32 GetLinkerIndex() const;
/**
* Returns the UE4 version of the linker for this object.
*
* @return the UE4 version of the engine's package file when this object
* was last saved, or GPackageFileUE4Version (current version) if
* this object does not have a linker, which indicates that
* a) this object is a native only class, or
* b) this object's linker has been detached, in which case it is already fully loaded
*/
int32 GetLinkerUE4Version() const;
/**
* Returns the licensee version of the linker for this object.
*
* @return the licensee version of the engine's package file when this object
* was last saved, or GPackageFileLicenseeVersion (current version) if
* this object does not have a linker, which indicates that
* a) this object is a native only class, or
* b) this object's linker has been detached, in which case it is already fully loaded
*/
int32 GetLinkerLicenseeUE4Version() const;
/**
* Returns the custom version of the linker for this object corresponding to the given custom version key.
*
* @return the custom version of the engine's package file when this object
* was last saved, or the current version if
* this object does not have a linker, which indicates that
* a) this object is a native only class, or
* b) this object's linker has been detached, in which case it is already fully loaded
*/
int32 GetLinkerCustomVersion(FGuid CustomVersionKey) const;
/**
* Overloaded < operator. Compares objects by name.
*
* @return true if this object's name is lexicographically smaller than the other object's name
*/
FORCEINLINE bool operator<(const UObjectBaseUtility& Other) const
{
return GetName() < Other.GetName();
}
/*******
* Stats
*******/
#if STATS || ENABLE_STATNAMEDEVENTS_UOBJECT
FORCEINLINE void ResetStatID()
{
GUObjectArray.IndexToObject(InternalIndex)->StatID = TStatId();
#if ENABLE_STATNAMEDEVENTS_UOBJECT
GUObjectArray.IndexToObject(InternalIndex)->StatIDStringStorage = nullptr;
#endif
}
#endif
/**
* Returns the stat ID of the object, used for profiling. This will create a stat ID if needed.
*
* @param bForDeferred If true, a stat ID will be created even if a group is disabled
*/
FORCEINLINE TStatId GetStatID(bool bForDeferredUse = false) const
{
#if STATS
const TStatId& StatID = GUObjectArray.IndexToObject(InternalIndex)->StatID;
// this is done to avoid even registering stats for a disabled group (unless we plan on using it later)
if (bForDeferredUse || FThreadStats::IsCollectingData(GET_STATID(STAT_UObjectsStatGroupTester)))
{
if (!StatID.IsValidStat())
{
CreateStatID();
}
return StatID;
}
#elif ENABLE_STATNAMEDEVENTS_UOBJECT
const TStatId& StatID = GUObjectArray.IndexToObject(InternalIndex)->StatID;
if (!StatID.IsValidStat() && (bForDeferredUse || GCycleStatsShouldEmitNamedEvents))
{
CreateStatID();
}
return StatID;
#endif // STATS
return TStatId(); // not doing stats at the moment, or ever
}
private:
#if STATS || ENABLE_STATNAMEDEVENTS_UOBJECT
/** Creates a stat ID for this object */
void CreateStatID() const
{
GUObjectArray.IndexToObject(InternalIndex)->CreateStatID();
}
#endif
};
/** Returns false if this pointer cannot be a valid pointer to a UObject */
FORCEINLINE bool IsPossiblyAllocatedUObjectPointer(UObject* Ptr)
{
auto CountByteValues = [](UPTRINT Val, UPTRINT ByteVal) -> int32
{
int32 Result = 0;
for (int32 I = 0; I != sizeof(UPTRINT); ++I)
{
if ((Val & 0xFF) == ByteVal)
{
++Result;
}
Val >>= 8;
}
return Result;
};
UPTRINT PtrVal = (UPTRINT)Ptr;
return PtrVal >= 0x1000 && CountByteValues(PtrVal, 0xCD) < sizeof(UPTRINT) / 2;
}
/**
* Returns the name of this object (with no path information)
* @param Object object to retrieve the name for; NULL gives "None"
* @return Name of the object.
*/
FORCEINLINE FString GetNameSafe(const UObjectBaseUtility* Object)
{
if (Object == NULL)
{
return TEXT("None");
}
else
{
return Object->GetName();
}
}
/**
* Returns the path name of this object
* @param Object object to retrieve the path name for; NULL gives "None"
* @return path name of the object.
*/
FORCEINLINE FString GetPathNameSafe(const UObjectBaseUtility* Object)
{
if (Object == NULL)
{
return TEXT("None");
}
else
{
return Object->GetPathName();
}
}
/**
* Returns the full name of this object
* @param Object object to retrieve the full name for; NULL (or a null class!) gives "None"
* @return full name of the object.
*/
FORCEINLINE FString GetFullNameSafe(const UObjectBaseUtility* Object)
{
if (!Object || !Object->GetClass())
{
return TEXT("None");
}
else
{
return Object->GetFullName();
}
}
/**
* Returns the native (C++) parent class of the supplied class
* If supplied class is native, it will be returned.
*/
COREUOBJECT_API UClass* GetParentNativeClass(UClass* Class);
#if !defined(USE_LIGHTWEIGHT_UOBJECT_STATS_FOR_HITCH_DETECTION)
#define USE_LIGHTWEIGHT_UOBJECT_STATS_FOR_HITCH_DETECTION (1)
#endif
#if STATS
/** Structure used to track time spent by a UObject */
class FScopeCycleCounterUObject: public FCycleCounter
{
public:
#if USE_MALLOC_PROFILER
/** Package path being tracked */
FName PackageTag;
/** Class path being tracked */
FName ClassTag;
/** Object path being tracked */
FName ObjectTag;
#endif
/**
* Constructor, starts timing
*/
FORCEINLINE_STATS FScopeCycleCounterUObject(const UObjectBaseUtility* Object)
{
if (Object)
{
TStatId ObjectStatId = Object->GetStatID();
if (FThreadStats::IsCollectingData(ObjectStatId))
{
Start(ObjectStatId);
}
}
#if USE_MALLOC_PROFILER
if (Object)
{
TrackObjectForMallocProfiling(Object);
}
#endif
}
/**
* Constructor, starts timing with an alternate enable stat to use high performance disable for only SOME UObject stats
*/
FORCEINLINE_STATS FScopeCycleCounterUObject(const UObjectBaseUtility* Object, TStatId OtherStat)
{
if (FThreadStats::IsCollectingData(OtherStat) && Object)
{
TStatId ObjectStatId = Object->GetStatID();
if (!ObjectStatId.IsNone())
{
Start(ObjectStatId);
}
}
#if USE_MALLOC_PROFILER
if (Object)
{
TrackObjectForMallocProfiling(Object);
}
#endif
}
/**
* Updates the stat with the time spent
*/
FORCEINLINE_STATS ~FScopeCycleCounterUObject()
{
Stop();
#if USE_MALLOC_PROFILER
UntrackObjectForMallocProfiling();
#endif
}
#if USE_MALLOC_PROFILER
COREUOBJECT_API void TrackObjectForMallocProfiling(const UObjectBaseUtility* InObject);
COREUOBJECT_API void TrackObjectForMallocProfiling(const FName InPackageName, const FName InClassName, const FName InObjectName);
COREUOBJECT_API void UntrackObjectForMallocProfiling();
#endif
};
/** Declares a scope cycle counter for a specific object with a Name context */
#define SCOPE_CYCLE_UOBJECT(Name, Object) \
FScopeCycleCounterUObject ObjCycleCount_##Name(Object);
#elif ENABLE_STATNAMEDEVENTS
class FScopeCycleCounterUObject
{
public:
FScopeCycleCounter ScopeCycleCounter;
FORCEINLINE_STATS FScopeCycleCounterUObject(const UObjectBaseUtility* Object)
: ScopeCycleCounter(Object ? Object -> GetStatID().StatString : nullptr)
{
}
FORCEINLINE_STATS FScopeCycleCounterUObject(const UObjectBaseUtility* Object, TStatId OtherStat)
: ScopeCycleCounter(Object ? Object -> GetStatID().StatString : nullptr)
{
}
};
/** Declares a scope cycle counter for a specific object with a Name context */
#define SCOPE_CYCLE_UOBJECT(Name, Object) \
FScopeCycleCounterUObject ObjCycleCount_##Name(Object);
#elif USE_LIGHTWEIGHT_STATS_FOR_HITCH_DETECTION && USE_HITCH_DETECTION && USE_LIGHTWEIGHT_UOBJECT_STATS_FOR_HITCH_DETECTION
extern CORE_API bool GHitchDetected;
class FScopeCycleCounterUObject
{
const UObject* StatObject;
public:
FORCEINLINE FScopeCycleCounterUObject(const UObject* InStatObject, TStatId OtherStat = TStatId())
{
StatObject = GHitchDetected ? nullptr : InStatObject;
}
FORCEINLINE ~FScopeCycleCounterUObject()
{
if (GHitchDetected && StatObject)
{
ReportHitch();
}
}
COREUOBJECT_API void ReportHitch();
};
/** Declares a scope cycle counter for a specific object with a Name context */
#define SCOPE_CYCLE_UOBJECT(Name, Object) \
FScopeCycleCounterUObject ObjCycleCount_##Name(Object);
#else
class FScopeCycleCounterUObject
{
public:
FORCEINLINE_STATS FScopeCycleCounterUObject(const UObjectBaseUtility* Object)
{
}
FORCEINLINE_STATS FScopeCycleCounterUObject(const UObjectBaseUtility* Object, TStatId OtherStat)
{
}
};
#define SCOPE_CYCLE_UOBJECT(Name, Object)
#endif
#if defined(_MSC_VER) && _MSC_VER == 1900
#ifdef PRAGMA_ENABLE_SHADOW_VARIABLE_WARNINGS
PRAGMA_ENABLE_SHADOW_VARIABLE_WARNINGS
#endif
#endif