2019-12-26 15:32:37 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2014-04-23 20:14:38 -04:00
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
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 "CoreMinimal.h"
|
|
|
|
|
#include "HAL/ThreadSafeCounter.h"
|
|
|
|
|
#include "Containers/IndirectArray.h"
|
|
|
|
|
#include "Containers/ChunkedArray.h"
|
|
|
|
|
#include "Misc/ScopeLock.h"
|
|
|
|
|
#include "ProfilerCommon.h"
|
|
|
|
|
#include "Stats/StatsData.h"
|
|
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
|
|
Basic structures
|
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
2016-01-27 12:09:53 -05:00
|
|
|
/** Helper struct used to calculate inclusive times, ignoring recursive calls. */
|
|
|
|
|
struct FInclusiveTime
|
|
|
|
|
{
|
|
|
|
|
/** Duration in cycles. */
|
|
|
|
|
uint32 DurationCycles;
|
|
|
|
|
/** Number of calls. */
|
|
|
|
|
int32 CallCount;
|
|
|
|
|
/** Number of recursion. */
|
|
|
|
|
int32 Recursion;
|
|
|
|
|
FInclusiveTime()
|
|
|
|
|
: DurationCycles( 0.0 )
|
|
|
|
|
, CallCount( 0 )
|
|
|
|
|
, Recursion( 0 )
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
/** Profiler stack node, used to store the whole call stack for one frame. */
|
|
|
|
|
struct FProfilerStackNode
|
|
|
|
|
{
|
|
|
|
|
/** Short name. */
|
|
|
|
|
FName StatName;
|
|
|
|
|
FName LongName;
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
TArray<FProfilerStackNode*> Children;
|
|
|
|
|
FProfilerStackNode* Parent;
|
|
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
int64 CyclesStart;
|
|
|
|
|
int64 CyclesEnd;
|
|
|
|
|
|
2016-01-27 12:09:53 -05:00
|
|
|
// #Profiler: 2014-04-23 This should be based on the global time.
|
2014-05-08 08:00:35 -04:00
|
|
|
double CycleCounterStartTimeMS;
|
|
|
|
|
double CycleCounterEndTimeMS;
|
2014-04-23 20:14:38 -04:00
|
|
|
|
|
|
|
|
/** Index of this node in the data provider's collection. */
|
2014-05-08 08:00:35 -04:00
|
|
|
/*const*/ uint32 SampleIndex;
|
|
|
|
|
|
|
|
|
|
/** Index of the frame the this node belongs to. */
|
|
|
|
|
const int32 FrameIndex;
|
2014-04-23 20:14:38 -04:00
|
|
|
|
|
|
|
|
/** Initializes thread root node. */
|
2014-05-08 08:00:35 -04:00
|
|
|
FProfilerStackNode( int32 InFrameIndex )
|
|
|
|
|
: StatName( FStatConstants::NAME_ThreadRoot )
|
2014-04-23 20:14:38 -04:00
|
|
|
, LongName( FStatConstants::NAME_ThreadRoot )
|
2014-05-08 08:00:35 -04:00
|
|
|
, Parent( nullptr )
|
2014-04-23 20:14:38 -04:00
|
|
|
, CyclesStart( 0 )
|
|
|
|
|
, CyclesEnd( 0 )
|
2014-05-08 08:00:35 -04:00
|
|
|
, CycleCounterStartTimeMS( 0.0f )
|
|
|
|
|
, CycleCounterEndTimeMS( 0.0f )
|
2014-04-23 20:14:38 -04:00
|
|
|
, SampleIndex( 0 )
|
2014-05-08 08:00:35 -04:00
|
|
|
, FrameIndex( InFrameIndex )
|
2014-04-23 20:14:38 -04:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/** Initializes children node. */
|
2014-05-08 08:00:35 -04:00
|
|
|
FProfilerStackNode( FProfilerStackNode* InParent, const FStatMessage& StatMessage, uint32 InSampleIndex, int32 InFrameIndex )
|
|
|
|
|
: StatName( StatMessage.NameAndInfo.GetShortName() )
|
2014-04-23 20:14:38 -04:00
|
|
|
, LongName( StatMessage.NameAndInfo.GetRawName() )
|
2014-05-08 08:00:35 -04:00
|
|
|
, Parent( InParent )
|
2014-04-23 20:14:38 -04:00
|
|
|
, CyclesStart( StatMessage.GetValue_int64() )
|
|
|
|
|
, CyclesEnd( 0 )
|
2014-05-08 08:00:35 -04:00
|
|
|
, CycleCounterStartTimeMS( 0.0f )
|
|
|
|
|
, CycleCounterEndTimeMS( 0.0f )
|
2014-04-23 20:14:38 -04:00
|
|
|
, SampleIndex( InSampleIndex )
|
2014-05-08 08:00:35 -04:00
|
|
|
, FrameIndex( InFrameIndex )
|
2014-04-23 20:14:38 -04:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
~FProfilerStackNode()
|
|
|
|
|
{
|
|
|
|
|
for( const auto& Child : Children )
|
|
|
|
|
{
|
|
|
|
|
delete Child;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
double GetDurationMS() const
|
|
|
|
|
{
|
|
|
|
|
return CycleCounterEndTimeMS - CycleCounterStartTimeMS;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
/** Calculates allocated size by all stacks node, only valid for the thread root node. */
|
|
|
|
|
int64 GetAllocatedSize() const
|
|
|
|
|
{
|
|
|
|
|
int64 AllocatedSize = sizeof(*this) + Children.GetAllocatedSize();
|
|
|
|
|
for( const auto& StackNode : Children )
|
|
|
|
|
{
|
|
|
|
|
AllocatedSize += StackNode->GetAllocatedSize();
|
|
|
|
|
}
|
|
|
|
|
return AllocatedSize;
|
|
|
|
|
}
|
2014-05-08 08:00:35 -04:00
|
|
|
|
|
|
|
|
void AdjustCycleCounters( double CycleCounterAdjustmentMS )
|
|
|
|
|
{
|
|
|
|
|
CycleCounterStartTimeMS -= CycleCounterAdjustmentMS;
|
|
|
|
|
CycleCounterEndTimeMS -= CycleCounterAdjustmentMS;
|
|
|
|
|
for( const auto& Child : Children )
|
|
|
|
|
{
|
|
|
|
|
Child->AdjustCycleCounters( CycleCounterAdjustmentMS );
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-23 20:14:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Profiler frame. */
|
|
|
|
|
struct FProfilerFrame
|
|
|
|
|
{
|
2014-05-08 08:00:35 -04:00
|
|
|
protected:
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
STACKNODE_INVALID = 0,
|
|
|
|
|
STACKNODE_VALID = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public:
|
2014-04-23 20:14:38 -04:00
|
|
|
/** Root node. */
|
2016-01-27 12:09:53 -05:00
|
|
|
// #Profiler: 2014-04-25 Replace with TIndirectArray<FProfilerStackNode>??
|
2014-04-23 20:14:38 -04:00
|
|
|
FProfilerStackNode* Root;
|
|
|
|
|
|
|
|
|
|
/** Thread times in milliseconds for this frame. */
|
|
|
|
|
TMap<uint32, float> ThreadTimesMS;
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
/** Target frame as captured by the stats system. */
|
|
|
|
|
int64 TargetFrame;
|
|
|
|
|
|
|
|
|
|
/** Frame time for this frame. */
|
|
|
|
|
double FrameTimeMS;
|
|
|
|
|
|
|
|
|
|
/** How many milliseconds have passed from the beginning. */
|
|
|
|
|
double ElapsedTimeMS;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Last time this frame has been accessed.
|
|
|
|
|
* Used by the profiler's GC to remove 'idle' profiler frames.
|
|
|
|
|
* Used only if working under specified memory constraint.
|
|
|
|
|
*/
|
|
|
|
|
double LastAccessTime;
|
|
|
|
|
|
2016-01-27 12:09:53 -05:00
|
|
|
// #Profiler: 2014-04-22 Non-frame stats like accumulator, counters, memory etc?
|
2014-04-23 20:14:38 -04:00
|
|
|
|
|
|
|
|
/**
|
2014-05-08 08:00:35 -04:00
|
|
|
* Indicates whether this profiler frame is in the memory.
|
|
|
|
|
* This is set by one thread and accessed by another thread, there is no thread contention.
|
2014-04-23 20:14:38 -04:00
|
|
|
*/
|
|
|
|
|
FThreadSafeCounter AccessLock;
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
FProfilerFrame( int64 InTargetFrame, double InFrameTimeMS, double InElapsedTimeMS )
|
|
|
|
|
: Root( new FProfilerStackNode( (int32)(InTargetFrame&MAX_int32) ) )
|
|
|
|
|
, TargetFrame( InTargetFrame )
|
|
|
|
|
, FrameTimeMS( InFrameTimeMS )
|
|
|
|
|
, ElapsedTimeMS( InElapsedTimeMS )
|
|
|
|
|
, LastAccessTime( FPlatformTime::Seconds() )
|
2014-04-23 20:14:38 -04:00
|
|
|
, AccessLock( STACKNODE_INVALID )
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
~FProfilerFrame()
|
|
|
|
|
{
|
|
|
|
|
FreeMemory();
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
void AddChild( FProfilerStackNode* ProfilerStackNode )
|
|
|
|
|
{
|
|
|
|
|
Root->Children.Add( ProfilerStackNode );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SortChildren()
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Sorts thread nodes to be in a particular order.
|
|
|
|
|
* GameThread, RenderThread.
|
|
|
|
|
*/
|
|
|
|
|
struct FThreadSort
|
|
|
|
|
{
|
|
|
|
|
FORCEINLINE bool operator()( const FProfilerStackNode& A, const FProfilerStackNode& B ) const
|
|
|
|
|
{
|
|
|
|
|
if( A.StatName == NAME_GameThread && B.StatName == NAME_RenderThread )
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Root->Children.Sort( FThreadSort() );
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
void MarkAsValid()
|
|
|
|
|
{
|
|
|
|
|
const int32 OldLock = AccessLock.Set( STACKNODE_VALID );
|
|
|
|
|
check( OldLock == STACKNODE_INVALID );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MarkAsInvalid()
|
|
|
|
|
{
|
|
|
|
|
const int32 OldLock = AccessLock.Set( STACKNODE_INVALID );
|
|
|
|
|
check( OldLock == STACKNODE_VALID );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsValid() const
|
|
|
|
|
{
|
|
|
|
|
return AccessLock.GetValue() == STACKNODE_VALID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int64 GetAllocatedSize() const
|
|
|
|
|
{
|
2015-03-27 16:44:10 -04:00
|
|
|
return ThreadTimesMS.GetAllocatedSize() + ( Root ? Root->GetAllocatedSize() : 0 );
|
2014-04-23 20:14:38 -04:00
|
|
|
}
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
/** Frees most of the memory allocated by this profiler frame. */
|
2014-04-23 20:14:38 -04:00
|
|
|
void FreeMemory()
|
|
|
|
|
{
|
|
|
|
|
delete Root;
|
|
|
|
|
Root = nullptr;
|
|
|
|
|
|
|
|
|
|
ThreadTimesMS.Empty();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Contains all processed profiler's frames. */
|
2016-01-27 12:09:53 -05:00
|
|
|
// #Profiler: 2014-04-22 Array frames to real frames conversion.
|
|
|
|
|
// #Profiler: 2014-04-22 Can be done fully lock free and thread safe, but later.
|
2014-04-23 20:14:38 -04:00
|
|
|
struct FProfilerStream
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
|
|
/** History frames collected so far or read from the file. */
|
2016-01-27 12:09:53 -05:00
|
|
|
// #Profiler: 2014-04-24 TSharedRef<THREAD_SAFE> ?
|
2014-04-23 20:14:38 -04:00
|
|
|
TChunkedArray<FProfilerFrame*> Frames;
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
/** Each element in this array stores the frame time, accessed by a frame index, in millisecond. */
|
|
|
|
|
TArray<double> FrameTimesMS;
|
|
|
|
|
|
|
|
|
|
/** Each element in this array stores the total frame time, accessed by a frame index, in millisecond. */
|
|
|
|
|
TArray<double> ElapsedFrameTimesMS;
|
|
|
|
|
|
|
|
|
|
/** Thread FNames that have been visible so far. */
|
|
|
|
|
TSet<FName> ThreadIDs;
|
|
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
/** Critical section. */
|
|
|
|
|
mutable FCriticalSection CriticalSection;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2014-05-08 08:00:35 -04:00
|
|
|
void AddProfilerFrame( int64 TargetFrame, FProfilerFrame* ProfilerFrame )
|
2014-04-23 20:14:38 -04:00
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
Frames.AddElement( ProfilerFrame );
|
2014-05-08 08:00:35 -04:00
|
|
|
|
|
|
|
|
FrameTimesMS.Add( ProfilerFrame->FrameTimeMS );
|
|
|
|
|
ElapsedFrameTimesMS.Add( ProfilerFrame->ElapsedTimeMS );
|
|
|
|
|
|
|
|
|
|
//TArray<uint32> FrameThreadIDs;
|
|
|
|
|
//ProfilerFrame->ThreadTimesMS.GenerateKeyArray( FrameThreadIDs );
|
|
|
|
|
ThreadIDs.Add( NAME_GameThread );
|
|
|
|
|
ThreadIDs.Add( NAME_RenderThread );
|
2014-04-23 20:14:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return a pointer to the profiler frame, once obtained can be used until end of the profiler session.
|
|
|
|
|
*/
|
|
|
|
|
FProfilerFrame* GetProfilerFrame( int32 FrameIndex ) const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
return Frames[FrameIndex];
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
/**
|
|
|
|
|
* @return frames indices, where X is the start frame index, Y is the end frame index
|
|
|
|
|
*/
|
|
|
|
|
const FIntPoint GetFramesIndicesForTimeRange( const double StartTimeMS, const double EndTimeMS ) const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
|
|
|
|
|
// Find the start frame index when the start time is less or equal.
|
|
|
|
|
const int32 StartFrameIndex = FBinaryFindIndex::LessEqual( ElapsedFrameTimesMS, StartTimeMS );
|
|
|
|
|
const int32 EndFrameIndex = FBinaryFindIndex::GreaterEqual( ElapsedFrameTimesMS, EndTimeMS, StartFrameIndex );
|
|
|
|
|
|
|
|
|
|
return FIntPoint( StartFrameIndex, EndFrameIndex );
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
int64 GetAllocatedSize() const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
int64 AllocatedSize = 0;
|
|
|
|
|
|
|
|
|
|
for( int32 FrameIndex = 0; FrameIndex < Frames.Num(); FrameIndex++ )
|
|
|
|
|
{
|
|
|
|
|
const FProfilerFrame* ProfilerFrame = Frames[FrameIndex];
|
|
|
|
|
if( ProfilerFrame->IsValid() )
|
|
|
|
|
{
|
|
|
|
|
AllocatedSize += ProfilerFrame->GetAllocatedSize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return AllocatedSize;
|
|
|
|
|
}
|
2014-05-08 08:00:35 -04:00
|
|
|
|
|
|
|
|
/** @return number of frames that have been collected so far. */
|
|
|
|
|
int32 GetNumFrames() const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
return Frames.Num();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @return the elapsed time for all collected frames. */
|
|
|
|
|
double GetElapsedTime() const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
return ElapsedFrameTimesMS[ElapsedFrameTimesMS.Num() - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @return the frame duration for the specified frame, in milliseconds. */
|
|
|
|
|
const double GetFrameTimeMS( const int32 InFrameIndex ) const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
return FrameTimesMS[InFrameIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @return the elapsed time for the specified frame, in milliseconds. */
|
|
|
|
|
const double GetElapsedFrameTimeMS( const int32 InFrameIndex ) const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
return ElapsedFrameTimesMS[InFrameIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AdjustCycleCounters( double CycleCounterAdjustmentMS )
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
for( int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex )
|
|
|
|
|
{
|
|
|
|
|
FProfilerStackNode* RootNode = Frames[FrameIndex]->Root;
|
|
|
|
|
RootNode->AdjustCycleCounters( CycleCounterAdjustmentMS );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return peak number of threads
|
|
|
|
|
*/
|
|
|
|
|
int32 GetNumThreads() const
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock( &CriticalSection );
|
|
|
|
|
return ThreadIDs.Num();
|
|
|
|
|
}
|
2014-04-23 20:14:38 -04:00
|
|
|
};
|
|
|
|
|
|
2016-01-27 12:09:53 -05:00
|
|
|
// #Profiler: 2014-04-23 ProfilerFrameLOD?
|
2014-05-08 08:00:35 -04:00
|
|
|
|
2014-04-23 20:14:38 -04:00
|
|
|
/**
|
|
|
|
|
* Profiler UI stack node.
|
|
|
|
|
* Similar to the profiler stack node, but contains data prepared and optimized for the UI
|
|
|
|
|
*/
|
|
|
|
|
struct FProfilerUIStackNode
|
|
|
|
|
{
|
2014-05-08 08:00:35 -04:00
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
/** Indicates that the nodes is the thread node and shouldn't be displayed. */
|
|
|
|
|
THREAD_NODE_INDEX = -1,
|
|
|
|
|
};
|
2014-04-23 20:14:38 -04:00
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
// FProfilerUIStackNode()
|
|
|
|
|
// : CycleCountersStartTimeMS( 0.0f )
|
|
|
|
|
// , CycleCountersEndTimeMS( 0.0f )
|
|
|
|
|
// , PositionXPx( 0.0f )
|
|
|
|
|
// , PositionY( 0.0f )
|
|
|
|
|
// , Width( 0.0f )
|
|
|
|
|
// , GlobalNodeDepth( THREAD_NODE_INDEX )
|
|
|
|
|
// , ThreadNodeDepth( 0 )
|
|
|
|
|
// , ThreadIndex( THREAD_NODE_INDEX )
|
|
|
|
|
// , bIsCombined( false )
|
|
|
|
|
// {}
|
|
|
|
|
|
|
|
|
|
FProfilerUIStackNode( const FProfilerStackNode* ProfilerStackNode, int32 InGlobalNodeDepth, int32 InThreadIndex, int32 InFrameIndex )
|
|
|
|
|
: StatName( ProfilerStackNode->StatName )
|
|
|
|
|
, LongName( ProfilerStackNode->LongName )
|
|
|
|
|
, CycleCountersStartTimeMS( ProfilerStackNode->CycleCounterStartTimeMS )
|
|
|
|
|
, CycleCountersEndTimeMS( ProfilerStackNode->CycleCounterEndTimeMS )
|
|
|
|
|
, PositionXPx( 0.0f )
|
|
|
|
|
, PositionY( 0.0f )
|
|
|
|
|
, WidthPx( 0.0f )
|
|
|
|
|
, GlobalNodeDepth( InGlobalNodeDepth )
|
|
|
|
|
, ThreadNodeDepth( 0 )
|
|
|
|
|
, ThreadIndex( InThreadIndex )
|
|
|
|
|
, FrameIndex( InFrameIndex )
|
|
|
|
|
, bIsCombined( false )
|
|
|
|
|
, bIsCulled( false )
|
|
|
|
|
{
|
|
|
|
|
OriginalStackNodes.Add( ProfilerStackNode );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FProfilerUIStackNode( const TArray<const FProfilerStackNode*>& ProfilerStackNodes, int32 NumStackNodes, int32 InGlobalNodeDepth, int32 InThreadIndex, int32 InFrameIndex )
|
|
|
|
|
: StatName( *FString::Printf( TEXT( "[%i]" ), NumStackNodes ) )
|
|
|
|
|
, LongName( *FString::Printf( TEXT( "Combined %i items" ), NumStackNodes ) )
|
|
|
|
|
, CycleCountersStartTimeMS( ProfilerStackNodes[0]->CycleCounterStartTimeMS )
|
|
|
|
|
, CycleCountersEndTimeMS( ProfilerStackNodes[NumStackNodes - 1]->CycleCounterEndTimeMS )
|
|
|
|
|
, PositionXPx( 0.0f )
|
|
|
|
|
, PositionY( 0.0f )
|
|
|
|
|
, WidthPx( 0.0f )
|
|
|
|
|
, GlobalNodeDepth( InGlobalNodeDepth )
|
|
|
|
|
, ThreadNodeDepth( 0 )
|
|
|
|
|
, ThreadIndex( InThreadIndex )
|
|
|
|
|
, FrameIndex( InFrameIndex )
|
|
|
|
|
, bIsCombined( true )
|
|
|
|
|
, bIsCulled( false )
|
|
|
|
|
{
|
|
|
|
|
OriginalStackNodes.Append( ProfilerStackNodes );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~FProfilerUIStackNode()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InitializeUIData( const double NumMillisecondsPerWindow, const double NumPixelsPerMillisecond, const double NumMillisecondsPerSample )
|
|
|
|
|
{
|
|
|
|
|
const double DurationMS = GetDurationMS();
|
|
|
|
|
const double NumPixels = DurationMS*NumPixelsPerMillisecond;
|
|
|
|
|
WidthPx = NumPixels;
|
|
|
|
|
|
|
|
|
|
PositionXPx = CycleCountersStartTimeMS*NumPixelsPerMillisecond;
|
|
|
|
|
PositionY = (double)GlobalNodeDepth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MarkAsCulled()
|
|
|
|
|
{
|
|
|
|
|
bIsCulled = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double GetDurationMS() const
|
|
|
|
|
{
|
|
|
|
|
return CycleCountersEndTimeMS - CycleCountersStartTimeMS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FVector2D GetLocalPosition( double PositionXOffsetPx, double PositionYOffset ) const
|
|
|
|
|
{
|
|
|
|
|
return FVector2D( PositionXPx - PositionXOffsetPx, PositionY - PositionYOffset );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsVisible() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Original stack nodes used to generate this UI stack node.
|
|
|
|
|
* Useful if this node is a combined stack node.
|
|
|
|
|
*/
|
|
|
|
|
TArray<const FProfilerStackNode*> OriginalStackNodes;
|
|
|
|
|
|
|
|
|
|
TIndirectArray<FProfilerUIStackNode> Children;
|
2014-04-23 20:14:38 -04:00
|
|
|
|
|
|
|
|
/** Short name. */
|
|
|
|
|
FName StatName;
|
2014-05-08 08:00:35 -04:00
|
|
|
|
|
|
|
|
/** Long name, short name, stat description, group name. */
|
2014-04-23 20:14:38 -04:00
|
|
|
FName LongName;
|
|
|
|
|
|
2014-05-08 08:00:35 -04:00
|
|
|
/** Time range of the stack node. */
|
|
|
|
|
double CycleCountersStartTimeMS;
|
|
|
|
|
double CycleCountersEndTimeMS;
|
|
|
|
|
|
|
|
|
|
/** UI Data. */
|
|
|
|
|
|
|
|
|
|
/** Position of the stack node, absolute position, needs to be converted to the local before rendering. */
|
|
|
|
|
double PositionXPx;
|
|
|
|
|
double PositionY;
|
|
|
|
|
|
|
|
|
|
/** Width of the stack node, in pixels. */
|
|
|
|
|
double WidthPx;
|
|
|
|
|
|
|
|
|
|
/** Depth of this node, in the global scope. */
|
|
|
|
|
int32 GlobalNodeDepth;
|
|
|
|
|
|
|
|
|
|
/** Depth of this node, in the thread scope. */
|
|
|
|
|
int32 ThreadNodeDepth;
|
|
|
|
|
|
|
|
|
|
/** Thread index of this node. */
|
|
|
|
|
int32 ThreadIndex;
|
|
|
|
|
|
|
|
|
|
/** Index of the frame the this node belongs to. */
|
|
|
|
|
int32 FrameIndex;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Whether this UI stack node has been combined, if this is true we stop processing here.
|
|
|
|
|
* Mouse processing is disabled, and there is no tooltip displayed.
|
|
|
|
|
* The user needs to zoom-in to see more details.
|
|
|
|
|
*/
|
|
|
|
|
bool bIsCombined;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* True means that the UI stack node has deeper call stack, but can't be displayed due to UI limitation.
|
|
|
|
|
* Culled nodes are rendered with a special marker to indicate that there are more nodes.
|
|
|
|
|
*/
|
|
|
|
|
bool bIsCulled;
|
|
|
|
|
|
|
|
|
|
/** Access lock. */
|
|
|
|
|
FThreadSafeCounter AccessLock;
|
2014-04-23 20:14:38 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct FProfilerUIStream
|
|
|
|
|
{
|
2014-05-08 08:00:35 -04:00
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Default number of rows displaying cycle counters.
|
|
|
|
|
* Read it as the call stack depth.
|
|
|
|
|
*/
|
2016-01-27 12:09:53 -05:00
|
|
|
// #Profiler: 2014-04-25 This probably should be the max seen so far for each thread.
|
2014-05-08 08:00:35 -04:00
|
|
|
DEFAULT_VISIBLE_THREAD_DEPTH = 16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TIndirectArray<FProfilerUIStackNode> ThreadNodes;
|
|
|
|
|
|
|
|
|
|
TArray< TArray<const FProfilerUIStackNode*> > LinearRowsOfNodes;
|
|
|
|
|
|
|
|
|
|
void GenerateUIStream( const FProfilerStream& ProfilerStream, const double StartTimeMS, const double EndTimeMS, const double ZoomFactorX, const double NumMillisecondsPerWindow, const double NumPixelsPerMillisecond, const double NumMillisecondsPerSample );
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
void CombineOrSet( FProfilerUIStackNode* ParentUIStackNode, const FProfilerStackNode& ProfilerStackNode, int32 GlobalNodeDepth, const double NumMillisecondsPerWindow, const double NumPixelsPerMillisecond, const double NumMillisecondsPerSample );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Converts a tree representation into a flat-array-based. */
|
|
|
|
|
void LinearizeStream()
|
|
|
|
|
{
|
2014-05-08 08:15:33 -04:00
|
|
|
//SCOPE_LOG_TIME_FUNC();
|
2014-05-08 08:00:35 -04:00
|
|
|
|
|
|
|
|
LinearRowsOfNodes.SetNum( ThreadNodes.Num()*DEFAULT_VISIBLE_THREAD_DEPTH );
|
|
|
|
|
for( auto& RowOfNodes : LinearRowsOfNodes )
|
|
|
|
|
{
|
|
|
|
|
RowOfNodes.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( const auto& ThreadNode : ThreadNodes )
|
|
|
|
|
{
|
|
|
|
|
LinearizeStream_Recursively( &ThreadNode );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LinearizeStream_Recursively( const FProfilerUIStackNode* UIStackNode )
|
|
|
|
|
{
|
|
|
|
|
if( UIStackNode->GlobalNodeDepth != FProfilerUIStackNode::THREAD_NODE_INDEX )
|
|
|
|
|
{
|
|
|
|
|
LinearRowsOfNodes[UIStackNode->GlobalNodeDepth].Add( UIStackNode );
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-04 15:11:29 -04:00
|
|
|
for( const auto& UIStackNodeChild : UIStackNode->Children )
|
2014-05-08 08:00:35 -04:00
|
|
|
{
|
2014-06-04 15:11:29 -04:00
|
|
|
LinearizeStream_Recursively(&UIStackNodeChild);
|
2014-05-08 08:00:35 -04:00
|
|
|
}
|
|
|
|
|
}
|
2014-04-23 20:14:38 -04:00
|
|
|
};
|
|
|
|
|
|