UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
StatsSystemTypes.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Misc/Build.h"
6
7// Note: it would be nice to only include these when STATS is defined but many other files
8// currently rely on these includes indirectly.
9#include "Containers/Array.h"
14#include "CoreGlobals.h"
15#include "CoreTypes.h"
16#include "Delegates/Delegate.h"
18#include "HAL/PlatformCrt.h"
19#include "HAL/PlatformMemory.h"
20#include "HAL/PlatformMisc.h"
21#include "HAL/PlatformTLS.h"
22#include "HAL/PlatformTime.h"
24#include "HAL/UnrealMemory.h"
25#include "Math/Color.h"
26#include "Math/NumericLimits.h"
28#include "Misc/CString.h"
29#include "Misc/EnumClassFlags.h"
30#include "Misc/SourceLocation.h"
34#include "Stats/StatsCommon.h"
35#include "Stats/StatsTrace.h"
36#include "Templates/Atomic.h"
39#include "Templates/UniquePtr.h"
43#include "Trace/Trace.h"
44#include "UObject/NameTypes.h"
45#include "UObject/UnrealNames.h"
46
47#if STATS
48
49class FThreadStats;
50template <typename T> struct TIsPODType;
51
52struct TStatIdData
53{
54 bool IsNone() const
55 {
57 return LocalName.IsNone();
58 }
59
60 TStatIdData() = default;
61
62 TStatIdData(FMinimalName InName) : Name(InName)
63 {
64 }
65
68
71
74};
75
76struct TStatId
77{
80 {
81 }
84 {
85 }
87 {
88 return !IsNone();
89 }
90 UE_FORCEINLINE_HINT bool IsNone() const
91 {
92 return StatIdPtr->IsNone();
93 }
95 {
96 return StatIdPtr;
97 }
98
99 UE_FORCEINLINE_HINT FMinimalName GetMinimalName(EMemoryOrder MemoryOrder) const
100 {
101 return StatIdPtr->Name.Load(MemoryOrder);
102 }
103
105 {
106 return MinimalNameToName(StatIdPtr->Name);
107 }
108
110 {
111 return TStatId_NAME_None;
112 }
113
120 {
121 return StatIdPtr->StatDescriptionAnsi.Get();
122 }
123
130 {
131 return StatIdPtr->StatDescriptionWide.Get();
132 }
133
135 {
136 return StatIdPtr == Other.StatIdPtr;
137 }
138
140 {
141 return StatIdPtr != Other.StatIdPtr;
142 }
143
144 friend uint32 GetTypeHash(TStatId StatId)
145 {
146 return GetTypeHash(StatId.StatIdPtr);
147 }
148
150 {
151 Closed = Open;
152 }
153
154private:
157
167 TStatIdData const* StatIdPtr;
168};
169
174namespace EThreadType
175{
176 enum Type
177 {
178 Invalid,
179 Game,
180 Renderer,
181 EndOfPipe,
182 Other,
183 };
184}
185
186
190struct EStatDataType
191{
192 enum Type
193 {
194 Invalid,
196 ST_None,
198 ST_int64,
200 ST_double,
202 ST_FName,
204 ST_Ptr,
205
206 Num,
207 Mask = 0x7,
208 Shift = 0,
209 NumBits = 3
210 };
211};
212
216struct EStatOperation
217{
218 enum Type
219 {
220 Invalid,
236 Set,
238 Clear,
240 Add,
242 Subtract,
243
244 // these are special ones for processed data
247 Leaf,
248 MaxVal,
249
251 Memory UE_DEPRECATED(5.3, "Use Trace/MemoryInsights and/or LLM for memory profiling."),
252
253 Num,
254 Mask = 0xf,
255 Shift = EStatDataType::Shift + EStatDataType::NumBits,
256 NumBits = 4
257 };
258};
259
263struct EStatMetaFlags
264{
265 enum Type
266 {
267 Invalid = 0x00,
269 DummyAlwaysOne = 0x01,
271 IsGPU = 0x02,
273 IsCycle = 0x04,
275 IsMemory = 0x08,
281 SendingFName = 0x40,
282
283 Num = 0x80,
284 Mask = 0xff,
285 Shift = EStatOperation::Shift + EStatOperation::NumBits,
286 NumBits = 8
287 };
288};
289
290//@todo merge these two after we have redone the user end
294struct EMemoryRegion
295{
296 enum Type
297 {
299
301 Mask = 0xf,
302 Shift = EStatMetaFlags::Shift + EStatMetaFlags::NumBits,
303 NumBits = 4
304 };
305 static_assert(FPlatformMemory::MCR_MAX < (1 << NumBits), "Need to expand memory region field.");
306};
307
309enum class UE_DEPRECATED(5.3, "Use Trace/MemoryInsights and/or LLM for memory profiling.") EMemoryOperation : uint8
310{
312 Invalid,
314 Alloc,
316 Free,
318 Realloc,
319
320 Num,
321 Mask = 0x7,
323 NumBits = 3,
324};
325
329namespace EStatAllFields
330{
331 enum Type
332 {
333 NumBits = EMemoryRegion::Shift + EMemoryRegion::NumBits,
334 StartShift = 28 - NumBits,
335 };
336}
337
338static_assert(EStatAllFields::StartShift > 0, "Too many stat fields.");
339
341{
342 return (int64(CallCount) << 32) | Duration;
343}
344
346{
347 return uint32(Both >> 32);
348}
349
351{
352 return uint32(Both & MAX_uint32);
353}
354
359{
365
366public:
367 static CORE_API const char* GpuStatCategory;
368
370 {
371 }
372
377 : Index(Other.GetComparisonIndex())
378 , Number(Other.GetNumber())
379 {
380 if (!bAlreadyHasMeta)
381 {
382 // ok, you can't have numbered stat FNames too large
383 checkStats(!(Number >> EStatAllFields::StartShift));
384 Number |= EStatMetaFlags::DummyAlwaysOne << (EStatMetaFlags::Shift + EStatAllFields::StartShift);
385 }
387 }
388
393 {
395 Index = LongName.GetComparisonIndex();
396 Number = LongName.GetNumber();
397
398 // ok, you can't have numbered stat FNames too large
399 checkStats(!(Number >> EStatAllFields::StartShift));
400 Number |= EStatMetaFlags::DummyAlwaysOne << (EStatMetaFlags::Shift + EStatAllFields::StartShift);
401
403 SetFlag(EStatMetaFlags::ShouldClearEveryFrame, bShouldClearEveryFrame);
404 SetFlag(EStatMetaFlags::IsCycle, bCycleStat);
406 {
407 SetFlag(EStatMetaFlags::IsMemory, true);
408 SetField<EMemoryRegion>(EMemoryRegion::Type(MemoryRegion));
409 }
410
412 {
413 SetFlag(EStatMetaFlags::IsGPU, true);
414 }
415
417 }
418
423 {
425 }
426
430 inline int32 GetRawNumber() const
431 {
433 return Number;
434 }
435
439 inline void SetRawName(FName RawName)
440 {
441 // ok, you can't have numbered stat FNames too large
442 checkStats(!(RawName.GetNumber() >> EStatAllFields::StartShift));
445 LocalNumber &= ~((1 << EStatAllFields::StartShift) - 1);
446 Index = RawName.GetComparisonIndex();
447 Number = (LocalNumber | RawName.GetNumber());
448 }
449
454 inline FName GetRawName() const
455 {
457 return FName(Index, Index, Number & ((1 << EStatAllFields::StartShift) - 1));
458 }
459
464 inline FName GetEncodedName() const
465 {
467 return FName(Index, Index, Number);
468 }
469
473 inline FName GetShortName() const
474 {
477 }
478
482 inline FName GetGroupName() const
483 {
486 }
487
491 inline FName GetGroupCategory() const
492 {
495 }
496
500 inline FString GetDescription() const
501 {
504 }
508 inline bool GetSortByName() const
509 {
512 }
513
514
518 inline void CheckInvariants() const
519 {
520 checkStats((Number & (EStatMetaFlags::DummyAlwaysOne << (EStatAllFields::StartShift + EStatMetaFlags::Shift)))
521 && Index);
522 }
523
528 template<typename TField>
529 typename TField::Type GetField() const
530 {
533 LocalNumber = (LocalNumber >> (EStatAllFields::StartShift + TField::Shift)) & TField::Mask;
534 checkStats(LocalNumber != TField::Invalid && LocalNumber < TField::Num);
535 return typename TField::Type(LocalNumber);
536 }
537
542 template<typename TField>
543 void SetField(typename TField::Type Value)
544 {
547 checkStats(Value < TField::Num && Value != TField::Invalid);
548 LocalNumber &= ~(TField::Mask << (EStatAllFields::StartShift + TField::Shift));
549 LocalNumber |= Value << (EStatAllFields::StartShift + TField::Shift);
552 }
553
558 bool GetFlag(EStatMetaFlags::Type Bit) const
559 {
562 checkStats(Bit < EStatMetaFlags::Num && Bit != EStatMetaFlags::Invalid);
563 return !!((LocalNumber >> (EStatAllFields::StartShift + EStatMetaFlags::Shift)) & Bit);
564 }
565
571 void SetFlag(EStatMetaFlags::Type Bit, bool Value)
572 {
575 checkStats(Bit < EStatMetaFlags::Num && Bit != EStatMetaFlags::Invalid);
576 if (Value)
577 {
578 LocalNumber |= (Bit << (EStatAllFields::StartShift + EStatMetaFlags::Shift));
579 }
580 else
581 {
582 LocalNumber &= ~(Bit << (EStatAllFields::StartShift + EStatMetaFlags::Shift));
583 }
586 }
587
588
598 CORE_API static FName ToLongName(FName InStatName, char const* InGroup, char const* InCategory, TCHAR const* InDescription, bool InSortByName);
604
605};
606
607
609union UStatData
610{
611private:
613 double Float;
615 int64 Cycles;
617 uint64 Ptr;
621 CORE_API const FString GetName() const
622 {
623 return FName::SafeString(FNameEntryId::FromUnstableInt(static_cast<uint32>(Cycles)));
624 }
625};
626
630struct FStatMessage
631{
635 enum
636 {
637 DATA_SIZE = 8,
638 DATA_ALIGN = 8,
639 };
640 union
641 {
642#if UE_BUILD_DEBUG
644#endif // UE_BUILD_DEBUG
646 };
647
652
654 {
655 }
656
662 {
663 NameAndInfo.SetField<EStatOperation>(EStatOperation::SetLongName);
664 }
665
668 {
669 }
670
674 inline FStatMessage(FName InStatName, EStatOperation::Type InStatOperation)
676 {
678 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
679 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) == true);
680
681 // these branches are FORCEINLINE_STATS of constants in almost all cases, so they disappear
682 if (InStatOperation == EStatOperation::CycleScopeStart || InStatOperation == EStatOperation::CycleScopeEnd)
683 {
685 }
686 else
687 {
688 checkStats(0);
689 }
690 }
691
695 inline FStatMessage(FName InStatName, EStatOperation::Type InStatOperation, int64 Value, bool bIsCycle)
697 {
699 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
700 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) == bIsCycle);
702 }
703
707 inline FStatMessage(FName InStatName, EStatOperation::Type InStatOperation, double Value, bool)
709 {
711 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_double);
712 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) == false);
714 }
715
719 inline FStatMessage(FName InStatName, EStatOperation::Type InStatOperation, FName Value, bool)
721 {
723 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_FName);
724 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) == false);
726 }
727
731 inline FStatMessage(FName InStatName, EStatOperation::Type InStatOperation, uint64 Value, bool)
733 {
735 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_Ptr);
736 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) == false);
738 }
739
743 inline void Clear()
744 {
745 static_assert(sizeof(uint64) == DATA_SIZE, "Bad clear.");
746 *(int64*)&StatData = 0;
747 }
748
752 inline int64& GetValue_int64()
753 {
754 static_assert(sizeof(int64) <= DATA_SIZE && alignof(int64) <= DATA_ALIGN, "Bad data for stat message.");
755 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
756 return *(int64*)&StatData;
757 }
758 inline int64 GetValue_int64() const
759 {
760 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
761 return *(int64 const*)&StatData;
762 }
763
764 inline uint64& GetValue_Ptr()
765 {
766 static_assert(sizeof(uint64) <= DATA_SIZE && alignof(uint64) <= DATA_ALIGN, "Bad data for stat message.");
767 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_Ptr);
768 return *(uint64*)&StatData;
769 }
770 inline uint64 GetValue_Ptr() const
771 {
772 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_Ptr);
773 return *(uint64 const*)&StatData;
774 }
775
776 inline int64 GetValue_Duration() const
777 {
778 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) && NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
779 if (NameAndInfo.GetFlag(EStatMetaFlags::IsPackedCCAndDuration))
780 {
782 }
783 return *(int64 const*)&StatData;
784 }
785
786 inline uint32 GetValue_CallCount() const
787 {
788 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsPackedCCAndDuration) && NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) && NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
790 }
791
792 inline double& GetValue_double()
793 {
794 static_assert(sizeof(double) <= DATA_SIZE && alignof(double) <= DATA_ALIGN, "Bad data for stat message.");
795 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_double);
796 return *(double*)&StatData;
797 }
798
799 inline double GetValue_double() const
800 {
801 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_double);
802 return *(double const*)&StatData;
803 }
804
806 {
807 static_assert(sizeof(FMinimalName) <= DATA_SIZE && alignof(FMinimalName) <= DATA_ALIGN, "Bad data for stat message.");
808 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_FName);
809 return *(FMinimalName*)&StatData;
810 }
811
813 {
814 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_FName);
815 return *(FMinimalName const*)&StatData;
816 }
817
818 inline FName GetValue_FName() const
819 {
820 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_FName);
821 return MinimalNameToName(*(FMinimalName const*)&StatData);
822 }
823};
824template<> struct TIsPODType<FStatMessage> { enum { Value = true }; };
825
827
831template< typename TEnum >
832struct TStatMessage
833{
834 typedef TEnum TStructEnum;
835 static constexpr int32 EnumCount = TEnum::Num;
836
840 enum
841 {
842 DATA_SIZE = 8 * EnumCount,
843 DATA_ALIGN = 8,
844 };
845
846 union
847 {
848#if UE_BUILD_DEBUG
850#endif // UE_BUILD_DEBUG
852 };
853
858
860 {}
861
865 explicit inline TStatMessage(const FStatMessage& Other)
867 {
868 // Reset data type and clear all fields.
869 NameAndInfo.SetField<EStatDataType>(EStatDataType::ST_None);
870 Clear();
871 }
872
874 TStatMessage& operator=(const FStatMessage& Other)
875 {
876 NameAndInfo = Other.NameAndInfo;
877 // Reset data type and clear all fields.
878 NameAndInfo.SetField<EStatDataType>(EStatDataType::ST_None);
879 Clear();
880 return *this;
881 }
882
884 void FixStatData(const EStatDataType::Type NewType)
885 {
886 const EStatDataType::Type OldType = NameAndInfo.GetField<EStatDataType>();
887 if (OldType != NewType)
888 {
889 // Convert from the old type to the new type.
890 if (OldType == EStatDataType::ST_int64 && NewType == EStatDataType::ST_double)
891 {
892 // Get old values.
893 int64 OldValues[TEnum::Num];
894 for (int32 FieldIndex = 0; FieldIndex < EnumCount; ++FieldIndex)
895 {
896 OldValues[FieldIndex] = GetValue_int64((typename TEnum::Type)FieldIndex);
897 }
898
899 // Set new stat data type
900 NameAndInfo.SetField<EStatDataType>(NewType);
901 for (int32 FieldIndex = 0; FieldIndex < EnumCount; ++FieldIndex)
902 {
903 GetValue_double((typename TEnum::Type)FieldIndex) = (double)OldValues[FieldIndex];
904 }
905 }
906 else if (OldType == EStatDataType::ST_double && NewType == EStatDataType::ST_int64)
907 {
908 // Get old values.
909 double OldValues[TEnum::Num];
910 for (int32 FieldIndex = 0; FieldIndex < EnumCount; ++FieldIndex)
911 {
912 OldValues[FieldIndex] = GetValue_double((typename TEnum::Type)FieldIndex);
913 }
914
915 // Set new stat data type
916 NameAndInfo.SetField<EStatDataType>(NewType);
917 for (int32 FieldIndex = 0; FieldIndex < EnumCount; ++FieldIndex)
918 {
919 GetValue_int64((typename TEnum::Type)FieldIndex) = (int64)OldValues[FieldIndex];
920 }
921 }
922 }
923 }
924
928 inline void Clear()
929 {
930 static_assert(sizeof(uint64) == DATA_SIZE / EnumCount, "Bad clear.");
931
932 for (int32 FieldIndex = 0; FieldIndex < EnumCount; ++FieldIndex)
933 {
934 *((int64*)&StatData + FieldIndex) = 0;
935 }
936 }
937
941 inline int64& GetValue_int64(typename TEnum::Type Index)
942 {
943 static_assert(sizeof(int64) <= DATA_SIZE && alignof(int64) <= DATA_ALIGN, "Bad data for stat message.");
944 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
946 int64& Value = *((int64*)&StatData + (uint32)Index);
947 return Value;
948 }
949 inline int64 GetValue_int64(typename TEnum::Type Index) const
950 {
951 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
953 const int64 Value = *((int64*)&StatData + (uint32)Index);
954 return Value;
955 }
956
957 inline int64 GetValue_Duration(typename TEnum::Type Index) const
958 {
959 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) && NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
961 if (NameAndInfo.GetFlag(EStatMetaFlags::IsPackedCCAndDuration))
962 {
964 return Value;
965 }
966 const int64 Value = *((int64 const*)&StatData + (uint32)Index);
967 return Value;
968 }
969
970 inline uint32 GetValue_CallCount(typename TEnum::Type Index) const
971 {
972 checkStats(NameAndInfo.GetFlag(EStatMetaFlags::IsPackedCCAndDuration) && NameAndInfo.GetFlag(EStatMetaFlags::IsCycle) && NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64);
975 return Value;
976 }
977
978 inline double& GetValue_double(typename TEnum::Type Index)
979 {
980 static_assert(sizeof(double) <= DATA_SIZE && alignof(double) <= DATA_ALIGN, "Bad data for stat message.");
982 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_double);
983 double& Value = *((double*)&StatData + (uint32)Index);
984 return Value;
985 }
986
987 inline double GetValue_double(typename TEnum::Type Index) const
988 {
989 checkStats(NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_double);
991 const double Value = *((double const*)&StatData + (uint32)Index);
992 return Value;
993 }
994
995 UE_FORCEINLINE_HINT FName GetShortName() const
996 {
997 return NameAndInfo.GetShortName();
998 }
999
1000 UE_FORCEINLINE_HINT FString GetDescription() const
1001 {
1002 return NameAndInfo.GetDescription();
1003 }
1004};
1005
1007struct EComplexStatField
1008{
1009 enum Type
1010 {
1012 IncSum,
1014 IncAve,
1016 IncMax,
1018 IncMin,
1020 ExcSum,
1022 ExcAve,
1024 ExcMax,
1026 ExcMin,
1027
1029 Num,
1030 };
1031};
1032
1038
1039template<> struct TIsPODType<FComplexStatMessage> { enum { Value = true }; };
1040
1041
1043{
1044 enum
1045 {
1046#if WITH_EDITOR
1047 MESSAGES_CHUNK_SIZE = 4 * 1024, // Smaller chunks prevent malloc deadlocks when running in the editor with TBB allocator
1048#else
1049 MESSAGES_CHUNK_SIZE = 64 * 1024,
1050#endif
1051 };
1052};
1053typedef TChunkedArray<FStatMessage, (uint32)EStatMessagesArrayConstants::MESSAGES_CHUNK_SIZE> FStatMessagesArray;
1054
1058struct FStatPacket
1059{
1061 int64 Frame = 1;
1063 uint32 ThreadId = 0;
1065 EThreadType::Type ThreadType;
1067 bool bBrokenCallstacks = false;
1072
1074 FStatPacket(EThreadType::Type InThreadType = EThreadType::Invalid)
1075 : ThreadType(InThreadType)
1076 {
1077 }
1078
1081 : Frame(Other.Frame)
1082 , ThreadId(Other.ThreadId)
1083 , ThreadType(Other.ThreadType)
1086 {
1087 }
1088
1090 void SetThreadProperties()
1091 {
1094 if (ThreadId == GGameThreadId)
1095 {
1096 ThreadType = EThreadType::Game;
1097 }
1098 else if (ThreadId == GRenderThreadId)
1099 {
1100 ThreadType = EThreadType::Renderer;
1101 }
1102 else
1103 {
1104 ThreadType = EThreadType::Other;
1105 }
1107 }
1108
1110 {
1111 Frame = InFrame;
1112 }
1113};
1114
1116struct FStatMessageLock
1117{
1120 {
1121 MessageScope++;
1122 }
1123
1125 {
1126 MessageScope--;
1127 }
1128
1129protected:
1131};
1132
1134struct FThreadStatsPool
1135{
1136private:
1139
1140public:
1143
1145 CORE_API static FThreadStatsPool& Get();
1146
1149
1151 CORE_API void ReturnToPool(FThreadStats* Instance);
1152};
1153
1158{
1159 friend class FStatsMallocProfilerProxy;
1160 friend class FStatsThreadState;
1161 friend class FStatsThread;
1162 friend struct FThreadStatsPool;
1163
1171 CORE_API static uint32 TlsSlot;
1173 CORE_API static bool bPrimaryEnable;
1175 CORE_API static bool bPrimaryDisableForever;
1177 CORE_API static bool bIsRawStatsActive;
1178
1181
1184
1186 int32 ScopeCount = 0;
1187
1190
1197
1199 bool bReentranceGuard = false;
1200
1202 bool bSawExplicitFlush = false;
1203
1205
1206protected:
1208 CORE_API FThreadStats(EThreadType::Type InThreadType = EThreadType::Invalid);
1209
1210public:
1212 static inline FThreadStats* GetThreadStats()
1213 {
1215 if (!Stats)
1216 {
1217 Stats = FThreadStatsPool::Get().GetFromPool();
1218 }
1219 return Stats;
1220 }
1221
1223 static CORE_API void CheckEnable();
1224
1231
1233 inline void UpdateExplicitFlush()
1234 {
1235 if (Packet.ThreadType != EThreadType::Other && bSawExplicitFlush)
1236 {
1237 bSawExplicitFlush = false;
1239 ScopeCount++; // prevent sends until the next explicit flush
1240 }
1241 }
1242
1244 CORE_API void Flush(bool bHasBrokenCallstacks = false, bool bForceFlush = false);
1245
1247 CORE_API void FlushRegularStats(bool bHasBrokenCallstacks, bool bForceFlush);
1248
1250 CORE_API void FlushRawStats(bool bHasBrokenCallstacks = false, bool bForceFlush = false);
1251
1254
1256 inline void AddStatMessage(const FStatMessage& StatMessage)
1257 {
1258 LLM_SCOPE(ELLMTag::Stats);
1260 Packet.StatMessages.AddElement(StatMessage);
1261 }
1262
1263protected:
1265 template <typename TValue>
1266 inline void AddMessageInner(FName InStatName, EStatOperation::Type InStatOperation, TValue Value, bool bIsCycle = false)
1267 {
1269
1270 if constexpr (std::is_same_v<std::decay_t<TValue>, double> || std::is_same_v<std::decay_t<TValue>, int64>)
1271 {
1272 switch (InStatOperation)
1273 {
1274 case EStatOperation::Set: TRACE_STAT_SET(InStatName, Value); break;
1275 case EStatOperation::Add: TRACE_STAT_ADD(InStatName, Value); break;
1276 case EStatOperation::Subtract: TRACE_STAT_ADD(InStatName, -Value); break;
1277 }
1278 }
1279
1280 if (!ScopeCount)
1281 {
1282 Flush();
1283 }
1284 else if (bIsRawStatsActive)
1285 {
1286 FlushRawStats();
1287 }
1288 }
1289
1292 inline void AddMessageInner(FName InStatName, EStatOperation::Type InStatOperation)
1293 {
1294 checkStats((InStatOperation == EStatOperation::CycleScopeStart || InStatOperation == EStatOperation::CycleScopeEnd));
1295
1296 // these branches are handled by the optimizer
1297 if (InStatOperation == EStatOperation::CycleScopeStart)
1298 {
1299 ScopeCount++;
1301
1303 {
1304 FlushRawStats();
1305 }
1306 }
1307 else if (InStatOperation == EStatOperation::CycleScopeEnd)
1308 {
1309 if (ScopeCount > bWaitForExplicitFlush)
1310 {
1312 ScopeCount--;
1313 if (!ScopeCount)
1314 {
1315 Flush();
1316 }
1317 else if (bIsRawStatsActive)
1318 {
1319 FlushRawStats();
1320 }
1321 }
1322 // else we dumped this frame without closing scope, so we just drop the closes on the floor
1323 }
1324 }
1325
1326public:
1328 static void Shutdown()
1329 {
1330 FThreadStats* Stats = IsThreadingReady() ? (FThreadStats*)FPlatformTLS::GetTlsValue(TlsSlot) : nullptr;
1331 if (Stats)
1332 {
1333 // Send all remaining messages.
1334 Stats->Flush(false, true);
1335 FPlatformTLS::SetTlsValue(TlsSlot, nullptr);
1336 FThreadStatsPool::Get().ReturnToPool(Stats);
1337 }
1338 }
1339
1341 static inline void AddMessage(FName InStatName, EStatOperation::Type InStatOperation)
1342 {
1343 if (!InStatName.IsNone() && WillEverCollectData() && IsThreadingReady())
1344 {
1345 GetThreadStats()->AddMessageInner(InStatName, InStatOperation);
1346 }
1347 }
1348
1350 template<typename TValue>
1351 static inline void AddMessage(FName InStatName, EStatOperation::Type InStatOperation, TValue Value, bool bIsCycle = false)
1352 {
1353 if (!InStatName.IsNone() && WillEverCollectData() && IsThreadingReady())
1354 {
1356 }
1357 }
1358
1363 static CORE_API void ExplicitFlush(bool DiscardCallstack = false);
1364
1367 {
1368 return bPrimaryEnable;
1369 }
1370 static UE_FORCEINLINE_HINT bool IsCollectingData(TStatId StatId)
1371 {
1372 // we don't test StatId for nullptr here because we assume it is non-null. If it is nullptr, that indicates a problem with higher level code.
1373 return !StatId.IsNone() && IsCollectingData();
1374 }
1375
1378 {
1379 return !bPrimaryDisableForever;
1380 }
1381
1384 {
1385 return FPlatformTLS::IsValidTlsSlot(TlsSlot);
1386 }
1387
1389 static inline void PrimaryEnableAdd(int32 Value = 1)
1390 {
1392 CheckEnable();
1393 }
1394
1396 static inline void PrimaryEnableSubtract(int32 Value = 1)
1397 {
1398 PrimaryEnableCounter.Subtract(Value);
1399 CheckEnable();
1400 }
1401
1403 static inline void PrimaryDisableForever()
1404 {
1406 CheckEnable();
1407 }
1408
1410 static inline void PrimaryDisableChangeTagLockAdd(int32 Value = 1)
1411 {
1414 PrimaryEnableUpdateNumber.Increment();
1415 }
1416
1418 static inline void PrimaryDisableChangeTagLockSubtract(int32 Value = 1)
1419 {
1421 PrimaryEnableUpdateNumber.Increment();
1424 }
1425
1427 static inline int32 PrimaryDisableChangeTag()
1428 {
1429 if (PrimaryDisableChangeTagLock.GetValue())
1430 {
1431 // while locked we are continually invalid, so we will just keep giving unique numbers
1432 return PrimaryEnableUpdateNumber.Increment();
1433 }
1434 return PrimaryEnableUpdateNumber.GetValue();
1435 }
1436
1438 static inline void FrameDataIsIncomplete()
1439 {
1441 PrimaryEnableUpdateNumber.Increment();
1443 }
1444
1446 static inline void EnableRawStats() TSAN_SAFE
1447 {
1448 bIsRawStatsActive = true;
1450 }
1451
1453 static inline void DisableRawStats() TSAN_SAFE
1454 {
1455 bIsRawStatsActive = false;
1457 }
1458
1460 static CORE_API void StartThread();
1462 static CORE_API void StopThread();
1464 static CORE_API void WaitForStats();
1465};
1466
1467/*
1468 * Wrapper used by the end-of-pipe tasks to report stats on the appropriate timeline.
1469 * Acts as a singleton instance of FThreadStats, since the end-of-pipe is a logical part of the frame, not a specific thread.
1470 */
1471class FEndOfPipeStats : private FThreadStats
1472{
1474
1476 : FThreadStats(EThreadType::EndOfPipe)
1477 {}
1478
1479public:
1480 static FEndOfPipeStats* Get()
1481 {
1482 return &EndOfPipeStats;
1483 }
1484
1485 template<typename TValue>
1486 void AddMessage(FName InStatName, EStatOperation::Type InStatOperation, TValue Value, bool bIsCycle = false)
1487 {
1488 if (!InStatName.IsNone() && WillEverCollectData() && IsThreadingReady())
1489 {
1490 FThreadStats::AddMessageInner(InStatName, InStatOperation, Value, bIsCycle);
1491 }
1492 }
1493
1494 void Flush()
1495 {
1496 FThreadStats::Flush(false, true);
1497 }
1498};
1499
1504class FCycleCounter
1505{
1507 enum
1508 {
1509 NamedEvent = 1 << 0,
1510 TraceEvent = 1 << 1,
1511 ThreadStatsEvent = 1 << 2,
1512 };
1513
1515 FName StatId;
1516 uint8 EmittedEvent = 0;
1517
1518public:
1519
1526 {
1528 if (StatMinimalName.IsNone())
1529 {
1530 return;
1531 }
1532
1533 // Emit named event for active cycle stat.
1536 {
1537#if PLATFORM_USES_ANSI_STRING_FOR_EXTERNAL_PROFILING
1538 FPlatformMisc::BeginNamedEvent(FColor(0), InStatId.GetStatDescriptionANSI());
1539#else
1540 FPlatformMisc::BeginNamedEvent(FColor(0), InStatId.GetStatDescriptionWIDE());
1541#endif
1543
1544#if CPUPROFILERTRACE_ENABLED
1546 {
1548 FCpuProfilerTrace::OutputBeginDynamicEventWithId(StatName, InStatId.GetStatDescriptionWIDE(), SourceLocation.GetFileName(), SourceLocation.GetLine());
1550 }
1551#endif
1552 }
1553
1554 if ((bAlways && FThreadStats::WillEverCollectData()) || FThreadStats::IsCollectingData())
1555 {
1557 StatId = StatName;
1558 FThreadStats::AddMessage(StatName, EStatOperation::CycleScopeStart);
1560 }
1561 }
1562
1565 {
1566 Start(InStatId, EStatFlags::None, bAlways, SourceLocation);
1567 }
1568
1570 inline void StartTrace(const FName Name)
1571 {
1572#if CPUPROFILERTRACE_ENABLED
1574 {
1575 FCpuProfilerTrace::OutputBeginDynamicEvent(Name);
1577 }
1578#endif
1579 }
1580
1582 inline void StartTrace(const FName Name, const TCHAR* Desc)
1583 {
1584#if CPUPROFILERTRACE_ENABLED
1586 {
1587 FCpuProfilerTrace::OutputBeginDynamicEventWithId(Name, Desc);
1589 }
1590#endif
1591 }
1592
1597 inline void Stop()
1598 {
1600 {
1602 }
1603
1604#if CPUPROFILERTRACE_ENABLED
1606 {
1607 FCpuProfilerTrace::OutputEndEvent();
1608 }
1609#endif
1610
1612 {
1613 FThreadStats::AddMessage(StatId, EStatOperation::CycleScopeEnd);
1614 }
1615
1616 EmittedEvent = 0;
1617 }
1618
1623 inline void StopAndResetStatId()
1624 {
1625 Stop();
1626 StatId = NAME_None;
1627 }
1628};
1629
1635class FScopeCycleCounter : public FCycleCounter
1636{
1637public:
1643 {
1644 Start(StatId, StatFlags, bAlways, SourceLocation);
1645 AutoRTFM::PushOnAbortHandler(this, [this]() { this->Stop(); });
1646 }
1647
1649 : FScopeCycleCounter(StatId, EStatFlags::None, bAlways, SourceLocation)
1650 {
1651 }
1652
1656 inline ~FScopeCycleCounter()
1657 {
1658 AutoRTFM::PopOnAbortHandler(this);
1659 Stop();
1660 }
1661
1662};
1663
1665{
1666 FThreadStats::PrimaryEnableAdd(Value);
1667}
1669{
1670 FThreadStats::PrimaryEnableSubtract(Value);
1671}
1672
1674{
1675public:
1676
1678 : StartTime(FPlatformTime::Seconds())
1679 , StatId(InStatId)
1680 , Scale(InScale)
1681 {
1682
1683 }
1684
1685 virtual ~FSimpleScopeSecondsStat()
1686 {
1687 double TotalTime = (FPlatformTime::Seconds() - StartTime) * Scale;
1688 FThreadStats::AddMessage(StatId.GetName(), EStatOperation::Add, TotalTime);
1689 }
1690
1691private:
1692
1693 double StartTime;
1694 TStatId StatId;
1695 double Scale;
1696};
1697
1699class FStartupMessages
1700{
1701 friend class FStatsThread;
1702
1703 TArray64<FStatMessage> DelayedMessages;
1704 FCriticalSection CriticalSection;
1705
1706public:
1709
1712
1714 CORE_API static FStartupMessages& Get();
1715};
1716
1721{
1722public:
1725
1727 virtual ~IStatGroupEnableManager()
1728 {
1729 }
1730
1742
1749 virtual void SetHighPerformanceEnableForGroup(FName Group, bool Enable) = 0;
1750
1756 virtual void SetHighPerformanceEnableForAllGroups(bool Enable) = 0;
1757
1761 virtual void ResetHighPerformanceEnableForAllGroups() = 0;
1762
1767 virtual void StatGroupEnableManagerCommand(FString const& Cmd) = 0;
1768
1770 virtual void UpdateMemoryUsage() = 0;
1771};
1772
1773
1778{
1779protected:
1782};
1783
1784template<class TStatData, bool TCompiledIn>
1786{
1787 inline TStatId GetStatId() const
1788 {
1791 {
1793 FName(TStatData::GetStatName()),
1794 TStatData::GetDescription(),
1795 TStatData::TGroup::GetGroupName(),
1796 TStatData::TGroup::GetGroupCategory(),
1797 TStatData::TGroup::GetDescription(),
1798 TStatData::TGroup::IsDefaultEnabled(),
1799 TStatData::IsClearEveryFrame(),
1800 TStatData::GetStatType(),
1801 TStatData::IsCycleStat(),
1802 TStatData::TGroup::GetSortByName(),
1803 TStatData::GetMemoryRegion()
1804 );
1805 }
1807 }
1808
1810 {
1811 return GetStatId().GetName();
1812 }
1813};
1814
1816{
1819 const TCHAR* InStatDesc,
1820 const char* InGroupName,
1821 const char* InGroupCategory,
1822 const TCHAR* InGroupDesc,
1823 bool bDefaultEnable,
1825 EStatDataType::Type InStatType,
1826 bool bCycleStat,
1827 bool bSortByName,
1829 )
1830 {
1832 InStatName,
1833 InStatDesc,
1839 InStatType,
1840 bCycleStat,
1843 );
1844 }
1845
1846 UE_FORCEINLINE_HINT TStatId GetStatId() const
1847 {
1849 }
1850
1852 {
1853 return GetStatId().GetName();
1854 }
1855};
1856
1857template<class TStatData>
1859{
1860 UE_FORCEINLINE_HINT TStatId GetStatId()
1861 {
1862 return TStatId();
1863 }
1865 {
1866 return FName();
1867 }
1868};
1869
1870template<class TStatData>
1871struct FThreadSafeStaticStat : public FThreadSafeStaticStatInner<TStatData, TStatData::TGroup::CompileTimeEnable>
1872{
1874 {
1875 //This call will result in registering the Group if it's compile time enabled.
1876 //It fixes a bug when a StatGroup only has counters that are using the INC_\DEC_ macros.
1877 //Those macros are guarded for the stats collection to be active which prevented the registration of the stat group.
1878 //It was not possible to activate the stat group unless another was already active.
1879 //Most groups are registered when a FScopeCycleCounter is declared as GetStatId is called as the constructor parameter.
1880 FThreadSafeStaticStatInner<TStatData, TStatData::TGroup::CompileTimeEnable>::GetStatId(); //-V530
1881 }
1882};
1883
1884#else // STATS
1885
1887{
1888}
1890{
1891}
1892
1893#endif // STATS
#define AUTORTFM_DISABLE
Definition AutoRTFMDefines.h:116
#define AUTORTFM_OPEN
Definition AutoRTFMDefines.h:122
#define UE_AUTORTFM_ALWAYS_OPEN
Definition AutoRTFMDefines.h:114
uint32 GRenderThreadId
Definition CoreGlobals.cpp:438
int32 GCycleStatsShouldEmitNamedEvents
Definition CoreGlobals.cpp:462
bool GShouldEmitVerboseNamedEvents
Definition CoreGlobals.cpp:468
uint32 GGameThreadId
Definition CoreGlobals.cpp:437
#define UE_DEPRECATED(Version, Message)
Definition CoreMiscDefines.h:302
#define TSAN_SAFE
Definition CoreMiscDefines.h:144
FPlatformTypes::TCHAR TCHAR
Either ANSICHAR or WIDECHAR, depending on whether the platform supports wide characters or the requir...
Definition Platform.h:1135
FPlatformTypes::WIDECHAR WIDECHAR
A wide character. Normally a signed type.
Definition Platform.h:1133
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
FPlatformTypes::ANSICHAR ANSICHAR
An ANSI character. Normally a signed type.
Definition Platform.h:1131
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
EMemoryOrder
Definition Atomic.h:32
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
constexpr bool EnumHasAnyFlags(Enum Flags, Enum Contains)
Definition EnumClassFlags.h:35
return true
Definition ExternalRpcRegistry.cpp:601
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
Definition GenericPlatformCompilerPreSetup.h:12
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
Definition GenericPlatformCompilerPreSetup.h:8
UE_FORCEINLINE_HINT bool operator!=(const FIndexedPointer &Other) const
Definition LockFreeList.h:76
#define LLM_SCOPE(...)
Definition LowLevelMemTracker.h:1095
@ Num
Definition MetalRHIPrivate.h:234
FORCEINLINE FMinimalName NameToMinimalName(FName InName)
Definition NameTypes.h:1602
FORCEINLINE FName MinimalNameToName(FMinimalName InName)
Definition NameTypes.h:1592
#define MAX_uint32
Definition NumericLimits.h:21
@ Stop
Definition PrecomputedVolumetricLightmapStreaming.cpp:26
EStatFlags
Definition StatsCommon.h:34
#define checkStats(x)
Definition StatsCommon.h:11
void StatsPrimaryEnableAdd(int32 Value=1)
Definition StatsSystemTypes.h:1886
void StatsPrimaryEnableSubtract(int32 Value=1)
Definition StatsSystemTypes.h:1889
#define TRACE_STAT_SET(...)
Definition StatsTrace.h:49
#define TRACE_STAT_ADD(...)
Definition StatsTrace.h:48
#define UE_TRACE_CHANNELEXPR_IS_ENABLED(ChannelsExpr)
Definition Trace.h:452
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition NameTypes.h:617
static CORE_API FString SafeString(FNameEntryId InDisplayIndex, int32 InstanceNumber=NAME_NO_NUMBER_INTERNAL)
Definition UnrealNames.cpp:3822
Definition UnrealTemplate.h:321
Definition LightweightStats.h:424
Definition ThreadSafeCounter.h:14
Definition Renderer.Build.cs:6
Definition Array.h:670
Definition Atomic.h:538
Definition ChunkedArray.h:56
Definition LockFreeList.h:904
Definition UniquePtr.h:107
Definition SourceLocation.h:21
static UE_CONSTEVAL FSourceLocation Current(FSourceLocationImpl Impl=FSourceLocationImpl::current()) noexcept
Definition SourceLocation.h:107
FText GetDescription(EHttpResponseCodes::Type StatusCode)
Definition IHttpResponse.h:52
const Type Shift
Definition GenericApplication.h:43
Type
Definition PawnAction_Move.h:11
IMAGECORE_API const TCHAR * GetName(Type Format)
Definition ImageCore.cpp:1378
uint32 GetTypeHash(const FKey &Key)
Definition BlackboardKey.h:35
int StartThread(void *p_os_thread_mem, ThreadMain p_thread_main, void *p_param)
void * Alloc(int size)
FORCEINLINE T * Get(const FObjectPtr &ObjectPtr)
Definition ObjectPtr.h:426
bool operator==(const FCachedAssetKey &A, const FCachedAssetKey &B)
Definition AssetDataMap.h:501
@ Start
Definition GeoEnum.h:100
Type * GetRawPointer(const TWeakObjectPtr< Type > Object)
Definition TypedElementDataStorageCompatibilityInterface.h:106
@ Packet
Definition Transport.h:15
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71
static UE_FORCEINLINE_HINT void MemoryBarrier()
Definition AndroidPlatformMisc.h:249
static uint32 GetCurrentThreadId(void)
Definition AndroidPlatformTLS.h:20
static UE_FORCEINLINE_HINT void * GetTlsValue(uint32 SlotIndex)
Definition AndroidPlatformTLS.h:57
static UE_FORCEINLINE_HINT void SetTlsValue(uint32 SlotIndex, void *Value)
Definition AndroidPlatformTLS.h:47
Definition AndroidPlatformTime.h:18
static uint32 Cycles()
Definition AndroidPlatformTime.h:27
static double Seconds()
Definition AndroidPlatformTime.h:20
Definition Color.h:486
EMemoryCounterRegion
Definition GenericPlatformMemory.h:249
@ MCR_Invalid
Definition GenericPlatformMemory.h:250
@ MCR_MAX
Definition GenericPlatformMemory.h:259
static FORCEINLINE void BeginNamedEvent(const struct FColor &Color, const TCHAR *Text)
Definition GenericPlatformMisc.h:920
static FORCEINLINE void EndNamedEvent()
Definition GenericPlatformMisc.h:926
static UE_FORCEINLINE_HINT bool IsValidTlsSlot(uint32 SlotIndex)
Definition GenericPlatformTLS.h:20
Definition NameTypes.h:439
FORCEINLINE bool IsNone() const
Definition NameTypes.h:1564
Definition NameTypes.h:69
static FNameEntryId FromUnstableInt(uint32 UnstableInt)
Definition NameTypes.h:114
Definition TypeCompatibleBytes.h:17
Definition IsPODType.h:12
@ Value
Definition IsPODType.h:13
Definition LightweightStats.h:416
static void AutoRTFMAssignFromOpenToClosed(TStatId &Closed, const TStatId &Open)
Definition LightweightStats.h:417