You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3121996 on 2016/09/12 by Ben.Marsh
Add support for Visual Studio 2017 (aka "15"; assuming consistent naming with other versions until final name is announced).
* Compiler, STL implementation and CRT are binary compatible with VS2015 (see https://blogs.msdn.microsoft.com/vcblog/2016/08/24/c1417-features-and-stl-fixes-in-vs-15-preview-4/), so no new third-party libraries needed so far. WindowsPlatform.GetVisualStudioCompilerVersionName() returns "2015" as a result.
* Default compiler for compiling and generating project files is still VS 2015 for now. Pass -2017 on the command line to GenerateProjectFiles.bat to generate VS2017 projects. Projects generated for VS2017 will use the 2017 compiler by default.
* Visual Studio source code accessor can talk to VS 2017 instances.
* Added a VS2017 configuration for UnrealVS, and added precompiled vsix package.
* Switched GetVSComnTools to check the SOFTWARE\Microsoft\VisualStudio\SxS\VS7 registry key rather than the individual product install registry key. "15" doesn't seem to have it's own "InstallDir" key, but this system seems to work for all versions of Visual Studio (including previous releases of VS Express).
* Removed ATL dependency from VisualStudioSourceCodeAccessor. It's not installed with VS by default any more, and is only used for a couple of smart pointer classes.
Tested running the editor and packaging TP_Flying for Win64. Packaging from the editor still defaults to using the 2015 compiler, so ConfigureToolchain() needs to be overriden from the .target.cs file if multiple Visual Studio versions are installed.
Change 3189363 on 2016/11/07 by Ben.Marsh
Consolidate functionality for determining the path to MSBuild.exe to use for compiling UE4 tools into a single batch file (GetMSBuildToolPath) and fix "Clean" not working on PS4 due to include/library paths being set to something by the Visual Studio environment.
Change 3210598 on 2016/11/27 by Ben.Marsh
UBT: Prevent the name of each file compiled being output twice on XboxOne. Compiler already outputs this string; the action doesn't need to.
Change 3210601 on 2016/11/27 by Ben.Marsh
PR #2967: Add silent version of switch game version (Contributed by EricLeeFriedman)
Change 3210602 on 2016/11/27 by Ben.Marsh
PR #2964: GitDependencies shouldn't try to clean up working directory files that are excluded or ignored (Contributed by joelmcginnis)
Change 3210605 on 2016/11/27 by Ben.Marsh
UGS: Add a warning when syncing latest would remove changes that have been authored locally. Typically happens when working with precompiled binaries.
Change 3211656 on 2016/11/28 by Ben.Marsh
UBT: Move ModuleRules and TargetRules into their own file.
Change 3211797 on 2016/11/28 by Ben.Marsh
UBT: Remove utility functions from TargetRules for checking different classes of target types. Moving TargetRules to be data-only.
Change 3211833 on 2016/11/28 by Ben.Marsh
UBT: Remove overridable configuration name from target rules. This feature is not used anywhere.
Change 3211859 on 2016/11/28 by Ben.Marsh
UBT: Deprecate the GetGeneratedCodeVersion() callback in favor of a member variable instead.
Change 3211942 on 2016/11/28 by Ben.Marsh
UBT: Remove legacy code which tries to change the output paths for console binaries. Output paths for monolithic binaries are always in the project folder now.
Change 3215333 on 2016/11/30 by Ben.Marsh
UBT: Replace the GetSupportedPlatforms() callback on TargetRules with a SupportedPlatforms attribute. Since a TargetRules object can only be instantiated with an actual platform, it doesn't make sense for it to be an instance method.
Change 3215482 on 2016/11/30 by Ben.Marsh
UBT: Remove the GetSupportedConfigurations() callback on the TargetRules class. A configuration is required to construct a TargetRules instance, so it doesn't make sense to need to call an instance method to find out which configurations are supported.
Change 3215743 on 2016/11/30 by Ben.Marsh
UBT: Deprecate the TargetRules.ShouldCompileMonolithic() function: this function requires access to the global command line to operate correctly, which prevents creating target-specific instances, and does not use the platform/configuration passed into the TargetRules constructor.
Rather than being a callback, the LinkType field can now be set to TargetLinkType.Modular or TargetLinkType.Monolithic from the constructor as appropriate. The default value (TargetLinkType.Default) results in the default link type for the target type being used. Parsing of the command-line overrides is now done when building the TargetDescriptor.
Change 3215778 on 2016/11/30 by Ben.Marsh
UBT: Mark overrides of the TargetRules.GetModulesToPrecompile method as obsolete.
Change 3217681 on 2016/12/01 by Ben.Marsh
UAT: Prevent UE4Build deleting .modules files when running with the -Clean argument; these files are artifacts generated by UBT itself, not by the exported XGE script.
Change 3217723 on 2016/12/01 by Ben.Marsh
UBT: Run pre- and post-build steps for all plugins that are being built, not just those that are enabled.
Change 3217930 on 2016/12/01 by Ben.Marsh
UGS: Add a perforce settings window, allowing users to set optional values for tuning Perforce performance on unreliable connections.
Change 3218762 on 2016/12/02 by Ben.Marsh
Enable warnings whenever an undefined macro is used in a constant expression inside an #if or #elif directive, and fix existing violations.
Change 3219161 on 2016/12/02 by Ben.Marsh
Core: Use the directory containing the current module to derive the UE4 base directory, rather than the executable directory. Allows UE4 to be hosted by a process in a different directory.
Change 3219197 on 2016/12/02 by Ben.Marsh
Core: When loading a DLL from disk, convert any relative paths to absolute before calling LoadLibrary. The OS resolves these paths relative to the directory containing the process executable -- not the working directory -- so paths need to be absolute to allow UE4 to be hosted by a process elsewhere.
Change 3219209 on 2016/12/02 by Ben.Marsh
Replace some calls to LoadLibrary() with FPlatformProcess::GetDllHandle(). The UE4 function makes sure that relative paths are resolved relative to the correct base directory, which is important when the host executable is not in Engine/Binaries/Win64.
Change 3219610 on 2016/12/02 by Ben.Marsh
Add the -q (quiet) option to the Mac unzip command, since it's creating too much log output to be useful.
Change 3219731 on 2016/12/02 by Ben.Marsh
UBT: Add option to disable IWYU checks regarding the use of monolithic headers (Engine.h, UnrealEd.h, etc...) and including the matching header for a cpp file first. bEnforceIWYU can be set to false in UEBuildConfiguration or on a per-module basis in the module rules.
Change 3220796 on 2016/12/04 by Ben.Marsh
Remove PrepForUATPackageOrDeploy from the UEBuildDeploy base class. It never has to be accessed through the base class anyway.
Change 3220825 on 2016/12/04 by Ben.Marsh
UBT: Change all executors to derive from a common base class (ActionExecutor).
Change 3220834 on 2016/12/04 by Ben.Marsh
UBT: Remove the global CommandLineContains() function.
Change 3222706 on 2016/12/05 by Ben.Marsh
Merging CL 3221949 from //UE4/Release-4.14: Fixes to code analysis template causing problems with stock install of VS2017.
Change 3222712 on 2016/12/05 by Ben.Marsh
Merging CL 3222021 from //UE4/Release-4.14: Change detection of MSBuild.exe path to match GetMSBuildPath.bat
Change 3223628 on 2016/12/06 by Ben.Marsh
Merging CL 3223369 from 4.14 branch: Use the same logic as GetMsBuildPath.bat inside FDesktopPlatformBase to determine path to MSBuild.exe
Change 3223817 on 2016/12/06 by Ben.Marsh
Remove non-ANSI characters from source files. Compiler/P4 support is patchy for this, and we want to avoid failing prey to different codepages resulting in different interpretations of the source text.
Change 3224046 on 2016/12/06 by Ben.Marsh
Remove the need for the iOS/TVOS deployment instances to have an IOSPlatformContext instance. The only dependency between the two -- a call to GetRequiredCapabilities() -- is now implemented by querying the INI file for the supported architectures when neeeded.
Change 3224792 on 2016/12/07 by Ben.Marsh
UBT: Touch PCH wrapper files whenever the file they include is newer rather than writing the timestamp for the included file into it as a comment. Allows use of ccache and similar tools.
Change 3225212 on 2016/12/07 by Ben.Marsh
UBT: Move settings required for deployment into the UEBuildDeployTarget class, allowing them to be serialized to and from a file the intermediate directory without having to construct a phony UEBuildTarget to deploy.
Deployment is now performed by a method on UEBuildPlatform, rather than having to create a UEBuildPlatformContext and using that to create a UEBuildDeploy object.
The -prepfordeploy UBT invocation from UAT, previously done by the per-platform PostBuildTarget() callback when building with XGE, is replaced by running UBT with a path to the serialized UEBuildDeployTarget object, and can be done in a platform agnostic manner.
Change 3226310 on 2016/12/07 by Ben.Marsh
PR #3015: Fixes wrong VSC++ flags being passed for .c files (Contributed by badlogic)
Change 3228273 on 2016/12/08 by Ben.Marsh
Update copyright notices for QAGame.
Change 3229166 on 2016/12/09 by Ben.Marsh
UBT: Rewritten config file parser. No longer requires hard-coded list of sections to be parsed, but parses them on demand. Measured 2x faster read speeds (largely due to eliminating construction of temporary string objects when parsing each line, to trim whitespace and so on). Also includes an attribute-driven parser, which allows reading named config values for marked up fields in an object.
Change 3230601 on 2016/12/12 by Ben.Marsh
Swarm: Change Swarm AgentInterface to target .NET framework 4.5, to remove dependency on having 4.0 framework installed.
Change 3230737 on 2016/12/12 by Ben.Marsh
UAT: Stop UE4Build deriving from CommandUtils. Confusing pattern, and causes problems trying to access instance variables that are only set for build commands.
Change 3230751 on 2016/12/12 by Ben.Marsh
UAT: Move ParseParam*() functions which use the instanced parameter list from CommandUtils to BuildCommand, since that's the only thing that it's instanced for.
Change 3230804 on 2016/12/12 by Ben.Marsh
UBT: Add the IsPromotedBuild flag to Build.version, and only set the bFormalBuild flag in UBT if it's set. This allows UGS users to avoid having to compile separate RC files for each output binary.
Change 3230831 on 2016/12/12 by Ben.Marsh
UGS: Warn when trying to switch streams if files are checked out.
Change 3231281 on 2016/12/12 by Chad.Garyet
Fixing a bug where .modules files were getting put into receipts with their absolute path instead of their relative one
Change 3231496 on 2016/12/12 by Ben.Marsh
Disable code analysis in CrashReportProcess; causes warnings when compiled with VS2015.
Change 3231979 on 2016/12/12 by Ben.Marsh
UBT: Suppress LNK4221 when generating import libraries. This can happen often when generating import libraries separately to linking.
Change 3232619 on 2016/12/13 by Ben.Marsh
Fix "#pragma once in main file" errors on Mac, which are occurring in //UE4/Main.
[CL 3232653 by Ben Marsh in Main branch]
2258 lines
67 KiB
C++
2258 lines
67 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "HAL/ThreadSingleton.h"
|
|
#include "Containers/ChunkedArray.h"
|
|
#include "Misc/Guid.h"
|
|
#include "ProfilerCommon.h"
|
|
#include "ProfilerSample.h"
|
|
|
|
class FEventGraphData;
|
|
class FEventGraphSample;
|
|
class FProfilerAggregatedStat;
|
|
class FProfilerSession;
|
|
class IDataProvider;
|
|
|
|
/** Type definition for shared pointers to instances of FEventGraphSample. */
|
|
typedef TSharedPtr<class FEventGraphSample> FEventGraphSamplePtr;
|
|
|
|
/** Type definition for shared references to instances of FEventGraphSample. */
|
|
typedef TSharedRef<class FEventGraphSample> FEventGraphSampleRef;
|
|
|
|
/** Type definition for weak references to instances of FEventGraphSample. */
|
|
typedef TWeakPtr<class FEventGraphSample> FEventGraphSampleWeak;
|
|
|
|
/** Type definition for shared pointers to instances of FEventGraphData. */
|
|
typedef TSharedPtr<class FEventGraphData, ESPMode::ThreadSafe> FEventGraphDataPtr;
|
|
|
|
/** Type definition for shared references to instances of FEventGraphData. */
|
|
typedef TSharedRef<class FEventGraphData, ESPMode::ThreadSafe> FEventGraphDataRef;
|
|
|
|
|
|
/** Scratch buffers for multithreaded usage. */
|
|
struct FProfilerScratchArea : public TThreadSingleton<FProfilerScratchArea>
|
|
{
|
|
TArray<FEventGraphSample*> ExecuteOperationArray;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
TimeAccuracy
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
struct FTimeAccuracy
|
|
{
|
|
enum Type
|
|
{
|
|
FPS008,
|
|
FPS015,
|
|
FPS030,
|
|
FPS060,
|
|
FPS120,
|
|
|
|
InvalidOrMax
|
|
};
|
|
|
|
static const float AsFrameTime( Type InTimeAccuracy )
|
|
{
|
|
static const float FrameTimeTable[InvalidOrMax] = {1000.0f/_FPS008,1000.0f/_FPS015,1000.0f/_FPS030,1000.0f/_FPS060,1000.0f/_FPS120};
|
|
return FrameTimeTable[InTimeAccuracy];
|
|
}
|
|
|
|
static const float AsInvFrameTime( Type InTimeAccuracy )
|
|
{
|
|
static const float FPSInvTable[InvalidOrMax] = {0.001f*_FPS008,0.001f*_FPS015,0.001f*_FPS030,0.001f*_FPS060,0.001f*_FPS120};
|
|
return FPSInvTable[InTimeAccuracy];
|
|
}
|
|
|
|
static const int32 AsFPSCounter( Type InTimeAccuracy )
|
|
{
|
|
static const int32 FPSTable[InvalidOrMax] = {_FPS008,_FPS015,_FPS030,_FPS060,_FPS120};
|
|
return FPSTable[InTimeAccuracy];
|
|
}
|
|
|
|
private:
|
|
static const int32 _FPS008;
|
|
static const int32 _FPS015;
|
|
static const int32 _FPS030;
|
|
static const int32 _FPS060;
|
|
static const int32 _FPS120;
|
|
};
|
|
|
|
// Private header and implementation
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
FGraphDataSourceDescription
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
class FGraphDataSourceDescription
|
|
{
|
|
public:
|
|
FGraphDataSourceDescription( const uint32 InStatID )
|
|
: StatID( InStatID )
|
|
, SampleType( EProfilerSampleTypes::InvalidOrMax )
|
|
, CreationTime( -1 )
|
|
{}
|
|
|
|
void Initialize( const FString InStatName, const FString InGroupName, const EProfilerSampleTypes::Type InSampleType, const FDateTime InCreationTime )
|
|
{
|
|
StatName = InStatName;
|
|
GroupName = InGroupName;
|
|
SampleType = InSampleType;
|
|
CreationTime = InCreationTime;
|
|
}
|
|
|
|
/**
|
|
* @return the ID of the stat owned by this data source.
|
|
*/
|
|
const uint32 GetStatID() const
|
|
{
|
|
return StatID;
|
|
}
|
|
|
|
/**
|
|
* @return name of the stat owned by this data source.
|
|
*/
|
|
const FString& GetStatName() const
|
|
{
|
|
return StatName;
|
|
}
|
|
|
|
/**
|
|
* @return name of the stat group owned by this data source.
|
|
*/
|
|
const FString& GetGroupName() const
|
|
{
|
|
return GroupName;
|
|
}
|
|
|
|
/**
|
|
* @return the sample type of the stat owned by this data source.
|
|
*/
|
|
const EProfilerSampleTypes::Type GetSampleType() const
|
|
{
|
|
return SampleType;
|
|
}
|
|
|
|
const FDateTime& GetCreationTime() const
|
|
{
|
|
return CreationTime;
|
|
}
|
|
|
|
/**
|
|
* @return number of bytes allocated by class instance.
|
|
*/
|
|
const SIZE_T GetMemoryUsage() const
|
|
{
|
|
SIZE_T MemoryUsage = sizeof(*this) + StatName.GetAllocatedSize() + GroupName.GetAllocatedSize();
|
|
return MemoryUsage;
|
|
}
|
|
|
|
protected:
|
|
/** The ID of the stat owned by this data source. */
|
|
const uint32 StatID;
|
|
|
|
/** The name of the stat owned by this data source. */
|
|
FString StatName;
|
|
|
|
/** The name of the stat group owned by this data source. */
|
|
FString GroupName;
|
|
|
|
/** The sample type of the stat owned by this data source. */
|
|
EProfilerSampleTypes::Type SampleType;
|
|
|
|
/** The time when this profiler session was created ( time of the connection to the client, time when a profiler capture was created ). */
|
|
FDateTime CreationTime;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
TCacheDataContainer
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Base class used for caching data.
|
|
* Type - type of data needed to be cached
|
|
*/
|
|
template< typename Type >
|
|
class TCacheDataContainer : public FNoncopyable
|
|
{
|
|
protected:
|
|
TCacheDataContainer()
|
|
: CachedValues( 0 )
|
|
{}
|
|
|
|
~TCacheDataContainer()
|
|
{}
|
|
|
|
/** Clears all cached values and reserves the same amount of memory that was allocated before. */
|
|
void ClearCache()
|
|
{
|
|
CachedChunks.Empty( CachedChunks.Num() );
|
|
CachedValues.Empty( CachedValues.Num() );
|
|
}
|
|
|
|
/**
|
|
* @return number of bytes allocated by class instance.
|
|
*/
|
|
const SIZE_T GetMemoryUsage() const
|
|
{
|
|
SIZE_T MemoryUsage = CachedValues.GetAllocatedSize() + CachedChunks.GetAllocatedSize();
|
|
return MemoryUsage;
|
|
}
|
|
|
|
enum
|
|
{
|
|
/** Number of cached values per chunk. */
|
|
NumElementsPerChunk = 64,
|
|
|
|
/** Number of bytes per chunk. */
|
|
NumBytesPerChunk = NumElementsPerChunk * sizeof(Type)
|
|
};
|
|
|
|
/** Cached values, one chunk contains 256 cached values. */
|
|
mutable TChunkedArray<Type,NumBytesPerChunk> CachedValues;
|
|
|
|
/** Each bit in this bit array indicates whether a chunk is fully cached or not. */
|
|
mutable TBitArray<> CachedChunks;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
TCachedDataByTime
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Simple class that provides mechanism for caching data by time with predefined time accuracy.
|
|
* Type - type of data needed to be cached
|
|
* ManagerClass - class that inherits from this class, must implement methods GetUncachedValueFromTimeRange(),GetTotalTimeMS()
|
|
*/
|
|
template< typename Type, typename ManagerClass >
|
|
class TCachedDataByTime : private TCacheDataContainer<Type>
|
|
{
|
|
public:
|
|
/** Typedef for template< typename Type, typename ManagerClass > class TCachedDataByTime. */
|
|
typedef TCachedDataByTime<Type, ManagerClass> ThisCachedDataByTime;
|
|
|
|
typedef TCacheDataContainer<Type> ThisCacheDataContainer;
|
|
|
|
TCachedDataByTime( const FTimeAccuracy::Type InTimeAccuracy )
|
|
: TCacheDataContainer<Type>()
|
|
, TimeAccuracyMS( FTimeAccuracy::AsFrameTime(InTimeAccuracy) )
|
|
, InvTimeAccuracyMS( FTimeAccuracy::AsInvFrameTime(InTimeAccuracy) )
|
|
{}
|
|
|
|
~TCachedDataByTime()
|
|
{}
|
|
|
|
void SetTimeAccuracy( const FTimeAccuracy::Type InTimeAccuracy )
|
|
{
|
|
ClearCache();
|
|
TimeAccuracyMS = FTimeAccuracy::AsFrameTime(InTimeAccuracy);
|
|
InvTimeAccuracyMS = FTimeAccuracy::AsInvFrameTime(InTimeAccuracy);
|
|
}
|
|
|
|
/** Clears all cached values and reserves the same amount of memory that was allocated before. */
|
|
void ClearCache()
|
|
{
|
|
TCacheDataContainer<Type>::ClearCache();
|
|
}
|
|
|
|
/**
|
|
* Calculates start index for the specified time range.
|
|
*
|
|
* @param StartTimeMS - the start of the time range
|
|
* @param EndTimeMS - the end of the time range
|
|
*
|
|
* @return an index of the frame for the specified time range
|
|
*/
|
|
FORCEINLINE const uint32 GetStartIndexFromTimeRange( const float StartTimeMS, const float EndTimeMS ) const
|
|
{
|
|
CheckInvariants( StartTimeMS, EndTimeMS );
|
|
const uint32 Index = FMath::TruncToInt( StartTimeMS * InvTimeAccuracyMS );
|
|
return Index;
|
|
}
|
|
|
|
/**
|
|
* Calculates value for the specified time range.
|
|
* This method looks for frames that meet the requirements.
|
|
* Then read the value(s) and calculate the average value.
|
|
*
|
|
* This is only a basic implementation and may change in future. Works only with constant time range.
|
|
*
|
|
* @param StartTimeMS - the start of the time range
|
|
* @param EndTimeMS - the end of the time range
|
|
*
|
|
* @return a value for the specified time range
|
|
*/
|
|
const Type GetValueFromTimeRange( const float StartTimeMS, const float EndTimeMS ) const
|
|
{
|
|
Type Result = (Type)0;
|
|
const uint32 Index = GetStartIndexFromTimeRange( StartTimeMS, EndTimeMS );
|
|
const uint32 CurrentChunkIndex = Index / ThisCacheDataContainer::NumElementsPerChunk;
|
|
const uint32 TotalNumFrames = FMath::TruncToInt( static_cast<const ManagerClass*>(this)->GetTotalTimeMS() * InvTimeAccuracyMS );
|
|
|
|
const uint32 NumNeededChunks = (TotalNumFrames + ThisCacheDataContainer::NumElementsPerChunk - 1) / ThisCacheDataContainer::NumElementsPerChunk;
|
|
const uint32 NumMissingValues = TotalNumFrames - ThisCacheDataContainer::CachedValues.Num();
|
|
|
|
// Add missing elements to the cached values.
|
|
ThisCacheDataContainer::CachedValues.Add( NumMissingValues );
|
|
|
|
// Add missing chunks and initialize to false.
|
|
for( uint32 NewChunkIndex = ThisCacheDataContainer::CachedChunks.Num(); NewChunkIndex < NumNeededChunks; NewChunkIndex++ )
|
|
{
|
|
ThisCacheDataContainer::CachedChunks.Add( false );
|
|
}
|
|
|
|
const bool bIsChunkFullyCached = ThisCacheDataContainer::CachedChunks[CurrentChunkIndex];
|
|
const bool bCanBeCached = CurrentChunkIndex < NumNeededChunks-1;
|
|
|
|
// Check if this stat for the specified frame index is included in the cached values.
|
|
if( bIsChunkFullyCached )
|
|
{
|
|
Result = ThisCacheDataContainer::CachedValues(Index);
|
|
}
|
|
// If value is not cached and if value can be cached, initialize the whole chunk.
|
|
else if( !bIsChunkFullyCached && bCanBeCached )
|
|
{
|
|
const uint32 ChunkStartIndex = CurrentChunkIndex * ThisCacheDataContainer::NumElementsPerChunk;
|
|
const uint32 ChunkEndIndex = ChunkStartIndex + ThisCacheDataContainer::NumElementsPerChunk;
|
|
|
|
for( uint32 NewValueIndex = ChunkStartIndex; NewValueIndex < ChunkEndIndex; NewValueIndex++ )
|
|
{
|
|
const float SampleStartTimeMS = NewValueIndex * TimeAccuracyMS;
|
|
ThisCacheDataContainer::CachedValues(NewValueIndex) = (Type)static_cast<const ManagerClass*>(this)->GetUncachedValueFromTimeRange( SampleStartTimeMS, SampleStartTimeMS+TimeAccuracyMS );
|
|
}
|
|
|
|
ThisCacheDataContainer::CachedChunks[CurrentChunkIndex] = true;
|
|
Result = ThisCacheDataContainer::CachedValues(Index);
|
|
}
|
|
else
|
|
{
|
|
Result = (Type)static_cast<const ManagerClass*>(this)->GetUncachedValueFromTimeRange( StartTimeMS, EndTimeMS );
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @return number of bytes allocated by class instance.
|
|
*/
|
|
const SIZE_T GetMemoryUsage() const
|
|
{
|
|
SIZE_T MemoryUsage = TCacheDataContainer<Type>::GetMemoryUsage();
|
|
return MemoryUsage;
|
|
}
|
|
|
|
protected:
|
|
FORCEINLINE void CheckInvariants( const float StartTimeMS, const float EndTimeMS ) const
|
|
{
|
|
check( EndTimeMS > StartTimeMS );
|
|
const float TimeRange = EndTimeMS - StartTimeMS;
|
|
const float AbsDiff = FMath::Abs<float>( TimeRange-TimeAccuracyMS );
|
|
bool bIsNearlyEqual = FMath::IsNearlyZero( AbsDiff, 0.1f );
|
|
check( bIsNearlyEqual && TEXT("Time accuracy doesn't match") );
|
|
}
|
|
|
|
protected:
|
|
/** Time accuracy of the cached data, in milliseconds. */
|
|
float TimeAccuracyMS;
|
|
|
|
/** Inverted time accuracy of the cached data, in milliseconds. */
|
|
float InvTimeAccuracyMS;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
TCachedDataByIndex
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Simple class that provides mechanism for caching data by index.
|
|
* Type - type of data needed to be cached
|
|
* ManagerClass - class that inherits from this class, must implement methods GetUncachedValueFromIndex() and GetNumFrames()
|
|
*/
|
|
template< typename Type, typename ManagerClass >
|
|
class TCachedDataByIndex : private TCacheDataContainer<Type>
|
|
{
|
|
public:
|
|
/** Typedef for template< typename Type, typename ManagerClass > class TCachedDataByIndex. */
|
|
typedef TCachedDataByIndex<Type, ManagerClass> ThisCachedDataByIndex;
|
|
|
|
typedef TCacheDataContainer<Type> ThisCacheDataContainer;
|
|
|
|
TCachedDataByIndex()
|
|
: TCacheDataContainer<Type>()
|
|
{}
|
|
|
|
/** Destructor. */
|
|
~TCachedDataByIndex()
|
|
{}
|
|
|
|
/**
|
|
* @param Index - index of the value that is being retrieved
|
|
*
|
|
* @return a value for the specified index, the value is cached on demand and stored in cache for instant access.
|
|
*/
|
|
const Type GetValueFromIndex( const uint32 Index ) const
|
|
{
|
|
Type Result = (Type)0;
|
|
|
|
const uint32 CurrentChunkIndex = Index / ThisCacheDataContainer::NumElementsPerChunk;
|
|
const uint32 TotalNumFrames = static_cast<const ManagerClass*>(this)->GetNumFrames();
|
|
|
|
const uint32 NumNeededChunks = (TotalNumFrames + ThisCacheDataContainer::NumElementsPerChunk - 1) / ThisCacheDataContainer::NumElementsPerChunk;
|
|
const uint32 NumMissingValues = TotalNumFrames - ThisCacheDataContainer::CachedValues.Num();
|
|
|
|
// Add missing elements to the cached values.
|
|
ThisCacheDataContainer::CachedValues.Add( NumMissingValues );
|
|
|
|
// Add missing chunks and initialize to false.
|
|
for( uint32 NewChunkIndex = ThisCacheDataContainer::CachedChunks.Num(); NewChunkIndex < NumNeededChunks; NewChunkIndex++ )
|
|
{
|
|
ThisCacheDataContainer::CachedChunks.Add( false );
|
|
}
|
|
|
|
const bool bIsChunkFullyCached = ThisCacheDataContainer::CachedChunks[CurrentChunkIndex];
|
|
const bool bCanBeCached = CurrentChunkIndex < NumNeededChunks-1;
|
|
|
|
// Check if this stat for the specified frame index is included in the cached values.
|
|
if( bIsChunkFullyCached )
|
|
{
|
|
Result = ThisCacheDataContainer::CachedValues(Index);
|
|
}
|
|
// If value is not cached and if value can be cached, initialize the whole chunk.
|
|
else if( !bIsChunkFullyCached && bCanBeCached )
|
|
{
|
|
const uint32 ChunkStartIndex = CurrentChunkIndex * ThisCacheDataContainer::NumElementsPerChunk;
|
|
const uint32 ChunkEndIndex = ChunkStartIndex + ThisCacheDataContainer::NumElementsPerChunk;
|
|
|
|
for( uint32 NewValueIndex = ChunkStartIndex; NewValueIndex < ChunkEndIndex; NewValueIndex++ )
|
|
{
|
|
ThisCacheDataContainer::CachedValues(NewValueIndex) = (Type)static_cast<const ManagerClass*>(this)->GetUncachedValueFromIndex( NewValueIndex );
|
|
}
|
|
|
|
ThisCacheDataContainer::CachedChunks[CurrentChunkIndex] = true;
|
|
|
|
Result = ThisCacheDataContainer::CachedValues(Index);
|
|
}
|
|
else
|
|
{
|
|
Result = (Type)static_cast<const ManagerClass*>(this)->GetUncachedValueFromIndex( Index );
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @return number of bytes allocated by class instance.
|
|
*/
|
|
const SIZE_T GetMemoryUsage() const
|
|
{
|
|
SIZE_T MemoryUsage = TCacheDataContainer<Type>::GetMemoryUsage();
|
|
return MemoryUsage;
|
|
}
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
FGraphDataSource
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/** Type definition for type of the cached values. */
|
|
typedef float TGraphDataType;
|
|
|
|
/**
|
|
* A specialized view of the a data provider. Provides access only to the specified group of data.
|
|
* This class allows accessing data in linear way which may be used to draw a line graph.
|
|
*/
|
|
class FGraphDataSource
|
|
: public FGraphDataSourceDescription
|
|
, public TCachedDataByIndex< TGraphDataType, FGraphDataSource >
|
|
, public TCachedDataByTime< TGraphDataType, FGraphDataSource >
|
|
{
|
|
friend class TCachedDataByIndex< TGraphDataType, FGraphDataSource >;
|
|
friend class TCachedDataByTime< TGraphDataType, FGraphDataSource >;
|
|
friend class FProfilerSession;
|
|
|
|
protected:
|
|
/**
|
|
* Initialization constructor, hidden on purpose.
|
|
*
|
|
* @param InProfilerSession - a reference to the profiler session that owns this stat
|
|
* @param InStatID - the ID of the stat that this graph data source will be created for
|
|
*
|
|
*/
|
|
FGraphDataSource( const TSharedRef<FProfilerSession>& InProfilerSession, const uint32 InStatID );
|
|
|
|
public:
|
|
/** Virtual destructor. */
|
|
virtual ~FGraphDataSource()
|
|
{
|
|
const SIZE_T MemoryUsage = GetMemoryUsage();
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* @return number of bytes allocated by this graph data source.
|
|
*/
|
|
const SIZE_T GetMemoryUsage() const
|
|
{
|
|
SIZE_T MemoryUsage = 0;
|
|
MemoryUsage += ThisCachedDataByIndex::GetMemoryUsage();
|
|
MemoryUsage += ThisCachedDataByTime::GetMemoryUsage();
|
|
MemoryUsage += FGraphDataSourceDescription::GetMemoryUsage();
|
|
return MemoryUsage;
|
|
|
|
}
|
|
|
|
const uint32 GetNumFrames() const;
|
|
|
|
const float GetTotalTimeMS() const;
|
|
|
|
const TSharedRef<IDataProvider> GetDataProvider() const;
|
|
|
|
/**
|
|
* @return a session instance ID to the profiler session that owns this graph data source.
|
|
*/
|
|
const FGuid GetSessionInstanceID() const;
|
|
|
|
/**
|
|
* @return a const pointer to the aggregated stat for the specified stat ID or null if not found.
|
|
*/
|
|
const FProfilerAggregatedStat* GetAggregatedStat() const;
|
|
|
|
const bool CanBeDisplayedAsTimeBased() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
const bool CanBeDisplayedAsIndexBased() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
/* @return a sample value for the specified frame index from the data provider. */
|
|
const TGraphDataType GetUncachedValueFromIndex( const uint32 FrameIndex ) const;
|
|
|
|
/* @return an approximated sample value for the specified time range from the data provider. */
|
|
const TGraphDataType GetUncachedValueFromTimeRange( const float StartTimeMS, const float EndTimeMS ) const;
|
|
|
|
/** A reference to the profiler session that owns this graph data source. */
|
|
const TSharedRef<FProfilerSession> ProfilerSession;
|
|
|
|
// @TODO: This needs to be moved to 'filters and presets' filtering options.
|
|
float Scale;
|
|
};
|
|
|
|
|
|
/**
|
|
* A specialized view of a few data providers. Provides access only to the specified group of data.
|
|
* Data is interpolated for 60 frames per second.
|
|
* This class allows accessing data in linear way which may be used to draw a combined line graph with min, max and average values.
|
|
*/
|
|
class FCombinedGraphDataSource
|
|
: public FGraphDataSourceDescription
|
|
, public TCachedDataByTime< FVector, FCombinedGraphDataSource >
|
|
{
|
|
friend class TCachedDataByTime< FVector, FCombinedGraphDataSource >;
|
|
friend class FProfilerManager;
|
|
|
|
protected:
|
|
/**
|
|
* Initialization constructor, hidden on purpose.
|
|
*
|
|
* @param InStatID - the ID of the stat that will be drawn in the data graph widget
|
|
*
|
|
*/
|
|
FCombinedGraphDataSource( const uint32 InStatID, const FTimeAccuracy::Type InTimeAccuracy );
|
|
|
|
public:
|
|
/** Destructor. */
|
|
~FCombinedGraphDataSource()
|
|
{
|
|
const SIZE_T MemoryUsage = GetMemoryUsage();
|
|
}
|
|
|
|
const bool CanBeDisplayedAsMulti() const
|
|
{
|
|
const bool bResult = GetSourcesNum() > 1;
|
|
return bResult;
|
|
}
|
|
|
|
const bool CanBeDisplayedAsTimeBased() const
|
|
{
|
|
return GetSourcesNum() > 0;
|
|
}
|
|
|
|
const bool CanBeDisplayedAsIndexBased() const
|
|
{
|
|
return GetSourcesNum() == 1;
|
|
}
|
|
|
|
const bool IsProfilerSessionRegistered( const FGuid& SessionInstanceID ) const
|
|
{
|
|
const bool bIsRegistered = GraphDataSources.Contains( SessionInstanceID );
|
|
return bIsRegistered;
|
|
}
|
|
|
|
void RegisterWithProfilerSession( const FGuid& SessionInstanceID, const TSharedRef<const FGraphDataSource>& GraphDataSource )
|
|
{
|
|
GraphDataSources.Add( SessionInstanceID, GraphDataSource );
|
|
ThisCachedDataByTime::ClearCache();
|
|
}
|
|
|
|
void UnregisterWithProfilerSession( const FGuid& SessionInstanceID )
|
|
{
|
|
GraphDataSources.Remove( SessionInstanceID );
|
|
ThisCachedDataByTime::ClearCache();
|
|
}
|
|
|
|
/**
|
|
* @return number of bytes allocated by this graph data source.
|
|
*/
|
|
const SIZE_T GetMemoryUsage() const
|
|
{
|
|
SIZE_T MemoryUsage = 0;
|
|
return MemoryUsage;
|
|
}
|
|
|
|
const TMap<FGuid,TSharedRef<const FGraphDataSource>>::TConstIterator GetSourcesIterator() const
|
|
{
|
|
return GraphDataSources.CreateConstIterator();
|
|
}
|
|
|
|
const int GetSourcesNum() const
|
|
{
|
|
return GraphDataSources.Num();
|
|
}
|
|
|
|
const TSharedRef<const FGraphDataSource>* GetFirstSource() const
|
|
{
|
|
// TODO: Add global accessible Null FGraphDataSourceRef, and other instances used in the profiler to avoid returning empty pointer.
|
|
const TSharedRef<const FGraphDataSource>* Result = nullptr;
|
|
if( GetSourcesNum() > 0 )
|
|
{
|
|
Result = &GetSourcesIterator().Value();
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
const uint32 GetNumFrames() const
|
|
{
|
|
if( GetSourcesNum() > 0 )
|
|
{
|
|
return FMath::TruncToInt( GetTotalTimeMS() * InvTimeAccuracyMS );
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const float GetTotalTimeMS() const
|
|
{
|
|
if( GetSourcesNum() > 0 )
|
|
{
|
|
float MinTotalTime = 1000.0f * 60 * 60 * 24 * 365;
|
|
|
|
for( auto It = GetSourcesIterator(); It; ++It )
|
|
{
|
|
const TSharedRef<const FGraphDataSource>& GraphDataSource = It.Value();
|
|
MinTotalTime = FMath::Min( MinTotalTime, GraphDataSource->GetTotalTimeMS() );
|
|
}
|
|
|
|
return MinTotalTime;
|
|
}
|
|
else
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param StartTimeMS - the start of the time range
|
|
* @param EndTimeMS - the end of the time range
|
|
* @param out_Indices - the map that contains calculated start index for each graph data source
|
|
*
|
|
*/
|
|
void GetStartIndicesFromTimeRange( const float StartTimeMS, const float EndTimeMS, TMap<FGuid,uint32>& out_StartIndices ) const;
|
|
|
|
protected:
|
|
/* @return an approximated sample value for the specified time range from the data provider. */
|
|
const FVector GetUncachedValueFromTimeRange( const float StartTimeMS, const float EndTimeMS ) const;
|
|
|
|
/** A map of graph data sources for all active profiler session instances for the specified stat ID. */
|
|
TMap<FGuid,TSharedRef<const FGraphDataSource>> GraphDataSources;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Minimal event graph sample property management
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/** Enumerates event graph columns index. */
|
|
enum EEventPropertyIndex
|
|
{
|
|
/** Stat name must be the first column, because of the expander arrow. */
|
|
StatName,
|
|
InclusiveTimeMS,
|
|
InclusiveTimePct,
|
|
ExclusiveTimeMS,
|
|
ExclusiveTimePct,
|
|
NumCallsPerFrame,
|
|
/** Special name used for unknown property. */
|
|
None,
|
|
|
|
MinInclusiveTimeMS,
|
|
MaxInclusiveTimeMS,
|
|
AvgInclusiveTimeMS,
|
|
MinNumCallsPerFrame,
|
|
MaxNumCallsPerFrame,
|
|
AvgNumCallsPerFrame,
|
|
ThreadName,
|
|
ThreadDurationMS,
|
|
FrameDurationMS,
|
|
ThreadPct,
|
|
FramePct,
|
|
ThreadToFramePct,
|
|
GroupName,
|
|
|
|
// Booleans
|
|
bIsHotPath,
|
|
bIsFiltered,
|
|
bIsCulled,
|
|
|
|
// Booleans internal
|
|
bNeedNotCulledChildrenUpdate,
|
|
|
|
/** Invalid enum type, may be used as a number of enumerations. */
|
|
InvalidOrMax,
|
|
};
|
|
|
|
|
|
/** Enumerates event graph sample value formatting types, usually match with event graph widget's columns. */
|
|
enum class EEventPropertyFormatters
|
|
{
|
|
/** Name, stored as a string, displayed as a regular string. */
|
|
Name,
|
|
|
|
/** Time in milliseconds, stored as a double, displayed as ".3f ms" */
|
|
TimeMS,
|
|
|
|
/** Time as percent, stored as a double, displayed as ".1f %" */
|
|
TimePct,
|
|
|
|
/** Number of calls, store as a double, displayed as ".1f" */
|
|
Number,
|
|
|
|
/** Boolean value, store as a bool, displaying is not supported yet. */
|
|
Bool,
|
|
|
|
/** Invalid enum type, may be used as a number of enumerations. */
|
|
InvalidOrMax,
|
|
};
|
|
|
|
|
|
/** Enumerates . */
|
|
enum class EEventPropertyTypes
|
|
{
|
|
/** double. */
|
|
Double,
|
|
|
|
/** FName. */
|
|
Name,
|
|
|
|
/** bool. */
|
|
Bool,
|
|
|
|
/** Invalid enum type, may be used as a number of enumerations. */
|
|
InvalidOrMax,
|
|
};
|
|
|
|
|
|
//namespace NEventProp
|
|
//{
|
|
class FEventProperty /*: public FNoncopyable*/
|
|
{
|
|
friend class FEventGraphSample;
|
|
protected:
|
|
FEventProperty
|
|
(
|
|
const EEventPropertyIndex PropertyIndex,
|
|
const FName PropertyName,
|
|
const uint32 PropertyOffset,
|
|
const EEventPropertyFormatters PropertyFormatter
|
|
)
|
|
:
|
|
Index( PropertyIndex ),
|
|
Name( PropertyName ),
|
|
Offset( PropertyOffset ),
|
|
Formatter( PropertyFormatter ),
|
|
Type( GetTypeFromFormatter(PropertyFormatter) )
|
|
{}
|
|
|
|
EEventPropertyTypes GetTypeFromFormatter( const EEventPropertyFormatters PropertyFormatter ) const
|
|
{
|
|
switch( PropertyFormatter )
|
|
{
|
|
case EEventPropertyFormatters::Name:
|
|
return EEventPropertyTypes::Name;
|
|
|
|
case EEventPropertyFormatters::TimeMS:
|
|
case EEventPropertyFormatters::TimePct:
|
|
case EEventPropertyFormatters::Number:
|
|
return EEventPropertyTypes::Double;
|
|
|
|
case EEventPropertyFormatters::Bool:
|
|
return EEventPropertyTypes::Bool;
|
|
|
|
default:
|
|
check(0);
|
|
return EEventPropertyTypes::InvalidOrMax;
|
|
}
|
|
}
|
|
|
|
public:
|
|
const bool IsDouble() const
|
|
{
|
|
return Type == EEventPropertyTypes::Double;
|
|
}
|
|
|
|
const bool IsBoolean() const
|
|
{
|
|
return Type == EEventPropertyTypes::Bool;
|
|
}
|
|
|
|
const bool IsName() const
|
|
{
|
|
return Type == EEventPropertyTypes::Name;
|
|
}
|
|
|
|
public:
|
|
const EEventPropertyIndex Index;
|
|
const FName Name;
|
|
const uint32 Offset;
|
|
const EEventPropertyFormatters Formatter;
|
|
const EEventPropertyTypes Type;
|
|
};
|
|
|
|
template< typename Type >
|
|
class TEventPropertyValue
|
|
{
|
|
public:
|
|
FORCEINLINE TEventPropertyValue( const FEventGraphSample& InEvent, const FEventProperty& EventProperty )
|
|
: Event( InEvent )
|
|
, PropertyOffset( EventProperty.Offset )
|
|
{}
|
|
|
|
FORCEINLINE_DEBUGGABLE const Type GetPropertyValue() const
|
|
{
|
|
const uint8* const PropertyAddress = GetPropertyAddress();
|
|
const Type Value = *(const Type*)PropertyAddress;
|
|
return Value;
|
|
}
|
|
|
|
FORCEINLINE_DEBUGGABLE Type& GetPropertyValueRef()
|
|
{
|
|
const uint8* const PropertyAddress = GetPropertyAddress();
|
|
Type& Value = (Type&)*(const Type*)PropertyAddress;
|
|
return Value;
|
|
}
|
|
|
|
FORCEINLINE_DEBUGGABLE const Type GetComparablePropertyValue() const
|
|
{
|
|
return GetPropertyValue();
|
|
}
|
|
|
|
FORCEINLINE const uint8* const GetPropertyAddress() const
|
|
{
|
|
const uint8* const SampleAddress = (const uint8* const)&Event;
|
|
const uint8* const PropertyAddress = SampleAddress + PropertyOffset;
|
|
return PropertyAddress;
|
|
}
|
|
|
|
protected:
|
|
const FEventGraphSample& Event;
|
|
const uint32 PropertyOffset;
|
|
};
|
|
|
|
typedef TEventPropertyValue< double > FEventPropertyValue_Double;
|
|
typedef TEventPropertyValue< bool > FEventPropertyValue_Bool;
|
|
|
|
class FEventPropertyValue_Name : public TEventPropertyValue< FName >
|
|
{
|
|
public:
|
|
FORCEINLINE FEventPropertyValue_Name( const FEventGraphSample& InEvent, const FEventProperty& EventProperty )
|
|
: TEventPropertyValue( InEvent, EventProperty )
|
|
{}
|
|
|
|
FORCEINLINE_DEBUGGABLE const FString GetComparablePropertyValue() const
|
|
{
|
|
const FString Value = GetPropertyValue().GetPlainNameString();
|
|
return Value;
|
|
}
|
|
};
|
|
|
|
namespace NEventFormatter
|
|
{
|
|
template< EEventPropertyFormatters PropertyType >
|
|
FORCEINLINE FString ToString( const FEventGraphSample& Event, const FEventProperty& EventProperty )
|
|
{
|
|
check(0);
|
|
return FString();
|
|
};
|
|
|
|
template <>
|
|
FORCEINLINE FString ToString< EEventPropertyFormatters::Name >( const FEventGraphSample& Event, const FEventProperty& EventProperty )
|
|
{
|
|
return FEventPropertyValue_Name(Event,EventProperty).GetPropertyValue().GetPlainNameString();
|
|
};
|
|
|
|
template <>
|
|
FORCEINLINE FString ToString< EEventPropertyFormatters::TimeMS >( const FEventGraphSample& Event, const FEventProperty& EventProperty )
|
|
{
|
|
return FString::Printf( TEXT("%.3f ms"), FEventPropertyValue_Double(Event,EventProperty).GetPropertyValue() );
|
|
};
|
|
|
|
template <>
|
|
FORCEINLINE FString ToString< EEventPropertyFormatters::TimePct >( const FEventGraphSample& Event, const FEventProperty& EventProperty )
|
|
{
|
|
return FString::Printf( TEXT("%.1f %%"), FEventPropertyValue_Double(Event,EventProperty).GetPropertyValue() );
|
|
};
|
|
|
|
template <>
|
|
FORCEINLINE FString ToString< EEventPropertyFormatters::Number >( const FEventGraphSample& Event, const FEventProperty& EventProperty )
|
|
{
|
|
return FString::Printf( TEXT("%.1f"), FEventPropertyValue_Double(Event,EventProperty).GetPropertyValue() );
|
|
};
|
|
}
|
|
|
|
//};
|
|
|
|
/** Hides all event graph related functionality under this namespace. */
|
|
//namespace NEventGraphSample {
|
|
|
|
/** Useful constants related to event graph functionality. */
|
|
struct FEventGraphConsts
|
|
{
|
|
static FName RootEvent;
|
|
static FName Self;
|
|
static FName FakeRoot;
|
|
};
|
|
|
|
// TODO: Rename to FProfilerEvent
|
|
/**
|
|
* Contains the same data as the profiler sample with some additions, doesn't depend on the other classes like profiler metadata or profiler aggregates.
|
|
* Modeled to be Slate compatible, thus it inherits from TSharedFromThis.
|
|
*/
|
|
class FEventGraphSample : public TSharedFromThis<FEventGraphSample>
|
|
{
|
|
struct FDuplicateHierarchyTag {};
|
|
struct FDuplicateSimpleTag {};
|
|
|
|
friend class FEventArraySorter;
|
|
friend class FEventGraphData;
|
|
|
|
public:
|
|
/** Initializes the minimal property manager for the event graph sample. */
|
|
static void InitializePropertyManagement();
|
|
|
|
static FORCEINLINE_DEBUGGABLE const FEventProperty& GetEventPropertyByIndex( const EEventPropertyIndex PropertyIndex )
|
|
{
|
|
return Properties[(const int32)PropertyIndex];
|
|
}
|
|
|
|
static FORCEINLINE_DEBUGGABLE const FEventProperty& GetEventPropertyByName( const FName PropertyName )
|
|
{
|
|
return *NamedProperties.FindChecked( PropertyName );
|
|
}
|
|
|
|
protected:
|
|
/** Contains all properties of the event graph sample class. */
|
|
static FEventProperty Properties[ EEventPropertyIndex::InvalidOrMax ];
|
|
|
|
/** Contains all properties of the event graph sample class, indexed by the name of the property, stored as FName -> FEventProperty&. */
|
|
static TMap<FName,const FEventProperty*> NamedProperties;
|
|
|
|
public:
|
|
/** Initialization constructor. Only used for root event. */
|
|
FEventGraphSample( const FName InName )
|
|
: _ParentPtr( nullptr )
|
|
, _RootPtr( nullptr )
|
|
, _ThreadPtr( nullptr )
|
|
, _ThreadName( InName )
|
|
, _GroupName( InName )
|
|
, _StatName( InName )
|
|
, _StatID( 0 )
|
|
, _InclusiveTimeMS( 0.0 )
|
|
, _InclusiveTimePct( 0.0 )
|
|
, _MinInclusiveTimeMS( TNumericLimits<double>::Max() )
|
|
, _MaxInclusiveTimeMS( TNumericLimits<double>::Min() )
|
|
, _AvgInclusiveTimeMS( 0.0 )
|
|
, _NumCallsPerFrame( 1.0 )
|
|
, _MinNumCallsPerFrame( TNumericLimits<double>::Max() )
|
|
, _MaxNumCallsPerFrame( TNumericLimits<double>::Min() )
|
|
, _AvgNumCallsPerFrame( 0.0 )
|
|
, _ExclusiveTimeMS( 0.0 )
|
|
, _ExclusiveTimePct( 0.0 )
|
|
, _FrameDurationMS( 0.0 )
|
|
, _ThreadDurationMS( 0.0 )
|
|
, _ThreadToFramePct( 0.0 )
|
|
, _ThreadPct( 0.0 )
|
|
, _FramePct( 0.0 )
|
|
, _bIsHotPath( false )
|
|
, _bIsFiltered( false )
|
|
, _bIsCulled( false )
|
|
, bNeedNotCulledChildrenUpdate( true )
|
|
{}
|
|
|
|
/**
|
|
* @return creates a named event
|
|
*/
|
|
static FEventGraphSamplePtr CreateNamedEvent( const FName EventName )
|
|
{
|
|
FEventGraphSample* RootEventGraphSample = new FEventGraphSample( EventName );
|
|
return MakeShareable( RootEventGraphSample );
|
|
}
|
|
|
|
protected:
|
|
/** Initialization constructor. */
|
|
FEventGraphSample
|
|
(
|
|
const FName InThreadName,
|
|
|
|
const FName InGroupName,
|
|
const uint32 InStatID,
|
|
const FName InStatName,
|
|
|
|
const double InInclusiveTimeMS,
|
|
const double InNumCallsPerFrame,
|
|
|
|
const FEventGraphSamplePtr InParentPtr = nullptr
|
|
)
|
|
: _ParentPtr( InParentPtr )
|
|
, _RootPtr( nullptr )
|
|
, _ThreadPtr( nullptr )
|
|
, _ThreadName( InThreadName )
|
|
, _GroupName( InGroupName )
|
|
, _StatName( InStatName )
|
|
, _StatID( InStatID )
|
|
, _InclusiveTimeMS( InInclusiveTimeMS )
|
|
, _InclusiveTimePct( 0.0f )
|
|
, _MinInclusiveTimeMS( InInclusiveTimeMS )
|
|
, _MaxInclusiveTimeMS( InInclusiveTimeMS )
|
|
, _AvgInclusiveTimeMS( InInclusiveTimeMS )
|
|
, _NumCallsPerFrame( InNumCallsPerFrame )
|
|
, _MinNumCallsPerFrame( InNumCallsPerFrame )
|
|
, _MaxNumCallsPerFrame( InNumCallsPerFrame )
|
|
, _AvgNumCallsPerFrame( InNumCallsPerFrame )
|
|
, _ExclusiveTimeMS( 0.0f )
|
|
, _ExclusiveTimePct( 0.0f )
|
|
, _FrameDurationMS( 0.0f )
|
|
, _ThreadDurationMS( 0.0f )
|
|
, _ThreadToFramePct( 0.0f )
|
|
, _ThreadPct( 0.0f )
|
|
, _FramePct( 0.0f )
|
|
, _bIsHotPath( false )
|
|
, _bIsFiltered( false )
|
|
, _bIsCulled( false )
|
|
, bNeedNotCulledChildrenUpdate( true )
|
|
{}
|
|
|
|
/** Copy constructor, copies properties from the specified source event. */
|
|
FEventGraphSample( const FEventGraphSample& SourceEvent, const FDuplicateSimpleTag )
|
|
: _ParentPtr( nullptr )
|
|
, _RootPtr( nullptr )
|
|
, _ThreadPtr( nullptr )
|
|
, _ThreadName( SourceEvent._ThreadName )
|
|
, _GroupName( SourceEvent._GroupName )
|
|
, _StatName( SourceEvent._StatName )
|
|
, _StatID( SourceEvent._StatID )
|
|
, _InclusiveTimeMS( SourceEvent._InclusiveTimeMS )
|
|
, _InclusiveTimePct( SourceEvent._InclusiveTimePct )
|
|
, _MinInclusiveTimeMS( SourceEvent._MinInclusiveTimeMS )
|
|
, _MaxInclusiveTimeMS( SourceEvent._MaxInclusiveTimeMS )
|
|
, _AvgInclusiveTimeMS( SourceEvent._AvgInclusiveTimeMS )
|
|
, _NumCallsPerFrame( SourceEvent._NumCallsPerFrame )
|
|
, _MinNumCallsPerFrame( SourceEvent._MinNumCallsPerFrame )
|
|
, _MaxNumCallsPerFrame( SourceEvent._MaxNumCallsPerFrame )
|
|
, _AvgNumCallsPerFrame( SourceEvent._AvgNumCallsPerFrame )
|
|
, _ExclusiveTimeMS( SourceEvent._ExclusiveTimeMS )
|
|
, _ExclusiveTimePct( SourceEvent._ExclusiveTimePct )
|
|
, _FrameDurationMS( SourceEvent._FrameDurationMS )
|
|
, _ThreadDurationMS( SourceEvent._ThreadDurationMS )
|
|
, _ThreadToFramePct( SourceEvent._ThreadToFramePct )
|
|
, _ThreadPct( SourceEvent._ThreadPct )
|
|
, _FramePct( SourceEvent._FramePct )
|
|
, _bIsHotPath( false )
|
|
, _bIsFiltered( false )
|
|
, _bIsCulled( false )
|
|
, bNeedNotCulledChildrenUpdate( true )
|
|
{}
|
|
|
|
public:
|
|
/** Destructor. */
|
|
~FEventGraphSample()
|
|
{
|
|
#ifdef _DEBUG
|
|
int k = 0; k++;
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Operations
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
FORCEINLINE_DEBUGGABLE void Combine( const FEventGraphSamplePtr& Other )
|
|
{
|
|
// Total
|
|
_InclusiveTimeMS += Other->_InclusiveTimeMS;
|
|
_NumCallsPerFrame += Other->_NumCallsPerFrame;
|
|
//_ExclusiveTimeMS += Other->_ExclusiveTimeMS;
|
|
|
|
// Min/Max
|
|
_MinInclusiveTimeMS = FMath::Min( _MinInclusiveTimeMS, Other->_MinInclusiveTimeMS );
|
|
_MaxInclusiveTimeMS = FMath::Max( _MaxInclusiveTimeMS, Other->_MaxInclusiveTimeMS );
|
|
|
|
_MinNumCallsPerFrame = FMath::Min( _MinNumCallsPerFrame, Other->_MinNumCallsPerFrame );
|
|
_MaxNumCallsPerFrame = FMath::Max( _MaxNumCallsPerFrame, Other->_MaxNumCallsPerFrame );
|
|
}
|
|
|
|
void RecalcTimes()
|
|
{
|
|
_FrameDurationMS = 0;
|
|
_ThreadDurationMS = 0;
|
|
_InclusiveTimeMS = 0;
|
|
|
|
for (auto Child : GetChildren())
|
|
{
|
|
_FrameDurationMS += Child->_FrameDurationMS;
|
|
_ThreadDurationMS += Child->_ThreadDurationMS;
|
|
_InclusiveTimeMS += Child->_InclusiveTimeMS;
|
|
}
|
|
|
|
for (auto Child : GetChildren())
|
|
{
|
|
Child->_InclusiveTimePct = (Child->_InclusiveTimeMS * 100) / _InclusiveTimeMS;
|
|
Child->_ThreadPct = (Child->_ThreadDurationMS * 100) / _ThreadDurationMS;
|
|
Child->_FramePct = (Child->_FrameDurationMS * 100) / _FrameDurationMS;
|
|
}
|
|
}
|
|
protected:
|
|
|
|
/** For creating per-frame average event graph. */
|
|
FORCEINLINE_DEBUGGABLE void CopyAverage( const double NumFrames )
|
|
{
|
|
_InclusiveTimeMS = _AvgInclusiveTimeMS;
|
|
_NumCallsPerFrame = _AvgNumCallsPerFrame;
|
|
|
|
_FrameDurationMS /= NumFrames;
|
|
_ThreadDurationMS /= NumFrames;
|
|
|
|
FixFrameThreadPcts();
|
|
}
|
|
|
|
/** For creating highest "per-frame" event graph. */
|
|
FORCEINLINE_DEBUGGABLE void CopyMaximum( FEventGraphSample* RootEvent, FEventGraphSample* ThreadEvent )
|
|
{
|
|
_InclusiveTimeMS = _MaxInclusiveTimeMS;
|
|
_NumCallsPerFrame = _MaxNumCallsPerFrame;
|
|
|
|
_ThreadDurationMS = ThreadEvent->_MaxInclusiveTimeMS;
|
|
_FrameDurationMS = RootEvent->_MaxInclusiveTimeMS;
|
|
|
|
// Exclusive values don't make sense for max.
|
|
_ExclusiveTimeMS = 0.0;
|
|
_ExclusiveTimePct = 0.0;
|
|
|
|
FixFrameThreadPcts();
|
|
}
|
|
|
|
/** Calculates time and percentage values that may depend on a child's parent. */
|
|
void FixChildrenTimesAndCalcAverages( const double NumFrames )
|
|
{
|
|
if (_StatName != FEventGraphConsts::RootEvent)
|
|
{
|
|
// Get correct value for frame and thread duration.
|
|
_FrameDurationMS = GetRoot()->_InclusiveTimeMS;
|
|
_ThreadDurationMS = GetThread()->_InclusiveTimeMS;
|
|
|
|
FEventGraphSample* Parent = GetParent().Get();
|
|
if (Parent)
|
|
{
|
|
if (IsSelf())
|
|
{
|
|
Parent->_ExclusiveTimeMS = _InclusiveTimeMS;
|
|
Parent->_ExclusiveTimePct = 100.0f * Parent->_ExclusiveTimeMS / Parent->_InclusiveTimeMS;
|
|
}
|
|
|
|
_InclusiveTimePct = 100.0f * _InclusiveTimeMS / Parent->_InclusiveTimeMS;
|
|
}
|
|
|
|
FixFrameThreadPcts();
|
|
}
|
|
else
|
|
{
|
|
_InclusiveTimePct = 100.0f;
|
|
_ThreadDurationMS = _InclusiveTimeMS;
|
|
_FrameDurationMS = _InclusiveTimeMS;
|
|
}
|
|
|
|
_AvgInclusiveTimeMS = _InclusiveTimeMS / NumFrames;
|
|
_AvgNumCallsPerFrame = _NumCallsPerFrame / NumFrames;
|
|
}
|
|
|
|
void FixFrameThreadPcts()
|
|
{
|
|
_ThreadToFramePct = 100.0f*_ThreadDurationMS / _FrameDurationMS;
|
|
_ThreadPct = 100.0f*_InclusiveTimeMS / _ThreadDurationMS;
|
|
_FramePct = 100.0f*_InclusiveTimeMS / _FrameDurationMS;
|
|
}
|
|
|
|
/** Copy maximum values for all children, also fixes thread/frame time. */
|
|
void SetMaximumTimesForAllChildren();
|
|
|
|
/** Sets root and thread for all children. */
|
|
void SetRootAndThreadForAllChildren();
|
|
|
|
/** Fixes children times and calculates average values. */
|
|
void FixChildrenTimesAndCalcAveragesForAllChildren( const double NumFrames );
|
|
|
|
FORCEINLINE_DEBUGGABLE bool AreTheSamePtr( const FEventGraphSamplePtr& Other ) const
|
|
{
|
|
const bool bAreChildrenTheSame = _ThreadName == Other->_ThreadName && _StatID == Other->_StatID;// && _GroupName==Other._GroupName;
|
|
return bAreChildrenTheSame;
|
|
}
|
|
|
|
|
|
FEventGraphSamplePtr FindChildPtr( const FEventGraphSamplePtr& ChildToLook );
|
|
void Combine_Recurrent( const FEventGraphSamplePtr& Other );
|
|
|
|
public:
|
|
template< typename TFunc >
|
|
FORCEINLINE_DEBUGGABLE void ExecuteOperationForAllChildren( TFunc FuncToCall )
|
|
{
|
|
const bool bAllowShrinking = false;
|
|
TArray<FEventGraphSample*>& Stack = FProfilerScratchArea::Get().ExecuteOperationArray;
|
|
|
|
Stack.Add( &AsShared().Get() );
|
|
int32 Idx = 1;
|
|
|
|
while (Stack.Num() > 0)
|
|
{
|
|
// Get the parent and assign events.
|
|
FEventGraphSample* Current = Stack.Pop( bAllowShrinking );
|
|
FuncToCall( Current );
|
|
|
|
// Push children onto the stack.
|
|
const TArray<FEventGraphSamplePtr>& ChildrenPtr = Current->GetChildren();
|
|
const int32 NumChildren = ChildrenPtr.Num();
|
|
for( int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex )
|
|
{
|
|
const FEventGraphSamplePtr& ChildPtr = ChildrenPtr[ChildIndex];
|
|
Stack.Push( ChildPtr.Get() );
|
|
}
|
|
}
|
|
|
|
// Reset, but keep the allocation.
|
|
Stack.Reset();
|
|
}
|
|
|
|
template< typename TFunc, typename TArg0 >
|
|
FORCEINLINE_DEBUGGABLE void ExecuteOperationForAllChildren( TFunc FuncToCall, TArg0 Arg0 )
|
|
{
|
|
const bool bAllowShrinking = false;
|
|
TArray<FEventGraphSample*>& Stack = FProfilerScratchArea::Get().ExecuteOperationArray;
|
|
|
|
Stack.Add( &AsShared().Get() );
|
|
int32 Idx = 1;
|
|
|
|
while (Stack.Num() > 0)
|
|
{
|
|
// Get the parent and assign events.
|
|
FEventGraphSample* Current = Stack.Pop( bAllowShrinking );
|
|
FuncToCall( Current, Arg0 );
|
|
|
|
// Push children onto the stack.
|
|
const TArray<FEventGraphSamplePtr>& ChildrenPtr = Current->GetChildren();
|
|
const int32 NumChildren = ChildrenPtr.Num();
|
|
for (int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
|
|
{
|
|
const FEventGraphSamplePtr& ChildPtr = ChildrenPtr[ChildIndex];
|
|
Stack.Push( ChildPtr.Get() );
|
|
}
|
|
}
|
|
|
|
// Reset, but keep the allocation.
|
|
Stack.Reset();
|
|
}
|
|
|
|
template< typename TFunc, typename TArg0, typename TArg1 >
|
|
FORCEINLINE_DEBUGGABLE void ExecuteOperationForAllChildren( TFunc FuncToCall, TArg0 Arg0, TArg1 Arg1 )
|
|
{
|
|
const bool bAllowShrinking = false;
|
|
TArray<FEventGraphSample*>& Stack = FProfilerScratchArea::Get().ExecuteOperationArray;
|
|
|
|
Stack.Add( &AsShared().Get() );
|
|
int32 Idx = 1;
|
|
|
|
while (Stack.Num() > 0)
|
|
{
|
|
// Get the parent and assign events.
|
|
FEventGraphSample* Current = Stack.Pop( bAllowShrinking );
|
|
FuncToCall( Current, Arg0, Arg1 );
|
|
|
|
// Push children onto the stack.
|
|
const TArray<FEventGraphSamplePtr>& ChildrenPtr = Current->GetChildren();
|
|
const int32 NumChildren = ChildrenPtr.Num();
|
|
for (int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
|
|
{
|
|
const FEventGraphSamplePtr& ChildPtr = ChildrenPtr[ChildIndex];
|
|
Stack.Push( ChildPtr.Get() );
|
|
}
|
|
}
|
|
|
|
// Reset, but keep the allocation.
|
|
Stack.Reset();
|
|
}
|
|
|
|
protected:
|
|
/** @return a shared pointer to the newly created copy of this event graph sample, creates a full copy of hierarchy and duplicates all samples. */
|
|
FEventGraphSamplePtr DuplicateWithHierarchyPtr()
|
|
{
|
|
FEventGraphSamplePtr ParentPtr = DuplicateSimplePtr();
|
|
|
|
// Duplicate children
|
|
const int32 NumChildren = _ChildrenPtr.Num();
|
|
ParentPtr->_ChildrenPtr.Reserve( _ChildrenPtr.Num() );
|
|
|
|
for( int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex )
|
|
{
|
|
FEventGraphSamplePtr ChildPtr = _ChildrenPtr[ChildIndex]->DuplicateWithHierarchyPtr();
|
|
//ParentPtr->AddChildAndSetParentPtr( ChildPtr );
|
|
ChildPtr->_ParentPtr = ParentPtr;
|
|
ParentPtr->_ChildrenPtr.Add( ChildPtr );
|
|
}
|
|
return ParentPtr;
|
|
}
|
|
|
|
FORCEINLINE void AddChildAndSetParentPtr( const FEventGraphSamplePtr& ChildPtr )
|
|
{
|
|
ChildPtr->_ParentPtr = AsShared();
|
|
_ChildrenPtr.Add( ChildPtr );
|
|
}
|
|
|
|
|
|
#if 0
|
|
FORCEINLINE_DEBUGGABLE void SetRootAndThreadEvents_Iterative( FEventGraphSample* RootEvent, FEventGraphSample* ThreadEvent )
|
|
{
|
|
const bool bAllowShrinking = false;
|
|
TArray<FEventGraphSample*>& Stack = FProfilerScratchArea::Get().ExecuteOperationArray;
|
|
|
|
Stack.Add( &AsShared().Get() );
|
|
int32 Idx = 1;
|
|
|
|
while (Stack.Num() > 0)
|
|
{
|
|
// Get the parent and assign events.
|
|
FEventGraphSample* Current = Stack.Pop( bAllowShrinking );
|
|
FSetRootAndThread()(Current, RootEvent, ThreadEvent);
|
|
|
|
// Push children onto the stack.
|
|
const TArray<FEventGraphSamplePtr>& ChildrenPtr = Current->GetChildren();
|
|
const int32 NumChildren = ChildrenPtr.Num();
|
|
for (int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
|
|
{
|
|
const FEventGraphSamplePtr& ChildPtr = ChildrenPtr[ChildIndex];
|
|
Stack.Push( ChildPtr.Get() );
|
|
}
|
|
}
|
|
|
|
// Reset, but keep the allocation.
|
|
Stack.Reset();
|
|
}
|
|
#endif // 0
|
|
|
|
public:
|
|
/** @return a shared pointer to the newly created copy of this event graph sample, without any children and with no parent. */
|
|
FEventGraphSamplePtr DuplicateSimplePtr()
|
|
{
|
|
return MakeShareable( new FEventGraphSample( *this, FDuplicateSimpleTag() ) );
|
|
}
|
|
|
|
/** Adds a child to this sample. */
|
|
FORCEINLINE void AddChildPtr( const FEventGraphSamplePtr& Child )
|
|
{
|
|
_ChildrenPtr.Add( Child );
|
|
}
|
|
|
|
/**
|
|
* @return true, if this event is a root event.
|
|
*/
|
|
bool IsRoot() const
|
|
{
|
|
return _StatName == FEventGraphConsts::RootEvent;
|
|
}
|
|
|
|
/**
|
|
* @return true, if this event is a fake self event.
|
|
*/
|
|
bool IsSelf() const
|
|
{
|
|
return _StatName == FEventGraphConsts::Self;
|
|
}
|
|
|
|
/**
|
|
* @return a shared pointer to the parent of this event, may be null.
|
|
*/
|
|
FORCEINLINE FEventGraphSamplePtr GetParent() const
|
|
{
|
|
return _ParentPtr.Pin();
|
|
}
|
|
|
|
/**
|
|
* @Reparent this event
|
|
*/
|
|
FORCEINLINE void SetParent(FEventGraphSamplePtr NewParent)
|
|
{
|
|
_ParentPtr = NewParent;
|
|
}
|
|
|
|
/**
|
|
* @return a shared pointer to the root event of this event, may be null.
|
|
*/
|
|
const FEventGraphSample* GetRoot() const
|
|
{
|
|
return _RootPtr;
|
|
}
|
|
|
|
/**
|
|
* @return a shared pointer to the thread event of this event, may be null.
|
|
*/
|
|
const FEventGraphSample* GetThread() const
|
|
{
|
|
return _ThreadPtr;
|
|
}
|
|
|
|
/**
|
|
* @return a const reference to the child samples of this sample.
|
|
*/
|
|
FORCEINLINE const TArray<FEventGraphSamplePtr>& GetChildren() const
|
|
{
|
|
return _ChildrenPtr;
|
|
}
|
|
|
|
/**
|
|
* @return a const array that contains all children that have not been culled.
|
|
*/
|
|
FORCEINLINE_DEBUGGABLE const TArray<FEventGraphSamplePtr>& GetNotCulledChildren() const
|
|
{
|
|
const_cast<FEventGraphSample*>(this)->UpdateNotCulledChildrenInternal();
|
|
return _NotCulledChildrenPtr;
|
|
}
|
|
|
|
FORCEINLINE void RequestNotCulledChildrenUpdate()
|
|
{
|
|
bNeedNotCulledChildrenUpdate = true;
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* @return an array that contains all children that have not been culled.
|
|
*/
|
|
FORCEINLINE_DEBUGGABLE TArray<FEventGraphSamplePtr>& GetNotCulledChildrenInternal()
|
|
{
|
|
UpdateNotCulledChildrenInternal();
|
|
return _NotCulledChildrenPtr;
|
|
}
|
|
|
|
/**
|
|
* Updates an array that will contain all children that have not been culled.
|
|
*/
|
|
FORCEINLINE_DEBUGGABLE void UpdateNotCulledChildrenInternal()
|
|
{
|
|
if( bNeedNotCulledChildrenUpdate )
|
|
{
|
|
_NotCulledChildrenPtr.Reset();
|
|
|
|
const int32 NumChildren = _ChildrenPtr.Num();
|
|
for( int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex )
|
|
{
|
|
const FEventGraphSamplePtr& ChildPtr = _ChildrenPtr[ChildIndex];
|
|
if( !ChildPtr->_bIsCulled )
|
|
{
|
|
_NotCulledChildrenPtr.Add( ChildPtr );
|
|
}
|
|
}
|
|
|
|
bNeedNotCulledChildrenUpdate = false;
|
|
}
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* @return true, if this event contains culled children.
|
|
*/
|
|
FORCEINLINE_DEBUGGABLE const bool HasCulledChildren() const
|
|
{
|
|
const bool bHasCulledChildren = GetChildren().Num() != GetNotCulledChildren().Num();
|
|
return bHasCulledChildren;
|
|
}
|
|
|
|
/**
|
|
* @return a reference to the child samples of this sample.
|
|
*/
|
|
FORCEINLINE TArray<FEventGraphSamplePtr>& GetChildren()
|
|
{
|
|
return _ChildrenPtr;
|
|
}
|
|
|
|
/**
|
|
* @return a shorter name of this event.
|
|
*/
|
|
FORCEINLINE const FString GetShortEventName() const
|
|
{
|
|
return FProfilerHelper::ShortenName( _StatName.GetPlainNameString() );
|
|
}
|
|
|
|
/**
|
|
* @return a topmost parent of this event, usually it is a thread event. The root event is excluded.
|
|
*/
|
|
FEventGraphSamplePtr GetOutermost()
|
|
{
|
|
FEventGraphSamplePtr Outermost;
|
|
for( FEventGraphSamplePtr Top = AsShared(); Top.IsValid() && Top->GetParent().IsValid(); Top = Top->GetParent() )
|
|
{
|
|
Outermost = Top;
|
|
}
|
|
return Outermost;
|
|
}
|
|
|
|
void GetStack( TArray<FEventGraphSamplePtr>& out_Stack )
|
|
{
|
|
for( FEventGraphSamplePtr Top = AsShared(); Top.IsValid() && Top->GetParent().IsValid(); Top = Top->GetParent() )
|
|
{
|
|
out_Stack.Add( Top );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates an array with all event samples, so they can be accessed in the linear way.
|
|
* None of the events are duplicated. The root event is excluded.
|
|
*/
|
|
FORCEINLINE_DEBUGGABLE void GetLinearEvents( TArray<FEventGraphSamplePtr>& out_LinearEvents, const bool bUseCulled )
|
|
{
|
|
out_LinearEvents.Reset();
|
|
|
|
const TArray<FEventGraphSamplePtr> RootChildren = bUseCulled ? GetNotCulledChildren() : GetChildren();
|
|
|
|
const int32 NumChildren = RootChildren.Num();
|
|
for( int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex )
|
|
{
|
|
GetLinearEvents_InternalRecurrent( RootChildren[ChildIndex], out_LinearEvents, bUseCulled );
|
|
}
|
|
}
|
|
|
|
private:
|
|
/** Internal method used to store linearized events. */
|
|
FORCEINLINE_DEBUGGABLE void GetLinearEvents_InternalRecurrent( const FEventGraphSamplePtr& ParentEvent, TArray<FEventGraphSamplePtr>& out_LinearEvents, const bool bUseCulled )
|
|
{
|
|
out_LinearEvents.Add( ParentEvent );
|
|
const TArray<FEventGraphSamplePtr> Children = bUseCulled ? ParentEvent->GetNotCulledChildren() : ParentEvent->GetChildren();
|
|
const int32 NumChildren = Children.Num();
|
|
for( int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex )
|
|
{
|
|
GetLinearEvents_InternalRecurrent( Children[ChildIndex], out_LinearEvents, bUseCulled );
|
|
}
|
|
}
|
|
|
|
public:
|
|
#if 0
|
|
/**
|
|
* @return children time in ms, excluding the self time by default
|
|
*/
|
|
double GetChildrenTimeMS( const bool bExcludeSelf = true ) const
|
|
{
|
|
double ChildrenTotalTimeMS = 0.0f;
|
|
const int32 NumChildren = _ChildrenPtr.Num();
|
|
for( int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex )
|
|
{
|
|
const FEventGraphSamplePtr& ChildPtr = _ChildrenPtr[ChildIndex];
|
|
if( ChildPtr->IsSelf() )
|
|
{
|
|
if( !bExcludeSelf )
|
|
{
|
|
ChildrenTotalTimeMS += ChildPtr->_InclusiveTimeMS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ChildrenTotalTimeMS += ChildPtr->_InclusiveTimeMS;
|
|
}
|
|
}
|
|
return ChildrenTotalTimeMS;
|
|
}
|
|
|
|
/**
|
|
* @return if exist, a fake self event for this event sample, otherwise null.
|
|
*/
|
|
FEventGraphSamplePtr GetSelfOrNull() const
|
|
{
|
|
FEventGraphSamplePtr Self;
|
|
const int32 NumChildren = _ChildrenPtr.Num();
|
|
for( int32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex )
|
|
{
|
|
const FEventGraphSamplePtr& ChildPtr = _ChildrenPtr[ChildIndex];
|
|
if( ChildPtr->IsSelf() )
|
|
{
|
|
Self = ChildPtr;
|
|
break;
|
|
}
|
|
}
|
|
return Self;
|
|
}
|
|
|
|
#endif // 0
|
|
|
|
public:
|
|
double& PropertyValueAsDouble( const EEventPropertyIndex PropertyIndex )
|
|
{
|
|
const FEventProperty& EventProperty = GetEventPropertyByIndex(PropertyIndex);
|
|
// In case if you want to get FName/bool as a double.
|
|
checkSlow( EventProperty.IsDouble() );
|
|
FEventPropertyValue_Double Value(*this, EventProperty );
|
|
return Value.GetPropertyValueRef();
|
|
}
|
|
|
|
FName& PropertyValueAsFName( const EEventPropertyIndex PropertyIndex )
|
|
{
|
|
const FEventProperty& EventProperty = GetEventPropertyByIndex(PropertyIndex);
|
|
// In case if you want to get a double/bool as FName.
|
|
checkSlow( EventProperty.IsName() );
|
|
FEventPropertyValue_Name Value(*this, EventProperty );
|
|
return Value.GetPropertyValueRef();
|
|
}
|
|
|
|
bool& PropertyValueAsBool( const EEventPropertyIndex PropertyIndex )
|
|
{
|
|
const FEventProperty& EventProperty = GetEventPropertyByIndex(PropertyIndex);
|
|
// In case if you want to get a double/FName as a bool.
|
|
checkSlow( EventProperty.IsBoolean() );
|
|
FEventPropertyValue_Bool Value(*this, EventProperty );
|
|
return Value.GetPropertyValueRef();
|
|
}
|
|
|
|
const FString GetPropertyValueAsString( const EEventPropertyIndex PropertyIndex ) const
|
|
{
|
|
const FEventProperty& EventProperty = GetEventPropertyByIndex(PropertyIndex);
|
|
// In case if you want to get a double/bool as FName.
|
|
checkSlow( EventProperty.IsName() );
|
|
FEventPropertyValue_Name Value(*this, EventProperty );
|
|
return Value.GetPropertyValueRef().GetPlainNameString();
|
|
}
|
|
|
|
const FString GetFormattedValue( EEventPropertyIndex PropertyIndex ) const
|
|
{
|
|
const FEventProperty& EventProperty = GetEventPropertyByIndex(PropertyIndex);
|
|
|
|
FString FormattedValue;
|
|
if( EventProperty.Formatter == EEventPropertyFormatters::Name )
|
|
{
|
|
FormattedValue = NEventFormatter::ToString<EEventPropertyFormatters::Name>( *this, EventProperty );
|
|
}
|
|
else if( EventProperty.Formatter == EEventPropertyFormatters::TimeMS )
|
|
{
|
|
FormattedValue = NEventFormatter::ToString<EEventPropertyFormatters::TimeMS>( *this, EventProperty );
|
|
}
|
|
else if( EventProperty.Formatter == EEventPropertyFormatters::TimePct )
|
|
{
|
|
FormattedValue = NEventFormatter::ToString<EEventPropertyFormatters::TimePct>( *this, EventProperty );
|
|
}
|
|
else if( EventProperty.Formatter == EEventPropertyFormatters::Number )
|
|
{
|
|
FormattedValue = NEventFormatter::ToString<EEventPropertyFormatters::Number>( *this, EventProperty );
|
|
}
|
|
else
|
|
{
|
|
check(0);
|
|
}
|
|
return FormattedValue;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Boolean states
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
template< bool FEventGraphSample::*BoolPtr >
|
|
struct FSetBooleanState
|
|
{
|
|
FORCEINLINE void operator()( FEventGraphSample* EventPtr, const bool bValue )
|
|
{
|
|
EventPtr->*BoolPtr = bValue;
|
|
}
|
|
};
|
|
|
|
public:
|
|
template< const EEventPropertyIndex BooleanPropertyIndex >
|
|
void SetBooleanStateForAllChildren( const bool bValue )
|
|
{
|
|
//static_assert(false, "Method not supported.");
|
|
|
|
// Not sure why but this if-statement is not optimized based on BooleanPropertyIndex
|
|
if( BooleanPropertyIndex == EEventPropertyIndex::bIsCulled )
|
|
{
|
|
ExecuteOperationForAllChildren( FSetBooleanState<&FEventGraphSample::_bIsCulled>(), bValue );
|
|
}
|
|
else if( BooleanPropertyIndex == EEventPropertyIndex::bIsFiltered )
|
|
{
|
|
ExecuteOperationForAllChildren( FSetBooleanState<&FEventGraphSample::_bIsFiltered>(), bValue );
|
|
}
|
|
else if( BooleanPropertyIndex == EEventPropertyIndex::bIsHotPath )
|
|
{
|
|
ExecuteOperationForAllChildren( FSetBooleanState<&FEventGraphSample::_bIsHotPath>(), bValue );
|
|
}
|
|
else if( BooleanPropertyIndex == EEventPropertyIndex::bNeedNotCulledChildrenUpdate )
|
|
{
|
|
ExecuteOperationForAllChildren( FSetBooleanState<&FEventGraphSample::bNeedNotCulledChildrenUpdate>(), bValue );
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
/** Not used, optimized version @see SetBooleanStateForAllChildren. */
|
|
void SetBooleanStateForAllChildren_Recurrent( const EEventPropertyIndex BooleanPropertyIndex, const bool bValue )
|
|
{
|
|
PropertyValueAsBool(BooleanPropertyIndex) = bValue;
|
|
const int32 NumChildren = _ChildrenPtr.Num();
|
|
for( int32 ChildNx = 0; ChildNx < NumChildren; ++ChildNx )
|
|
{
|
|
const FEventGraphSamplePtr& Child = _ChildrenPtr[ChildNx];
|
|
Child->SetBooleanStateForAllChildren_Recurrent(BooleanPropertyIndex,bValue);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
|
|
/** A weak pointer to the parent of this event. */
|
|
FEventGraphSampleWeak _ParentPtr;
|
|
|
|
/** A weak pointer to the parent of this event. */
|
|
FEventGraphSample* _RootPtr;
|
|
|
|
/** A weak pointer to the parent of this event. */
|
|
FEventGraphSample* _ThreadPtr;
|
|
|
|
/** Children of this event. */
|
|
TArray<FEventGraphSamplePtr> _ChildrenPtr;
|
|
|
|
/** Not culled children of this event. */
|
|
TArray<FEventGraphSamplePtr> _NotCulledChildrenPtr;
|
|
|
|
public:
|
|
/** Name of the thread that this event was captured on. */
|
|
FName _ThreadName;
|
|
|
|
/** Name of the stat group this event belongs to, ex. Engine. */
|
|
FName _GroupName;
|
|
|
|
/** Name of this event, ex. Frametime. If empty, it means that this sample is a root sample, use _ThreadName. */
|
|
FName _StatName;
|
|
|
|
/** Stat ID of the event. */ // TODO: Not needed, move to _statname if meta data is not available
|
|
uint32 _StatID;
|
|
|
|
/** Duration of this event and its children, in milliseconds. */
|
|
double _InclusiveTimeMS;
|
|
|
|
/** Duration of this event and its children as percent of the caller. */
|
|
double _InclusiveTimePct;
|
|
|
|
/** Minimum inclusive time of all instances for this event, in milliseconds. */
|
|
double _MinInclusiveTimeMS;
|
|
|
|
/** Maximum inclusive time of all instances for this event, in milliseconds. */
|
|
double _MaxInclusiveTimeMS;
|
|
|
|
/** Average inclusive time of all instances for this event, in milliseconds. */
|
|
double _AvgInclusiveTimeMS;
|
|
|
|
/** Number of times this event was called. */
|
|
double _NumCallsPerFrame;
|
|
|
|
/** Minimum number of times this event was called. */
|
|
double _MinNumCallsPerFrame;
|
|
|
|
/** Maximum number of times this event was called. */
|
|
double _MaxNumCallsPerFrame;
|
|
|
|
/** Average number of times this event was called. */
|
|
double _AvgNumCallsPerFrame;
|
|
|
|
/** Exclusive time of this event, in milliseconds. */
|
|
double _ExclusiveTimeMS;
|
|
|
|
/** Exclusive time of this event as percent of this call's inclusive time. */
|
|
double _ExclusiveTimePct;
|
|
|
|
|
|
//{
|
|
/** Duration of the frame that this event belongs to, in milliseconds. */
|
|
double _FrameDurationMS;
|
|
|
|
/** Duration of the thread that this event was captured on, in milliseconds. */
|
|
double _ThreadDurationMS;
|
|
//}
|
|
|
|
/** Percent of time spent in the thread in relation to the entire frame. */
|
|
double _ThreadToFramePct;
|
|
|
|
/** Percent of inclusive time spent by this event in the particular thread. */
|
|
double _ThreadPct;
|
|
|
|
/** Percent of inclusive time spent by this event in the particular frame. */
|
|
double _FramePct;
|
|
|
|
/** True, if this event is marked as being in the hot path. */
|
|
bool _bIsHotPath;
|
|
|
|
/** True, if this event is marked as being filtered based, but still should be visible in the event graph, but faded. */
|
|
bool _bIsFiltered;
|
|
|
|
/** True, if this event is marked as being culled and shouldn't be visible in the event graph. */
|
|
bool _bIsCulled;
|
|
|
|
private:
|
|
/** Whether we need to update the array that contains non culled children. */
|
|
bool bNeedNotCulledChildrenUpdate;
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Sorting by property
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/** Enumerates compare operations. */
|
|
namespace EEventCompareOps
|
|
{
|
|
enum Type
|
|
{
|
|
/** A < B. */
|
|
Less,
|
|
/** B < A. */
|
|
Greater,
|
|
|
|
/** A == B. */
|
|
Equal,
|
|
|
|
/** A contains B. */
|
|
Contains,
|
|
|
|
/** Invalid enum type, may be used as a number of enumerations. */
|
|
InvalidOrMax,
|
|
};
|
|
}
|
|
|
|
namespace EventGraphPrivate
|
|
{
|
|
/** Helper class used to during sorting the events. */
|
|
template< const EEventPropertyTypes PropType, const EEventCompareOps::Type CompareOp >
|
|
struct TCompareByProperty
|
|
{
|
|
private:
|
|
const EEventPropertyIndex PropertyIndex;
|
|
|
|
public:
|
|
FORCEINLINE TCompareByProperty( const EEventPropertyIndex InPropertyIndex )
|
|
: PropertyIndex( InPropertyIndex )
|
|
{}
|
|
};
|
|
|
|
template<>
|
|
struct TCompareByProperty<EEventPropertyTypes::Name,EEventCompareOps::Greater>
|
|
{
|
|
private:
|
|
const EEventPropertyIndex PropertyIndex;
|
|
|
|
public:
|
|
FORCEINLINE TCompareByProperty( const EEventPropertyIndex InPropertyIndex )
|
|
: PropertyIndex( InPropertyIndex )
|
|
{}
|
|
|
|
FORCEINLINE_DEBUGGABLE bool operator()( const FEventGraphSamplePtr& A, const FEventGraphSamplePtr& B ) const
|
|
{
|
|
return A.Get()->PropertyValueAsFName(PropertyIndex).Compare(B.Get()->PropertyValueAsFName(PropertyIndex)) > 0;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct TCompareByProperty<EEventPropertyTypes::Double,EEventCompareOps::Greater>
|
|
{
|
|
private:
|
|
const EEventPropertyIndex PropertyIndex;
|
|
|
|
public:
|
|
FORCEINLINE TCompareByProperty( const EEventPropertyIndex InPropertyIndex )
|
|
: PropertyIndex( InPropertyIndex )
|
|
{}
|
|
|
|
FORCEINLINE_DEBUGGABLE bool operator()( const FEventGraphSamplePtr& A, const FEventGraphSamplePtr& B ) const
|
|
{
|
|
return A.Get()->PropertyValueAsDouble(PropertyIndex) > B.Get()->PropertyValueAsDouble(PropertyIndex);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct TCompareByProperty<EEventPropertyTypes::Name,EEventCompareOps::Less>
|
|
{
|
|
private:
|
|
const EEventPropertyIndex PropertyIndex;
|
|
|
|
public:
|
|
FORCEINLINE TCompareByProperty( const EEventPropertyIndex InPropertyIndex )
|
|
: PropertyIndex( InPropertyIndex )
|
|
{}
|
|
|
|
FORCEINLINE_DEBUGGABLE bool operator()( const FEventGraphSamplePtr& A, const FEventGraphSamplePtr& B ) const
|
|
{
|
|
return A.Get()->PropertyValueAsFName(PropertyIndex).Compare(B.Get()->PropertyValueAsFName(PropertyIndex)) < 0;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct TCompareByProperty<EEventPropertyTypes::Double,EEventCompareOps::Less>
|
|
{
|
|
private:
|
|
const EEventPropertyIndex PropertyIndex;
|
|
|
|
public:
|
|
FORCEINLINE TCompareByProperty( const EEventPropertyIndex InPropertyIndex )
|
|
: PropertyIndex( InPropertyIndex )
|
|
{}
|
|
|
|
FORCEINLINE_DEBUGGABLE bool operator()( const FEventGraphSamplePtr& A, const FEventGraphSamplePtr& B ) const
|
|
{
|
|
return A.Get()->PropertyValueAsDouble(PropertyIndex) < B.Get()->PropertyValueAsDouble(PropertyIndex);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
* Class used to executing specified operation for the specified property on array of events.
|
|
* After executing the specified boolean property will be changed accordingly.
|
|
*/
|
|
class FEventArrayBooleanOp
|
|
{
|
|
// @TODO: Move parameters to constructor and remove static
|
|
// FEventArrayBooleanOp( parameters ).Execute()
|
|
// vs
|
|
// FEventGraphSample::ExecuteOperation( parameters )
|
|
public:
|
|
static void ExecuteOperation
|
|
(
|
|
FEventGraphSamplePtr DestPtr,
|
|
const EEventPropertyIndex DestPropertyIndex,
|
|
const FEventGraphSamplePtr SrcPtr,
|
|
const EEventPropertyIndex SrcPropertyIndex,
|
|
const EEventCompareOps::Type OpType
|
|
)
|
|
{
|
|
const FEventProperty& SrcEventProperty = FEventGraphSample::GetEventPropertyByIndex(SrcPropertyIndex);
|
|
const bool bCompareAsStrings = SrcEventProperty.IsName();
|
|
const bool bCompareAsFloat = SrcEventProperty.IsDouble();
|
|
|
|
const FEventProperty& DestEventProperty = FEventGraphSample::GetEventPropertyByIndex(DestPropertyIndex);
|
|
check( DestEventProperty.IsBoolean() );
|
|
|
|
// These branches should be gone once template is instantiated.
|
|
if( OpType == EEventCompareOps::Less )
|
|
{
|
|
if( bCompareAsStrings )
|
|
{
|
|
ExecuteOperationInternal( DestPtr->GetChildren(), DestEventProperty, SrcPtr, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Name,EEventCompareOps::Less>(SrcEventProperty.Index) );
|
|
}
|
|
else if( bCompareAsFloat )
|
|
{
|
|
ExecuteOperationInternal( DestPtr->GetChildren(), DestEventProperty, SrcPtr, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Double,EEventCompareOps::Less>(SrcEventProperty.Index) );
|
|
}
|
|
|
|
}
|
|
else if( OpType == EEventCompareOps::Greater )
|
|
{
|
|
if( bCompareAsStrings )
|
|
{
|
|
ExecuteOperationInternal( DestPtr->GetChildren(), DestEventProperty, SrcPtr, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Name,EEventCompareOps::Greater>(SrcEventProperty.Index) );
|
|
}
|
|
else if( bCompareAsFloat )
|
|
{
|
|
ExecuteOperationInternal( DestPtr->GetChildren(), DestEventProperty, SrcPtr, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Double,EEventCompareOps::Greater>(SrcEventProperty.Index) );
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
template < const EEventPropertyTypes PropType, const EEventCompareOps::Type CompareOp >
|
|
static void ExecuteOperationInternal
|
|
(
|
|
TArray< FEventGraphSamplePtr >& DestEvents,
|
|
const FEventProperty& DestEventProperty,
|
|
const FEventGraphSamplePtr& SrcPtr,
|
|
const EventGraphPrivate::TCompareByProperty<PropType,CompareOp>& Comparator
|
|
)
|
|
{
|
|
for( int32 ChildIndex = 0; ChildIndex < DestEvents.Num(); ChildIndex++ )
|
|
{
|
|
FEventGraphSamplePtr& Child = DestEvents[ChildIndex];
|
|
const bool bBooleanState = Comparator( Child, SrcPtr );
|
|
Child->PropertyValueAsBool(DestEventProperty.Index) = bBooleanState;
|
|
ExecuteOperationInternal( Child->GetChildren(), DestEventProperty, SrcPtr, Comparator );
|
|
}
|
|
}
|
|
|
|
static void ExecuteAssignOperation( TArray< FEventGraphSamplePtr >& DestEvents, const FEventProperty& DestEventProperty )
|
|
{
|
|
for( int32 ChildIndex = 0; ChildIndex < DestEvents.Num(); ChildIndex++ )
|
|
{
|
|
FEventGraphSamplePtr& Child = DestEvents[ChildIndex];
|
|
Child->PropertyValueAsBool(DestEventProperty.Index) = true;
|
|
ExecuteAssignOperation( Child->GetChildren(), DestEventProperty );
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Class used to sorting array of events, based on specified property. */
|
|
class FEventArraySorter
|
|
{
|
|
public:
|
|
static void Sort( TArray< FEventGraphSamplePtr >& ChildrenToSort, const FName PropertyName, const EEventCompareOps::Type OpType )
|
|
{
|
|
const FEventProperty& EventProperty = FEventGraphSample::GetEventPropertyByName(PropertyName);
|
|
const bool bCompareAsStrings = EventProperty.IsName();
|
|
const bool bCompareAsFloat = EventProperty.IsDouble();
|
|
|
|
if( OpType == EEventCompareOps::Greater )
|
|
{
|
|
if( bCompareAsStrings )
|
|
{
|
|
SortInternal( ChildrenToSort, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Name,EEventCompareOps::Greater>(EventProperty.Index) );
|
|
}
|
|
else if( bCompareAsFloat )
|
|
{
|
|
SortInternal( ChildrenToSort, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Double,EEventCompareOps::Greater>(EventProperty.Index) );
|
|
}
|
|
}
|
|
else if( OpType == EEventCompareOps::Less )
|
|
{
|
|
if( bCompareAsStrings )
|
|
{
|
|
SortInternal( ChildrenToSort, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Name,EEventCompareOps::Less>(EventProperty.Index) );
|
|
}
|
|
else if( bCompareAsFloat )
|
|
{
|
|
SortInternal( ChildrenToSort, EventGraphPrivate::TCompareByProperty<EEventPropertyTypes::Double,EEventCompareOps::Less>(EventProperty.Index) );
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
template< const EEventPropertyTypes PropType, const EEventCompareOps::Type CompareOp >
|
|
static void SortInternal( TArray< FEventGraphSamplePtr >& ChildrenToSort, const EventGraphPrivate::TCompareByProperty<PropType,CompareOp>& CompareInstance )
|
|
{
|
|
ChildrenToSort.Sort( CompareInstance );
|
|
|
|
for( int32 ChildIndex = 0; ChildIndex < ChildrenToSort.Num(); ChildIndex++ )
|
|
{
|
|
TArray<FEventGraphSamplePtr>& Children = ChildrenToSort[ChildIndex]->GetChildren();
|
|
if( Children.Num() )
|
|
{
|
|
SortInternal( Children, CompareInstance );
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
FEventGraphData related classes
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/** Enumerates event graph types. */
|
|
struct EEventGraphTypes
|
|
{
|
|
enum Type
|
|
{
|
|
/** Per-frame average event graph. */
|
|
Average,
|
|
|
|
/** Highest "per-frame" event graph. */
|
|
Maximum,
|
|
|
|
/** Event graph for one frame, so both average and maximum can be used. */
|
|
OneFrame,
|
|
|
|
/** Selected frames event graph. */
|
|
Total,
|
|
|
|
/** Invalid enum type, may be used as a number of enumerations. */
|
|
InvalidOrMax,
|
|
};
|
|
|
|
/**
|
|
* @param EventGraphType - The value to get the string for.
|
|
*
|
|
* @return string representation of the specified EEventGraphTypes value.
|
|
*/
|
|
static FString ToName( const EEventGraphTypes::Type EventGraphType );
|
|
|
|
/**
|
|
* @param EventGraphType - The value to get the string for.
|
|
*
|
|
* @return string representation with more detailed explanation of the specified EEventGraphTypes value.
|
|
*/
|
|
static FString ToDescription( const EEventGraphTypes::Type EventGraphType );
|
|
};
|
|
|
|
/** Simple struct used to return a result. */
|
|
struct FEventGraphContainer
|
|
{
|
|
/** Initialization constructor. */
|
|
FEventGraphContainer( uint32 InFrameStartIndex, uint32 InFrameEndIndex, const FEventGraphDataRef& InAverage, const FEventGraphDataRef& InMaximum, const FEventGraphDataRef& InTotal )
|
|
: FrameStartIndex( InFrameStartIndex )
|
|
, FrameEndIndex( InFrameEndIndex )
|
|
, Average( InAverage )
|
|
, Maximum( InMaximum )
|
|
, Total( InTotal )
|
|
{}
|
|
|
|
const uint32 FrameStartIndex;
|
|
const uint32 FrameEndIndex;
|
|
FEventGraphDataRef Average;
|
|
FEventGraphDataRef Maximum;
|
|
FEventGraphDataRef Total;
|
|
};
|
|
|
|
/**
|
|
* Provides access only to the profiler samples specified by a frame index or frame indices.
|
|
* This class allows accessing root and child samples which may be used to create an event graph.
|
|
*/
|
|
class FEventGraphData : public FNoncopyable
|
|
{
|
|
friend class FProfilerSession;
|
|
|
|
public:
|
|
/** Minimal default constructor. */
|
|
FEventGraphData();
|
|
|
|
/** Destructor, for debugging purpose only. */
|
|
FORCEINLINE_DEBUGGABLE ~FEventGraphData()
|
|
{}
|
|
|
|
/** Copy constructor, create a full duplication of the source event graph data. */
|
|
FEventGraphData(const FEventGraphData& Source);
|
|
|
|
protected:
|
|
/**
|
|
* Initialization constructor, hidden on purpose, may be only called from the FProfilerSession class.
|
|
*
|
|
* @param InProfilerSession - a const pointer to the profiler session that will be used to generate this event graph data
|
|
* @param InFrameIndex - the frame number from which to generate this event graph data
|
|
*
|
|
*/
|
|
FEventGraphData( const FProfilerSession * const InProfilerSession, const uint32 InFrameIndex );
|
|
|
|
/**
|
|
* Recursively populates the hierarchy of the event graph samples.
|
|
*
|
|
* @param ParentEventGraphSample - <describe ParentEventGraphSample>
|
|
* @param ParentProfilerSample - <describe ParentProfilerSample>
|
|
* @param DataProvider - <describe DataProvider>
|
|
* @param FrameDurationMS - <describe FrameDurationMS>
|
|
* @param ThreadName - <describe ThreadName>
|
|
* @param ThreadDurationMS - <describe ThreadDurationMS>
|
|
*
|
|
*/
|
|
void PopulateHierarchy_Recurrent
|
|
(
|
|
const FProfilerSession * const ProfilerSession,
|
|
const FEventGraphSamplePtr ParentEvent,
|
|
const FProfilerSample& ParentSample,
|
|
const TSharedRef<IDataProvider> DataProvider
|
|
);
|
|
|
|
public:
|
|
|
|
/**
|
|
* @return a duplicated instance of this event graph, this is a deep duplication, which means that for all events a new event is created
|
|
*/
|
|
FEventGraphDataRef DuplicateAsRef();
|
|
|
|
/** Combines current event graph with the second one. */
|
|
void Combine( const FEventGraphData& Other );
|
|
|
|
/** Updates data to get per-frame average event graph. */
|
|
void SetAsAverage();
|
|
|
|
/** Updates data to get highest "per-frame" event graph. */
|
|
void SetAsMaximim();
|
|
|
|
/** Finalizes current event graph. */
|
|
void Finalize( const uint32 InFrameStartIndex, const uint32 InFrameEndIndex );
|
|
|
|
/**
|
|
* @return root event that contains all thread root events and its children
|
|
*/
|
|
FORCEINLINE const FEventGraphSamplePtr& GetRoot() const
|
|
{
|
|
return RootEvent;
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* @return the frame start index this event graph data was generated from.
|
|
*/
|
|
FORCEINLINE const uint32 GetFrameStartIndex() const
|
|
{
|
|
return FrameStartIndex;
|
|
}
|
|
|
|
/**
|
|
* @return the frame end index this event graph data was generated from.
|
|
*/
|
|
FORCEINLINE const uint32 GetFrameEndIndex() const
|
|
{
|
|
return FrameEndIndex;
|
|
}
|
|
|
|
/**
|
|
* @return the number of frames used to create this event graph data.
|
|
*/
|
|
const uint32 GetNumFrames() const
|
|
{
|
|
return FrameEndIndex - FrameStartIndex;
|
|
}
|
|
|
|
/**
|
|
* @return the description for this event graph data.
|
|
*/
|
|
FORCEINLINE const FString& GetDescription() const
|
|
{
|
|
return Description;
|
|
}
|
|
|
|
protected:
|
|
// TODO: FEventGraphSamplePtr -> FEventGraphSampleRef
|
|
|
|
/** Root sample, contains all thread samples and its children. */
|
|
FEventGraphSamplePtr RootEvent;
|
|
|
|
/** Description as "SessionName - FrameIndex/Indices". */
|
|
FString Description;
|
|
|
|
/** The frame start index this event graph data was generated from. */
|
|
uint32 FrameStartIndex;
|
|
|
|
/** The frame end index this event graph data was generated from. */
|
|
uint32 FrameEndIndex;
|
|
};
|
|
|
|
//}//namespace FEventGraphSample
|