UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
StatsData.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Array.h"
7#include "Containers/Map.h"
8#include "Containers/Set.h"
11#include "CoreTypes.h"
12#include "Delegates/Delegate.h"
13#include "HAL/PlatformCrt.h"
14#include "HAL/PlatformMemory.h"
16#include "Math/NumericLimits.h"
19#include "Misc/Build.h"
20#include "Misc/Parse.h"
21#include "Stats/Stats.h"
22#include "Stats/StatsCommon.h"
24#include "UObject/NameTypes.h"
25#include "UObject/UnrealNames.h"
26
27struct FEventData;
28
29#if STATS
30
34enum
35{
37 MAX_STAT_LAG = 4,
38};
39
41struct FStatConstants
42{
44 static CORE_API const FName NAME_ThreadRoot;
45
47 static CORE_API const char* ThreadGroupName;
48 static CORE_API const FName NAME_ThreadGroup;
49
52
54 static CORE_API const FName NAME_NoCategory;
55
57 static CORE_API const FString StatsFileExtension;
58
60 static CORE_API const FString StatsFileRawExtension;
61
63 static CORE_API const FString ThreadNameMarker;
64
67
70
72 static CORE_API const FName RAW_NamedMarker;
73
75 static CORE_API const FStatNameAndInfo AdvanceFrame;
76};
77
81template <typename T>
82void ParseTypedValue( const TCHAR* Stream, const TCHAR* Match, T& Out )
83{
84 TCHAR Temp[64] = TEXT( "" );
85 if( FParse::Value( Stream, Match, Temp, UE_ARRAY_COUNT( Temp ) ) )
86 {
87 LexFromString( Out, Temp );
88 }
89}
90
95template<typename T>
97{
98public:
99 TParsedValueWithDefault( const TCHAR* Stream, const TCHAR* Match, const T& Default )
100 : Value( Default )
101 {
102 if (Stream && Match)
103 {
105 }
106 }
107
108 const T& Get() const
109 {
110 return Value;
111 }
112
113 void Set( const T& NewValue )
114 {
115 Value = NewValue;
116 }
117
118private:
119 T Value;
120};
121
123struct EStatCompareBy
124{
125 enum Type
126 {
128 Name,
130 CallCount,
132 Sum,
133 };
134};
135
137struct EStatDisplayMode
138{
139 enum Type
140 {
141 Invalid = 0x0,
142 Hierarchical = 0x1,
143 Flat = 0x2,
144 };
145};
146
148template< typename T >
150{
151 inline bool operator()( const T& A, const T& B ) const
152 {
153 check(0);
154 return 0;
155 }
156};
157
159template< typename T >
161{
162 inline bool operator()( const T& A, const T& B ) const
163 {
164 check(0);
165 return 0;
166 }
167};
168
170template< typename T >
172{
173 inline bool operator()( const T& A, const T& B ) const
174 {
175 check(0);
176 return 0;
177 }
178};
179
181template<>
183{
184 UE_FORCEINLINE_HINT bool operator()( const FStatMessage& A, const FStatMessage& B ) const
185 {
186 return A.NameAndInfo.GetRawName().Compare( B.NameAndInfo.GetRawName() ) < 0;
187 }
188};
189
191template<>
193{
194 inline bool operator()( const FStatMessage& A, const FStatMessage& B ) const
195 {
196 const uint32 DurationA = FromPackedCallCountDuration_Duration( A.GetValue_int64() );
197 const uint32 DurationB = FromPackedCallCountDuration_Duration( B.GetValue_int64() );
199 }
200};
201
203template<>
205{
206 inline bool operator()( const FStatMessage& A, const FStatMessage& B ) const
207 {
208 const uint32 CallCountA = FromPackedCallCountDuration_CallCount( A.GetValue_int64() );
209 const uint32 CallCountB = FromPackedCallCountDuration_CallCount( B.GetValue_int64() );
211 }
212};
213
217//@todo can we just use TIndirectArray here?
218struct FStatPacketArray
219{
220 TArray<FStatPacket*> Packets;
221
223 {
224 Empty();
225 }
226
228 CORE_API void Empty();
229
231 {
232 Packets.Empty();
233 }
234};
235
240{
242 FStatMessage Meta; // includes aggregated inclusive time and call counts packed into the int64
243
246
249 : Meta(FStatConstants::NAME_ThreadRoot, EStatDataType::ST_None, nullptr, nullptr, nullptr, false, false, false)
250 {
251 }
252
255
257 explicit FRawStatStackNode(FStatMessage const& InMeta)
258 : Meta(InMeta)
259 {
260 }
261
263 FRawStatStackNode& operator=(const FRawStatStackNode& Other)
264 {
265 if( this != &Other )
266 {
268 Meta = Other.Meta;
269
270 Children.Empty(Other.Children.Num());
271 for (TMap<FName, FRawStatStackNode*>::TConstIterator It(Other.Children); It; ++It)
272 {
273 Children.Add(It.Key(), new FRawStatStackNode(*It.Value()));
274 }
275 }
276 return *this;
277 }
278
281 {
283 }
284
287 CORE_API void Divide(uint32 Div);
288
291
294
297
299 CORE_API void AddSelf();
300
302 CORE_API void DebugPrint(TCHAR const* Filter = nullptr, int32 MaxDepth = MAX_int32, int32 Depth = 0) const;
303
305 CORE_API void DebugPrintLeafFilter(TCHAR const* Filter) const;
306
309
312
314 CORE_API int64 ChildCycles() const;
315
317 template< class TComparer >
318 void Sort( const TComparer& Comparer )
319 {
320 Children.ValueSort( Comparer );
321 for( auto It = Children.CreateIterator(); It; ++It )
322 {
323 FRawStatStackNode* Child = It.Value();
324 Child->Sort( Comparer );
325 }
326 }
327
329 CORE_API void* operator new(size_t Size);
330 CORE_API void operator delete(void *RawMemory);
331
332protected:
334 {
335 for (TMap<FName, FRawStatStackNode*>::TIterator It(Children); It; ++It)
336 {
337 delete It.Value();
338 }
339 }
340};
341
343template<>
345{
346 UE_FORCEINLINE_HINT bool operator()( const FRawStatStackNode& A, const FRawStatStackNode& B ) const
347 {
348 return A.Meta.NameAndInfo.GetRawName().Compare( B.Meta.NameAndInfo.GetRawName() ) < 0;
349 }
350};
351
353template<>
355{
356 inline bool operator()( const FRawStatStackNode& A, const FRawStatStackNode& B ) const
357 {
358 const uint32 DurationA = FromPackedCallCountDuration_Duration( A.Meta.GetValue_int64() );
359 const uint32 DurationB = FromPackedCallCountDuration_Duration( B.Meta.GetValue_int64() );
361 }
362};
363
365template<>
367{
368 inline bool operator()( const FRawStatStackNode& A, const FRawStatStackNode& B ) const
369 {
370 const uint32 CallCountA = FromPackedCallCountDuration_CallCount( A.Meta.GetValue_int64() );
371 const uint32 CallCountB = FromPackedCallCountDuration_CallCount( B.Meta.GetValue_int64() );
373 }
374};
375
378{
381
384
387 {}
388
391
394
396 {
398
399 ComplexStat = Other.Meta;
400
401 Children.Empty(Other.Children.Num());
402 for (auto It = Other.Children.CreateConstIterator(); It; ++It)
403 {
404 Children.Add(It.Key(), new FComplexRawStatStackNode(*It.Value()));
405 }
406 }
407
408public:
411 {
413 }
414
417
419 void Divide(uint32 Div);
420
423
426
429
431 void* operator new(size_t Size);
432 void operator delete(void *RawMemory);
433
434protected:
436 {
437 for (auto It = Children.CreateIterator(); It; ++It)
438 {
439 delete It.Value();
440 }
441 }
442};
443
444
446struct FEventData
447{
449 FEventData()
450 : Frame( 0 )
451 , Duration( 0 )
452 , DurationMS( 0.0f )
453 {}
454
458 bool HasValidStacks() const
459 {
460 return WaitStackStats.Num() > 0 && TriggerStackStats.Num() > 0;
461 }
462
468 int64 Frame;
472 float DurationMS;
473};
474
475//@todo split header
476
480struct IItemFilter
481{
482 virtual ~IItemFilter() { }
483
485 virtual bool Keep(FStatMessage const& Item) = 0;
486};
487
488
489// #Stats: 2014-03-21 Move metadata functionality into a separate class
490//
496{
497 friend class FStatsThread;
498 friend struct FStatPacketArray;
499 friend struct FStatsReadFile;
500
503
506
509
512
513public:
515
518
521 {
524 }
525
526private:
529
532
535
537 CORE_API void ResetRawStats();
538
541
544
547
548protected:
550 CORE_API void FindOrAddMetaData(FStatMessage const& Item);
551
554
557
559 CORE_API int64 GetFastThreadFrameTimeInternal(int64 TargetFrame, int32 ThreadID, EThreadType::Type Thread) const;
560
563
566
569
572
575
578
581
584
587
590
593
594public:
595
598
601
604
607
610
612
615
618
621
624
627
630
633
636
639
641 bool IsFrameValid(int64 Frame) const
642 {
643 return GoodFrames.Contains(Frame);
644 }
645
649
651 CORE_API FName GetStatThreadName( const FStatPacket& Packet ) const;
652
655
658 {
660 const FStatPacketArray& Frame = History.FindChecked( TargetFrame );
661 return Frame;
662 }
663
666
669
672
675
678
681
684
687};
688
689//@todo split header
690
694struct FStatsUtils
695{
697 static CORE_API void DivideStat(FStatMessage& Dest, uint32 Div);
698
701
704
706 static CORE_API void DivideStatArray(TArray<FStatMessage>& Dest, uint32 Div);
707
709 static CORE_API void AccumulateStat(FStatMessage& Dest, FStatMessage const& Item, EStatOperation::Type Op = EStatOperation::Invalid, bool bAllowNameMismatch = false);
710
712 static void AddNonStackStats( const FName LongName, const FStatMessage& Item, const EStatOperation::Type Op, TMap<FName, FStatMessage>& out_NonStackStats )
713 {
714 if (
715 Item.NameAndInfo.GetField<EStatDataType>() != EStatDataType::ST_None &&
716 Item.NameAndInfo.GetField<EStatDataType>() != EStatDataType::ST_FName &&
717 (Op == EStatOperation::Set ||
718 Op == EStatOperation::Clear ||
719 Op == EStatOperation::Add ||
720 Op == EStatOperation::Subtract ||
721 Op == EStatOperation::MaxVal))
722 {
724 if (!Result)
725 {
726 Result = &out_NonStackStats.Add(LongName, Item);
727 Result->NameAndInfo.SetField<EStatOperation>(EStatOperation::Set);
728 Result->Clear();
729 }
730 FStatsUtils::AccumulateStat(*Result, Item);
731 }
732 }
733
735 static CORE_API FString DebugPrint(FStatMessage const& Item);
736
738 static CORE_API void GetNameAndGroup(FStatMessage const& Item, FString& OutName, FString& OutGroup);
739
742 {
743 checkStats(ScopeStart.NameAndInfo.GetField<EStatOperation>() == EStatOperation::CycleScopeStart);
744 checkStats(ScopeEnd.NameAndInfo.GetField<EStatOperation>() == EStatOperation::CycleScopeEnd);
746 Result.NameAndInfo.SetField<EStatOperation>(EStatOperation::Set);
747 Result.NameAndInfo.SetFlag(EStatMetaFlags::IsPackedCCAndDuration, true);
748 checkStats(ScopeEnd.NameAndInfo.GetFlag(EStatMetaFlags::IsCycle));
749
750 // cycles can wrap and are actually uint32's so we do something special here
751 int64 Delta = int32(uint32(ScopeEnd.GetValue_int64()) - uint32(ScopeStart.GetValue_int64()));
752 checkStats(Delta >= 0);
753 Result.GetValue_int64() = ToPackedCallCountDuration(1, uint32(Delta));
754 return Result;
755 }
756
758 static void StatOpMaxVal_Int64( const FStatNameAndInfo& DestNameAndInfo, int64& Dest, const int64& Other )
759 {
760 if (DestNameAndInfo.GetFlag(EStatMetaFlags::IsPackedCCAndDuration))
761 {
765 }
766 else
767 {
768 Dest = FMath::Max<int64>(Dest,Other);
769 }
770 }
772 static void StatOpMinVal_Int64(const FStatNameAndInfo& DestNameAndInfo, int64& Dest, const int64& Other)
773 {
774 if (DestNameAndInfo.GetFlag(EStatMetaFlags::IsPackedCCAndDuration))
775 {
779 }
780 else
781 {
782 Dest = FMath::Min<int64>(Dest, Other);
783 }
784 }
785
788 static CORE_API FString FromEscapedString(const TCHAR* Escaped);
789
790 static FString BuildUniqueThreadName( uint32 InThreadID )
791 {
792 // Make unique name.
793 return FString::Printf( TEXT( "%s%x_0" ), *FStatConstants::ThreadNameMarker, InThreadID );
794 }
795
796 static int32 ParseThreadID( const FString& InThreadName, FString* out_ThreadName = nullptr )
797 {
798 // Extract the thread id.
799 const FString ThreadName = InThreadName.Replace( TEXT( "_0" ), TEXT( "" ) );
800 int32 Index = 0;
801 ThreadName.FindLastChar(TEXT('_'),Index);
802
803 // Parse the thread name if requested.
804 if( out_ThreadName )
805 {
806 *out_ThreadName = ThreadName.Left( Index );
807 }
808
809 const FString ThreadIDStr = ThreadName.RightChop(Index+1);
810 const uint32 ThreadID = FParse::HexNumber( *ThreadIDStr );
811
812 return ThreadID;
813 }
814};
815
820{
822 static CORE_API void AddAndMinMax( FComplexStatMessage& Dest, const FStatMessage& Item, EComplexStatField::Type SumIndex, EComplexStatField::Type MaxIndex, EComplexStatField::Type MinIndex);
823
825 static CORE_API void DivideStat( FComplexStatMessage& Dest, uint32 Div, EComplexStatField::Type SumIndex, EComplexStatField::Type DestIndex );
826
828 static CORE_API void MergeAddAndMinMaxArray( TArray<FComplexStatMessage>& Dest, const TArray<FStatMessage>& Source, EComplexStatField::Type SumIndex, EComplexStatField::Type MaxIndex, EComplexStatField::Type MinIndex);
829
831 static CORE_API void DiviveStatArray( TArray<FComplexStatMessage>& Dest, uint32 Div, EComplexStatField::Type SumIndex, EComplexStatField::Type DestIndex );
832};
833
834//@todo split header
835
838{
841
844
847
849 TArray<int32> Indentation;
850
853
856
859
862
865};
866
871{
876 {}
877
879 const FComplexStatMessage* GetStatData(const FName& StatName ) const
880 {
881 return NameToStatMap.FindRef(StatName);
882 }
883
885 TArray<FName> GroupNames;
889 FString RootFilter;
890
892
894 const bool bDrawOnlyRawStats;
895
897 const bool bRenderStats;
898};
899
904{
906
908 : Latest(nullptr)
909 {
910 }
911
913 {
914 delete Latest;
915 Latest = nullptr;
916 }
917
918 void NewData(FGameThreadStatsData* Data);
919
921};
922
927{
928public:
930
931 void NewData(FStatNameAndInfo NameAndInfo)
932 {
934 const FName GroupName = NameAndInfo.GetGroupName();
935 if (!GroupName.IsNone() && GroupName != NAME_Groups)
936 {
937 StatGroupNames.Add(GroupName);
938 }
939 }
940
941 void SendData()
942 {
943 if (NameAndInfos.Num() > 0)
944 {
945 // Should only do this if the delegate has been registered!
946 check(NewStatGroupDelegate.IsBound());
948 ClearData();
949 }
950 }
951
952 void ClearData()
953 {
954 NameAndInfos.Empty();
955 }
956
960
963
964private:
967
970};
971
972#endif
#define check(expr)
Definition AssertionMacros.h:314
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::TCHAR TCHAR
Either ANSICHAR or WIDECHAR, depending on whether the platform supports wide characters or the requir...
Definition Platform.h:1135
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#define UE_FORCEINLINE_HINT
Definition Platform.h:723
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
void Sort(T *First, const int32 Num, const PREDICATE_CLASS &Predicate)
Definition Sorting.h:78
#define DECLARE_DELEGATE_OneParam(DelegateName, Param1Type)
Definition DelegateCombinations.h:48
#define DECLARE_TS_MULTICAST_DELEGATE_OneParam(DelegateName, Param1Type)
Definition DelegateCombinations.h:50
void LexFromString(EAudioFeature &OutFeature, const TCHAR *String)
Definition IOSAppDelegate.cpp:163
#define MAX_int32
Definition NumericLimits.h:25
#define checkStats(x)
Definition StatsCommon.h:11
#define UE_ARRAY_COUNT(array)
Definition UnrealTemplate.h:212
uint32 Size
Definition VulkanMemory.cpp:4034
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition NameTypes.h:617
FORCEINLINE bool IsNone() const
Definition NameTypes.h:827
Definition ThreadSafeCounter.h:14
Definition Array.h:670
void Empty(SizeType Slack=0)
Definition Array.h:2273
Definition IndirectArray.h:20
Definition UnrealString.h.inl:34
int32 Find(CharRangeType &&SubStr, ESearchCase::Type SearchCase=ESearchCase::IgnoreCase, ESearchDir::Type SearchDir=ESearchDir::FromStart, int32 StartPosition=INDEX_NONE) const
Definition UnrealString.h.inl:1116
@ Hierarchical
Definition NavigationSystemTypes.h:89
Type
Definition PawnAction_Move.h:11
FORCEINLINE T * Get(const FObjectPtr &ObjectPtr)
Definition ObjectPtr.h:426
float Encode(EEncoding SourceEncoding, float Value)
Definition TransferFunctions.cpp:51
UE_STRING_CLASS Result(Forward< LhsType >(Lhs), RhsLen)
Definition String.cpp.inl:732
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71
static CORE_API uint32 HexNumber(FStringView HexString)
Definition Parse.cpp:1342
static CORE_API bool Value(const TCHAR *Stream, const TCHAR *Match, FName &Name)
Definition Parse.cpp:584