UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
LightweightStats.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "CoreTypes.h"
5#include "CoreGlobals.h"
6#include "Misc/Build.h"
7#include "StatsCommon.h"
8
9// UE_USE_LIGHTWEIGHT_STATS is defined in StatsCommon.h
10#if UE_USE_LIGHTWEIGHT_STATS
11
12#include "AutoRTFM.h"
13#include "Hash/Fnv.h"
14#include "Math/Color.h"
15#include "Misc/NotNull.h"
18
19struct TStatId
20{
22
23 FORCEINLINE constexpr TStatId()
24 : StatString(nullptr)
25 {
26 }
27
30 {
31 }
32
33 FORCEINLINE constexpr const PROFILER_CHAR* GetName() const
34 {
35 return StatString;
36 }
37
38 FORCEINLINE constexpr bool IsValidStat() const
39 {
40 return StatString != nullptr;
41 }
42
43 FORCEINLINE constexpr bool operator==(TStatId Other) const
44 {
45 return StatString == Other.StatString;
46 }
47
48 FORCEINLINE constexpr bool operator!=(TStatId Other) const
49 {
50 return StatString != Other.StatString;
51 }
52
54 {
55 Closed = Open;
56 }
57};
58
59namespace UE::Stats::Private
60{
61 // A TStatId that we've already checked can be emitted (via GetStatIfEnabled).
62 struct FCheckedStat
63 {
64 // Validity is separate from the stat name being not-null
65 // as even if the stat shouldn't be emitted as a named event, we may need the stat name for other
66 // things such as the FHitchTrackingStatScope.
67 // However, it's worth noting the StatString can be null for cases where we do not want to emit a hitch scope
68 // which is the case for conditional scopes when the condition is false.
69 static constexpr FCheckedStat MakeValid(const PROFILER_CHAR* InStatString)
70 {
71 return FCheckedStat{ InStatString, true };
72 }
73
74 static constexpr FCheckedStat MakeInvalid(const PROFILER_CHAR* InStatString)
75 {
76 return FCheckedStat{ InStatString, false };
77 }
78
79 // Allow implicit conversions to TStatId as FCheckedStat can be assigned to TStatId via GetStatIfEnabled.
80 constexpr operator TStatId() const
81 {
82 // Systems that use TStatId directly test validity through the stat string.
83 return bIsValid ? TStatId(StatString) : TStatId{};
84 }
85
86 // Returns the stat name, even if this stat isn't valid.
87 constexpr const PROFILER_CHAR* GetName() const
88 {
89 return StatString;
90 }
91
92 // Returns true if this stat should be emitted.
93 constexpr bool IsValidStat() const
94 {
95 return bIsValid;
96 }
97
98 private:
99 constexpr explicit FCheckedStat(const PROFILER_CHAR* InStatString, bool bValidStat)
101 , bIsValid(bValidStat)
102 {
103 }
104
106 bool bIsValid;
107 };
108
109 template<typename DerivedScopeCounterType, typename StatIdType = TStatId>
111 {
112 public:
113 FORCEINLINE constexpr explicit TScopeCycleCounterBase(const StatIdType& InStatId)
114#if USE_LIGHTWEIGHT_STATS_FOR_HITCH_DETECTION && USE_HITCH_DETECTION
115 : StatScope(InStatId.GetName())
116#endif
117 {
118 if (DerivedScopeCounterType::CanEmitStat(InStatId))
119 {
121 {
122 DerivedScopeCounterType::BeginNamedEvent(InStatId.GetName());
123 bPop = true;
124 };
125 AutoRTFM::PushOnAbortHandler(this, []() { FPlatformMisc::EndNamedEvent(); });
126 }
127 }
128
130 {
131 if (bPop)
132 {
133 AutoRTFM::PopOnAbortHandler(this);
135 {
137 };
138 }
139 }
140
141 FORCEINLINE static bool CanEmitStat(const TStatId& Stat)
142 {
143 return Stat.IsValidStat() && GCycleStatsShouldEmitNamedEvents > 0;
144 }
145
146 private:
147#if USE_LIGHTWEIGHT_STATS_FOR_HITCH_DETECTION && USE_HITCH_DETECTION
148 UE::Stats::FHitchTrackingStatScope StatScope;
149#endif
150 bool bPop = false;
151 };
152
156 class FScopeCycleCounterStatic : TScopeCycleCounterBase<FScopeCycleCounterStatic, FCheckedStat>
157 {
159 public:
160 using Base::CanEmitStat;
161
163 : Base(InStatId)
164 {
165 }
166
167 FORCEINLINE static constexpr bool CanEmitStat(const FCheckedStat& Stat)
168 {
169 // If we were given a FCheckedStat it means it's gone through GetIfStatEnabled so
170 // GCycleStatsShouldEmitNamedEvents has already been checked.
171 return Stat.IsValidStat();
172 }
173
174 FORCEINLINE static void BeginNamedEvent(TNotNull<const PROFILER_CHAR* const> StatName)
175 {
176#if PLATFORM_IMPLEMENTS_BeginNamedEventStatic
177 FPlatformMisc::BeginNamedEventStatic(FColor(0), StatName);
178#else
180#endif
181 }
182 };
183
184} // namespace UE::Stats::Private
185
186// ----------------------------------------------------------------
187// FScopeCycleCounter
188// ----------------------------------------------------------------
189
190// Scope counter for stat names with dynamic storage.
191class FScopeCycleCounter : private UE::Stats::Private::TScopeCycleCounterBase<FScopeCycleCounter>
192{
193 using Base = UE::Stats::Private::TScopeCycleCounterBase<FScopeCycleCounter>;
194
195public:
196 using Base::CanEmitStat;
197
198 // NOTE: this signature must match the other FScopeCycleCounter implementations
200 : Base(InStatId)
201 {
202 }
203
204 // NOTE: this signature must match the other FScopeCycleCounter implementations
207 {
208 }
209
210 FORCEINLINE static void BeginNamedEvent(TNotNull<const PROFILER_CHAR* const> StatName)
211 {
213 }
214};
215
216// ----------------------------------------------------------------
217// Internal Helpers
218// ----------------------------------------------------------------
219namespace UE::Stats::Private
220{
221 // ----------------------------------------------------------------
222 // Stat/Group Getter Helpers
223 // ----------------------------------------------------------------
224
225 // Fallback group data for groups that don't have a custom group struct defined.
226 struct FDefaultGroupData
227 {
228 static constexpr const PROFILER_CHAR* GetName()
229 {
230 return nullptr;
231 }
232 static constexpr uint32 GetNameHash()
233 {
234 return 0;
235 }
236 // Note: matches spelling of implementation when STATS is 1.
237 static constexpr bool IsCompileTimeEnable()
238 {
239 return true;
240 }
241 };
242
243 // Fallback stat data for StatId's that don't have a custom stat struct defined.
244 struct FDefaultStatData
245 {
247
248 static constexpr const PROFILER_CHAR* GetName()
249 {
250 return nullptr;
251 }
252 static constexpr EStatFlags GetFlags()
253 {
254 return EStatFlags::None;
255 }
256 };
257
258 template<typename StatDataType, bool AllowDefault, typename = void>
259 struct TStatDataHelper
260 {
261 // This error means a DECLARE_* macro is missing for this stat for this compile configuration.
262 // The Declaration macros can be found in Stats.h.
263 static_assert(AllowDefault, "Default StatId not permitted. Please provide a declaration for this stat via one of the DECLARE_ Stat macros.");
264 using Type = FDefaultStatData;
265 };
266 // If StatDataType is a complete type such that we can call sizeof on it, this specialization is used
267 template<typename StatDataType, bool AllowDefault>
268 struct TStatDataHelper<StatDataType, AllowDefault, std::void_t<decltype(sizeof(StatDataType))>>
269 {
270 using Type = StatDataType;
271 };
272
273 // Helper defined as either the custom stat data struct if it's defined, or the default (FDefaultStatData).
274 template<typename StatDataType, bool AllowDefault>
275 using TStatDataType = typename TStatDataHelper<StatDataType, AllowDefault>::Type;
276
277
278 template<typename AllowDefaultStatToken, typename = void>
280 {
281 static constexpr bool Value = false;
282 };
283 template<typename AllowDefaultStatToken>
284 struct TAllowDefaultStatHelper<AllowDefaultStatToken, std::void_t<decltype(sizeof(AllowDefaultStatToken))>>
285 {
286 // A token has been defined for this stat, so it's allowed.
287 static constexpr bool Value = true;
288 };
289
290 // Determines if falling back to FDefaultStatData is valid for a given stat.
291 // To be allowed, INTERNAL_ALLOW_DEFAULT_STAT must be defined for the stat.
292 template<typename AllowDefaultStatToken>
293 constexpr bool AllowDefaultForStat = TAllowDefaultStatHelper<AllowDefaultStatToken>::Value;
294
295 template<typename GroupDataType, typename = void>
296 struct TGroupDataHelper
297 {
298 using Type = FDefaultGroupData;
299 };
300 template<typename GroupDataType>
301 struct TGroupDataHelper<GroupDataType, std::void_t<decltype(sizeof(GroupDataType))>>
302 {
303 using Type = GroupDataType;
304 };
305
306 // Helper defined as either the custom group data struct if it's defined, or the default (FDefaultGroupData).
307 template<typename GroupDataType>
308 using TGroupDataType = typename TGroupDataHelper<GroupDataType>::Type;
309
310 // Tests if this stat can be emitted
311 template<typename StatDataType = FDefaultStatData, typename GroupDataType = typename StatDataType::TGroup>
312 inline bool IsStatEnabled(uint32 StatNameHash, uint32 GroupNameHash = 0)
313 {
314 if constexpr (!GroupDataType::IsCompileTimeEnable())
315 {
316 return false;
317 }
318 else
319 {
320 if constexpr (EnumHasAnyFlags(StatDataType::GetFlags(), EStatFlags::Verbose))
321 {
323 {
324 return false;
325 }
326 }
327
328#if UE_STATS_ALLOW_PER_THREAD_IGNORELIST
329 const uint32 GroupHash = GroupNameHash != 0 ? GroupNameHash : GroupDataType::GetNameHash();
330 if (UE::Stats::IsStatOrGroupIgnoredOnCurrentThread(StatNameHash, GroupHash))
331 {
332 return false;
333 }
334#endif // UE_STATS_ALLOW_PER_THREAD_IGNORELIST
335 return true;
336 }
337 }
338
346 template<typename StatDataType = FDefaultStatData, typename GroupDataType = typename StatDataType::TGroup>
348 {
349 // We're testing if named events are enabled here as it's cheaper than some of the checks in IsStatEnabled.
352 {
353 return FCheckedStat::MakeValid(StatName);
354 }
355 return FCheckedStat::MakeInvalid(StatName);
356 }
357
358 /* Returns a valid TStatId if the condition is true and we can emit it. */
359 template<typename StatDataType = FDefaultStatData, typename GroupDataType = typename StatDataType::TGroup>
361 {
362 if (bCondition)
363 {
365 }
366 // Preserve existing behavior of not emitting hitch scopes if the condition is false.
367 return FCheckedStat::MakeInvalid(nullptr);
368 }
369} // namespace UE::Stats::Private
370
371// ----------------------------------------------------------------
372// Helper Macros
373// ----------------------------------------------------------------
374
375#define UE_INTERNAL_GET_STATGROUP_TYPE(GroupId)\
376 UE::Stats::Private::TGroupDataType<struct FStatGroup_##GroupId>
377
378// Specifies that a custom FStat struct doesn't need to be defined for this stat and we can fallback to the default.
379// This is for internal use only currently.
380#define UE_INTERNAL_ALLOW_DEFAULT_STAT(Stat)\
381 struct FAllowDefaultStat_##Stat{};
382
383// Returns either the custom stat data struct for a stat, or FDefaultStatData if one doesn't exist.
384#define UE_INTERNAL_GET_STATDATA(Stat)\
385 UE::Stats::Private::TStatDataType<struct FStat_##Stat, UE::Stats::Private::AllowDefaultForStat<struct FAllowDefaultStat_##Stat>>
386
387// Returns either the custom stat data struct for a stat, or FDefaultStatData if one doesn't exist.
388// This version will not emit a compilation error if a default is not allowed to handled special cases.
389#define UE_INTERNAL_GET_STATDATA_ALLOW_DEFAULT(Stat)\
390 UE::Stats::Private::TStatDataType<struct FStat_##Stat, true>
391
392// Helper to evaluate if a stat is marked as verbose.
393#define UE_IS_STAT_VERBOSE(Stat)\
394 EnumHasAnyFlags(UE_INTERNAL_GET_STATDATA(Stat)::GetFlags(), EStatFlags::Verbose)
395
396#define UE_IS_STAT_VERBOSE_ALLOW_DEFAULT(Stat)\
397 EnumHasAnyFlags(UE_INTERNAL_GET_STATDATA_ALLOW_DEFAULT(Stat)::GetFlags(), EStatFlags::Verbose)
398
399// Getters
400#define UE_INTERNAL_GET_STAT_IF_ENABLED(Stat)\
401 UE::Stats::Private::GetStatIfEnabled<UE_INTERNAL_GET_STATDATA(Stat)>(ANSI_TO_PROFILING(#Stat), UE_STATS_HASH_NAME(Stat))
402
403#define UE_INTERNAL_GET_STAT_IF_ENABLED_COND(Stat, Cond)\
404 UE::Stats::Private::GetStatIfEnabled<UE_INTERNAL_GET_STATDATA(Stat)>(ANSI_TO_PROFILING(#Stat), (Cond), UE_STATS_HASH_NAME(Stat))
405
406#define UE_INTERNAL_GET_QUICK_STAT_IF_ENABLED(Stat)\
407 UE::Stats::Private::GetStatIfEnabled<UE_INTERNAL_GET_STATDATA_ALLOW_DEFAULT(Stat)>(ANSI_TO_PROFILING(#Stat), UE_STATS_HASH_NAME(Stat))
408
409#define UE_INTERNAL_GET_QUICK_STAT_WITH_GROUP_IF_ENABLED(Stat, Group)\
410 UE::Stats::Private::GetStatIfEnabled<UE_INTERNAL_GET_STATDATA_ALLOW_DEFAULT(Stat), UE_INTERNAL_GET_STATGROUP_TYPE(Group)>(ANSI_TO_PROFILING(#Stat), UE_STATS_HASH_NAME(Stat), UE_STATS_HASH_NAME(Group))
411
412
413#elif !STATS // UE_USE_LIGHTWEIGHT_STATS
414
416{
418 {
419 Closed = Open;
420 }
421};
422
424{
425public:
427 {
428 }
429 inline FScopeCycleCounter(TStatId, bool bAlways = false)
430 {
431 }
432};
433#endif // UE_USE_LIGHTWEIGHT_STATS
#define FORCEINLINE
Definition AndroidPlatform.h:140
int32 GCycleStatsShouldEmitNamedEvents
Definition CoreGlobals.cpp:462
bool GShouldEmitVerboseNamedEvents
Definition CoreGlobals.cpp:468
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
constexpr bool EnumHasAnyFlags(Enum Flags, Enum Contains)
Definition EnumClassFlags.h:35
UE_FORCEINLINE_HINT bool operator!=(const FIndexedPointer &Other) const
Definition LockFreeList.h:76
T TNotNull
Definition NotNull.h:307
EStatFlags
Definition StatsCommon.h:34
WIDECHAR PROFILER_CHAR
Definition StatsCommon.h:24
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition LightweightStats.h:424
FScopeCycleCounter(TStatId, EStatFlags, bool bAlways=false)
Definition LightweightStats.h:426
FScopeCycleCounter(TStatId, bool bAlways=false)
Definition LightweightStats.h:429
uint32 GetFlags(uint32 Word3)
Definition CollisionFilterData.cpp:21
Type
Definition PawnAction_Move.h:11
IMAGECORE_API const TCHAR * GetName(Type Format)
Definition ImageCore.cpp:1378
bool operator==(const FCachedAssetKey &A, const FCachedAssetKey &B)
Definition AssetDataMap.h:501
Definition StatsSystem.cpp:35
Definition Color.h:486
static FORCEINLINE void BeginNamedEvent(const struct FColor &Color, const TCHAR *Text)
Definition GenericPlatformMisc.h:920
static FORCEINLINE void EndNamedEvent()
Definition GenericPlatformMisc.h:926
Definition LightweightStats.h:416
static void AutoRTFMAssignFromOpenToClosed(TStatId &Closed, const TStatId &Open)
Definition LightweightStats.h:417