You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
* Fixed non-unity compile errors when building non editor config #preflight skipped #rb none [CL 23592538 by henrik karlsson in ue5-main branch]
532 lines
13 KiB
C++
532 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ObjectTrace.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(ObjectTrace)
|
|
|
|
#if OBJECT_TRACE_ENABLED
|
|
|
|
#include "SceneView.h"
|
|
#if WITH_EDITOR
|
|
#include "Editor.h"
|
|
#else
|
|
#include "UObject/Package.h"
|
|
#include "UObject/UObjectAnnotation.h"
|
|
#endif
|
|
|
|
UE_TRACE_CHANNEL(ObjectChannel)
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, Class)
|
|
UE_TRACE_EVENT_FIELD(uint64, Id)
|
|
UE_TRACE_EVENT_FIELD(uint64, SuperId)
|
|
UE_TRACE_EVENT_FIELD(UE::Trace::WideString, Name)
|
|
UE_TRACE_EVENT_FIELD(UE::Trace::WideString, Path)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, Object)
|
|
UE_TRACE_EVENT_FIELD(uint64, Id)
|
|
UE_TRACE_EVENT_FIELD(uint64, ClassId)
|
|
UE_TRACE_EVENT_FIELD(uint64, OuterId)
|
|
UE_TRACE_EVENT_FIELD(UE::Trace::WideString, Name)
|
|
UE_TRACE_EVENT_FIELD(UE::Trace::WideString, Path)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, ObjectLifetimeBegin2)
|
|
UE_TRACE_EVENT_FIELD(uint64, Cycle)
|
|
UE_TRACE_EVENT_FIELD(double, RecordingTime)
|
|
UE_TRACE_EVENT_FIELD(uint64, Id)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, ObjectLifetimeEnd2)
|
|
UE_TRACE_EVENT_FIELD(uint64, Cycle)
|
|
UE_TRACE_EVENT_FIELD(double, RecordingTime)
|
|
UE_TRACE_EVENT_FIELD(uint64, Id)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, ObjectEvent)
|
|
UE_TRACE_EVENT_FIELD(uint64, Cycle)
|
|
UE_TRACE_EVENT_FIELD(uint64, Id)
|
|
UE_TRACE_EVENT_FIELD(UE::Trace::WideString, Event)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, PawnPossess)
|
|
UE_TRACE_EVENT_FIELD(uint64, Cycle)
|
|
UE_TRACE_EVENT_FIELD(uint64, ControllerId)
|
|
UE_TRACE_EVENT_FIELD(uint64, PawnId)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, World)
|
|
UE_TRACE_EVENT_FIELD(uint64, Id)
|
|
UE_TRACE_EVENT_FIELD(int32, PIEInstanceId)
|
|
UE_TRACE_EVENT_FIELD(uint8, Type)
|
|
UE_TRACE_EVENT_FIELD(uint8, NetMode)
|
|
UE_TRACE_EVENT_FIELD(bool, IsSimulating)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, RecordingInfo)
|
|
UE_TRACE_EVENT_FIELD(uint64, WorldId)
|
|
UE_TRACE_EVENT_FIELD(uint64, Cycle)
|
|
UE_TRACE_EVENT_FIELD(uint32, RecordingIndex)
|
|
UE_TRACE_EVENT_FIELD(uint32, FrameIndex)
|
|
UE_TRACE_EVENT_FIELD(double, ElapsedTime)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
UE_TRACE_EVENT_BEGIN(Object, View)
|
|
UE_TRACE_EVENT_FIELD(uint64, PlayerId)
|
|
UE_TRACE_EVENT_FIELD(uint64, Cycle)
|
|
|
|
UE_TRACE_EVENT_FIELD(double, PosX)
|
|
UE_TRACE_EVENT_FIELD(double, PosY)
|
|
UE_TRACE_EVENT_FIELD(double, PosZ)
|
|
|
|
UE_TRACE_EVENT_FIELD(float, Pitch)
|
|
UE_TRACE_EVENT_FIELD(float, Yaw)
|
|
UE_TRACE_EVENT_FIELD(float, Roll)
|
|
|
|
UE_TRACE_EVENT_FIELD(float, Fov)
|
|
UE_TRACE_EVENT_FIELD(float, AspectRatio)
|
|
UE_TRACE_EVENT_END()
|
|
|
|
// Object annotations used for tracing
|
|
struct FTracedObjectAnnotation
|
|
{
|
|
FTracedObjectAnnotation()
|
|
: bTraced(false)
|
|
{}
|
|
|
|
// Whether this object has been traced this session
|
|
bool bTraced;
|
|
|
|
/** Determine if this annotation is default - required for annotations */
|
|
FORCEINLINE bool IsDefault() const
|
|
{
|
|
return bTraced == false;
|
|
}
|
|
};
|
|
|
|
struct FObjectIdAnnotation
|
|
{
|
|
FObjectIdAnnotation()
|
|
: Id(0)
|
|
{}
|
|
|
|
// Object ID
|
|
uint64 Id;
|
|
|
|
/** Determine if this annotation is default - required for annotations */
|
|
FORCEINLINE bool IsDefault() const
|
|
{
|
|
return Id == 0;
|
|
}
|
|
|
|
bool operator == (const FObjectIdAnnotation& other) const
|
|
{
|
|
return Id == other.Id;
|
|
}
|
|
};
|
|
|
|
int32 GetTypeHash(const FObjectIdAnnotation& Annotation)
|
|
{
|
|
return GetTypeHash(Annotation.Id);
|
|
}
|
|
|
|
// Object annotations used for tracing
|
|
FUObjectAnnotationSparse<FTracedObjectAnnotation, true> GObjectTracedAnnotations;
|
|
FUObjectAnnotationSparseSearchable<FObjectIdAnnotation, true> GObjectIdAnnotations;
|
|
|
|
// Handle used to hook to world tick
|
|
static FDelegateHandle WorldTickStartHandle;
|
|
|
|
void TickObjectTraceWorldSubsystem(UWorld* InWorld, ELevelTick InTickType, float InDeltaSeconds)
|
|
{
|
|
if(InTickType == LEVELTICK_All)
|
|
{
|
|
if (!InWorld->IsPaused())
|
|
{
|
|
if(UObjectTraceWorldSubsystem* Subsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(InWorld))
|
|
{
|
|
Subsystem->FrameIndex++;
|
|
Subsystem->ElapsedTime += InDeltaSeconds;
|
|
|
|
UE_TRACE_LOG(Object, RecordingInfo, ObjectChannel)
|
|
<< RecordingInfo.WorldId(FObjectTrace::GetObjectId(InWorld))
|
|
<< RecordingInfo.Cycle(FPlatformTime::Cycles64())
|
|
<< RecordingInfo.ElapsedTime(Subsystem->ElapsedTime)
|
|
<< RecordingInfo.FrameIndex(Subsystem->FrameIndex)
|
|
<< RecordingInfo.RecordingIndex(Subsystem->RecordingIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FObjectTrace::Init()
|
|
{
|
|
WorldTickStartHandle = FWorldDelegates::OnWorldTickStart.AddStatic(&TickObjectTraceWorldSubsystem);
|
|
}
|
|
|
|
void FObjectTrace::Destroy()
|
|
{
|
|
FWorldDelegates::OnWorldTickStart.Remove(WorldTickStartHandle);
|
|
}
|
|
|
|
void FObjectTrace::Reset()
|
|
{
|
|
GObjectIdAnnotations.RemoveAllAnnotations();
|
|
GObjectTracedAnnotations.RemoveAllAnnotations();
|
|
}
|
|
|
|
uint64 FObjectTrace::GetObjectId(const UObject* InObject)
|
|
{
|
|
// An object ID uses a combination of its own and its outer's index
|
|
// We do this to represent objects that get renamed into different outers
|
|
// as distinct traces (we don't attempt to link them).
|
|
|
|
auto GetObjectIdInner = [](const UObject* InObjectInner)
|
|
{
|
|
static uint64 CurrentId = 1;
|
|
|
|
FObjectIdAnnotation Annotation = GObjectIdAnnotations.GetAnnotation(InObjectInner);
|
|
if(Annotation.Id == 0)
|
|
{
|
|
Annotation.Id = CurrentId++;
|
|
GObjectIdAnnotations.AddAnnotation(InObjectInner, MoveTemp(Annotation));
|
|
}
|
|
|
|
return Annotation.Id;
|
|
};
|
|
|
|
uint64 Id = 0;
|
|
uint64 OuterId = 0;
|
|
if(InObject)
|
|
{
|
|
Id = GetObjectIdInner(InObject);
|
|
|
|
if(const UObject* Outer = InObject->GetOuter())
|
|
{
|
|
OuterId = GetObjectIdInner(Outer);
|
|
}
|
|
}
|
|
|
|
return Id | (OuterId << 32);
|
|
}
|
|
|
|
UObject* FObjectTrace::GetObjectFromId(uint64 Id)
|
|
{
|
|
FObjectIdAnnotation FindAnnotation;
|
|
// Id used for annotation map doesn't include the parent id in the upper bits, so zero those first
|
|
FindAnnotation.Id = Id & 0x00000000FFFFFFFFll;
|
|
if (FindAnnotation.IsDefault())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return GObjectIdAnnotations.Find(FindAnnotation);
|
|
}
|
|
|
|
void FObjectTrace::ResetWorldElapsedTime(const UWorld* World)
|
|
{
|
|
if(UObjectTraceWorldSubsystem* WorldSubsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(World))
|
|
{
|
|
WorldSubsystem->ElapsedTime = 0;
|
|
}
|
|
|
|
GObjectTracedAnnotations.RemoveAllAnnotations();
|
|
}
|
|
|
|
double FObjectTrace::GetWorldElapsedTime(const UWorld* World)
|
|
{
|
|
if(UObjectTraceWorldSubsystem* WorldSubsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(World))
|
|
{
|
|
return WorldSubsystem->ElapsedTime;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
double FObjectTrace::GetObjectWorldElapsedTime(const UObject* InObject)
|
|
{
|
|
if(InObject != nullptr)
|
|
{
|
|
if(UWorld* World = InObject->GetWorld())
|
|
{
|
|
if(UObjectTraceWorldSubsystem* WorldSubsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(World))
|
|
{
|
|
return WorldSubsystem->ElapsedTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FObjectTrace::SetWorldRecordingIndex(const UWorld* World, uint16 Index)
|
|
{
|
|
if(UObjectTraceWorldSubsystem* WorldSubsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(World))
|
|
{
|
|
WorldSubsystem->RecordingIndex = Index;
|
|
}
|
|
}
|
|
|
|
uint16 FObjectTrace::GetWorldRecordingIndex(const UWorld* World)
|
|
{
|
|
if(UObjectTraceWorldSubsystem* WorldSubsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(World))
|
|
{
|
|
return WorldSubsystem->RecordingIndex;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint16 FObjectTrace::GetObjectWorldRecordingIndex(const UObject* InObject)
|
|
{
|
|
if(InObject != nullptr)
|
|
{
|
|
if(UWorld* World = InObject->GetWorld())
|
|
{
|
|
if(UObjectTraceWorldSubsystem* WorldSubsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(World))
|
|
{
|
|
return WorldSubsystem->RecordingIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint16 FObjectTrace::GetObjectWorldTickCounter(const UObject* InObject)
|
|
{
|
|
if(InObject != nullptr)
|
|
{
|
|
if(UWorld* World = InObject->GetWorld())
|
|
{
|
|
if(UObjectTraceWorldSubsystem* WorldSubsystem = UWorld::GetSubsystem<UObjectTraceWorldSubsystem>(World))
|
|
{
|
|
return WorldSubsystem->FrameIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FObjectTrace::OutputClass(const UClass* InClass)
|
|
{
|
|
bool bChannelEnabled = UE_TRACE_CHANNELEXPR_IS_ENABLED(ObjectChannel);
|
|
if (!bChannelEnabled || InClass == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FTracedObjectAnnotation Annotation = GObjectTracedAnnotations.GetAnnotation(InClass);
|
|
if(Annotation.bTraced)
|
|
{
|
|
// Already traced, so skip
|
|
return;
|
|
}
|
|
|
|
OutputClass(InClass->GetSuperClass());
|
|
|
|
Annotation.bTraced = true;
|
|
GObjectTracedAnnotations.AddAnnotation(InClass, MoveTemp(Annotation));
|
|
|
|
FString ClassPathName = InClass->GetPathName();
|
|
TCHAR ClassName[FName::StringBufferSize];
|
|
uint32 ClassNameLength = InClass->GetFName().ToString(ClassName);
|
|
|
|
UE_TRACE_LOG(Object, Class, ObjectChannel)
|
|
<< Class.Id(GetObjectId(InClass))
|
|
<< Class.SuperId(GetObjectId(InClass->GetSuperClass()))
|
|
<< Class.Name(ClassName, ClassNameLength)
|
|
<< Class.Path(*ClassPathName, ClassPathName.Len());
|
|
}
|
|
|
|
void FObjectTrace::OutputView(const UObject* InPlayer, const FSceneView* InView)
|
|
{
|
|
bool bChannelEnabled = UE_TRACE_CHANNELEXPR_IS_ENABLED(ObjectChannel);
|
|
if (!bChannelEnabled || InPlayer == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CANNOT_TRACE_OBJECT(InPlayer->GetWorld()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FIntRect& ViewRect = InView->CameraConstrainedViewRect;
|
|
float AspectRatio = (float)ViewRect.Width()/(float)ViewRect.Height();
|
|
|
|
FMatrix ProjMatrix = InView->ViewMatrices.GetProjectionMatrix();
|
|
float Fov = atan(1.0 / ProjMatrix.M[0][0]) * 2.0 * 180.0 / UE_DOUBLE_PI;
|
|
|
|
UE_TRACE_LOG(Object, View, ObjectChannel)
|
|
<< View.Cycle(FPlatformTime::Cycles64())
|
|
<< View.PlayerId(GetObjectId(InPlayer))
|
|
<< View.PosX(InView->ViewLocation.X)
|
|
<< View.PosY(InView->ViewLocation.Y)
|
|
<< View.PosZ(InView->ViewLocation.Z)
|
|
<< View.Pitch(InView->ViewRotation.Pitch)
|
|
<< View.Yaw(InView->ViewRotation.Yaw)
|
|
<< View.Roll(InView->ViewRotation.Roll)
|
|
<< View.Fov(Fov)
|
|
<< View.AspectRatio(AspectRatio);
|
|
}
|
|
|
|
void FObjectTrace::OutputObject(const UObject* InObject)
|
|
{
|
|
bool bChannelEnabled = UE_TRACE_CHANNELEXPR_IS_ENABLED(ObjectChannel);
|
|
if (!bChannelEnabled || InObject == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(InObject->HasAnyFlags(RF_ClassDefaultObject))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CANNOT_TRACE_OBJECT(InObject->GetWorld()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FTracedObjectAnnotation Annotation = GObjectTracedAnnotations.GetAnnotation(InObject);
|
|
if(Annotation.bTraced)
|
|
{
|
|
// Already traced, so skip
|
|
return;
|
|
}
|
|
|
|
OutputObject(InObject->GetOuter());
|
|
|
|
Annotation.bTraced = true;
|
|
GObjectTracedAnnotations.AddAnnotation(InObject, MoveTemp(Annotation));
|
|
|
|
// Trace the object's class first
|
|
TRACE_CLASS(InObject->GetClass());
|
|
|
|
TCHAR ObjectName[FName::StringBufferSize];
|
|
uint32 ObjectNameLength = InObject->GetFName().ToString(ObjectName);
|
|
FString ObjectPathName = InObject->GetPathName();
|
|
|
|
UE_TRACE_LOG(Object, Object, ObjectChannel)
|
|
<< Object.Id(GetObjectId(InObject))
|
|
<< Object.ClassId(GetObjectId(InObject->GetClass()))
|
|
<< Object.OuterId(GetObjectId(InObject->GetOuter()))
|
|
<< Object.Name(ObjectName, ObjectNameLength)
|
|
<< Object.Path(*ObjectPathName, ObjectPathName.Len());
|
|
|
|
UE_TRACE_LOG(Object, ObjectLifetimeBegin2, ObjectChannel)
|
|
<< ObjectLifetimeBegin2.Cycle(FPlatformTime::Cycles64())
|
|
<< ObjectLifetimeBegin2.RecordingTime(FObjectTrace::GetWorldElapsedTime(InObject->GetWorld()))
|
|
<< ObjectLifetimeBegin2.Id(GetObjectId(InObject));
|
|
}
|
|
|
|
void FObjectTrace::OutputObjectEvent(const UObject* InObject, const TCHAR* InEvent)
|
|
{
|
|
bool bChannelEnabled = UE_TRACE_CHANNELEXPR_IS_ENABLED(ObjectChannel);
|
|
if (!bChannelEnabled || InObject == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(InObject->HasAnyFlags(RF_ClassDefaultObject))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CANNOT_TRACE_OBJECT(InObject->GetWorld()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
TRACE_OBJECT(InObject);
|
|
|
|
UE_TRACE_LOG(Object, ObjectEvent, ObjectChannel)
|
|
<< ObjectEvent.Cycle(FPlatformTime::Cycles64())
|
|
<< ObjectEvent.Id(GetObjectId(InObject))
|
|
<< ObjectEvent.Event(InEvent);
|
|
}
|
|
|
|
void FObjectTrace::OutputObjectLifetimeBegin(const UObject* InObject)
|
|
{
|
|
TRACE_OBJECT(InObject);
|
|
}
|
|
|
|
void FObjectTrace::OutputObjectLifetimeEnd(const UObject* InObject)
|
|
{
|
|
const bool bChannelEnabled = UE_TRACE_CHANNELEXPR_IS_ENABLED(ObjectChannel);
|
|
if (!bChannelEnabled || InObject == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(InObject->HasAnyFlags(RF_ClassDefaultObject))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CANNOT_TRACE_OBJECT(InObject->GetWorld()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
TRACE_OBJECT(InObject);
|
|
|
|
UE_TRACE_LOG(Object, ObjectLifetimeEnd2, ObjectChannel)
|
|
<< ObjectLifetimeEnd2.Cycle(FPlatformTime::Cycles64())
|
|
<< ObjectLifetimeEnd2.RecordingTime(FObjectTrace::GetWorldElapsedTime(InObject->GetWorld()))
|
|
<< ObjectLifetimeEnd2.Id(GetObjectId(InObject));
|
|
}
|
|
|
|
void FObjectTrace::OutputPawnPossess(const UObject* InController, const UObject* InPawn)
|
|
{
|
|
bool bChannelEnabled = UE_TRACE_CHANNELEXPR_IS_ENABLED(ObjectChannel);
|
|
if (!bChannelEnabled || InController == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TRACE_OBJECT(InController);
|
|
if (InPawn)
|
|
{
|
|
TRACE_OBJECT(InPawn);
|
|
}
|
|
|
|
UE_TRACE_LOG(Object, PawnPossess, ObjectChannel)
|
|
<< PawnPossess.Cycle(FPlatformTime::Cycles64())
|
|
<< PawnPossess.ControllerId(GetObjectId(InController))
|
|
<< PawnPossess.PawnId(GetObjectId(InPawn));
|
|
}
|
|
|
|
void FObjectTrace::OutputWorld(const UWorld* InWorld)
|
|
{
|
|
bool bChannelEnabled = UE_TRACE_CHANNELEXPR_IS_ENABLED(ObjectChannel);
|
|
if (!bChannelEnabled || InWorld == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CANNOT_TRACE_OBJECT(InWorld))
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
bool bIsSimulating = GEditor ? GEditor->bIsSimulatingInEditor : false;
|
|
#else
|
|
bool bIsSimulating = false;
|
|
#endif
|
|
|
|
UE_TRACE_LOG(Object, World, ObjectChannel)
|
|
<< World.Id(GetObjectId(InWorld))
|
|
<< World.PIEInstanceId(InWorld->GetOutermost()->GetPIEInstanceID())
|
|
<< World.Type((uint8)InWorld->WorldType)
|
|
<< World.NetMode((uint8)InWorld->GetNetMode())
|
|
<< World.IsSimulating(bIsSimulating);
|
|
|
|
// Trace object AFTER world info so we dont risk world info not being present in the trace
|
|
TRACE_OBJECT(InWorld);
|
|
}
|
|
|
|
#endif
|
|
|