UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
RHIBreadcrumbs.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Misc/Build.h"
6#include "Misc/MemStack.h"
7#include "HAL/Platform.h"
12
13#include "GpuProfilerTrace.h"
14#include "RHIFwd.h"
15#include "RHIPipeline.h"
16#include "MultiGPU.h"
17
18#include <array>
19
20//
21// Controls whether the infrastructure for the RHI breadcrumbs system is included in the build.
22// (RHI breadcrumb allocators, platform RHI implemntation etc).
23//
24#ifndef WITH_RHI_BREADCRUMBS
25#define WITH_RHI_BREADCRUMBS (UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT || WITH_PROFILEGPU || (HAS_GPU_STATS && RHI_NEW_GPU_PROFILER))
26#endif
27
28// Enables all RDG/RHI breadcrumb scopes, and features such as Insights markers.
29#define WITH_RHI_BREADCRUMBS_FULL (WITH_RHI_BREADCRUMBS && (UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT || WITH_PROFILEGPU))
30
31// Enables only the necessary scopes for GPU stats to work
32#define WITH_RHI_BREADCRUMBS_MINIMAL (WITH_RHI_BREADCRUMBS && (!WITH_RHI_BREADCRUMBS_FULL))
33
34// Whether to emit Unreal Insights breadcrumb events on threads involved in RHI command list recording and execution.
35#define RHI_BREADCRUMBS_EMIT_CPU (WITH_RHI_BREADCRUMBS_FULL && CPUPROFILERTRACE_ENABLED && 1)
36
37// Whether to store the filename and line number of each RHI breadcrumb and emit this data to Insights.
38#define RHI_BREADCRUMBS_EMIT_LOCATION (WITH_RHI_BREADCRUMBS_FULL && (CPUPROFILERTRACE_ENABLED || GPUPROFILERTRACE_ENABLED) && 1)
39
40#if RHI_NEW_GPU_PROFILER && HAS_GPU_STATS
42{
43 struct FGPUStat;
44}
45#endif
46
47#if WITH_RHI_BREADCRUMBS
48
49 //
50 // Holds the filename and line number location of the RHI breadcrumb in source.
51 //
53 {
54#if RHI_BREADCRUMBS_EMIT_LOCATION
55 ANSICHAR const* File;
57#endif
58
60#if RHI_BREADCRUMBS_EMIT_LOCATION
61 : File(File)
62 , Line(Line)
63#endif
64 {}
65 };
66
67 //
68 // Holds both a stats system ID, and a CSV profiler ID.
69 // The computed stat value is emitted to both "stat gpu" and the CSV profiler.
70 //
72 {
73#if RHI_NEW_GPU_PROFILER && HAS_GPU_STATS
74
76
79 {}
80
81 bool ShouldComputeStat() const
82 {
83 return GPUStat != nullptr;
84 }
85
86 bool operator == (FRHIBreadcrumbData_Stats const& RHS) const
87 {
88 return GPUStat == RHS.GPUStat;
89 }
90
92 {
93 return PointerHash(Stats.GPUStat);
94 }
95
96#elif HAS_GPU_STATS
97
98 #if STATS
99 TStatId StatId {};
100 #endif
101 #if CSV_PROFILER_STATS
103 #endif
104
106 {
107 #if STATS
108 StatId = InStatId;
109 #endif
110 #if CSV_PROFILER_STATS
112 #endif
113 }
114
115 bool ShouldComputeStat() const
116 {
117 #if STATS
118 return StatId.IsValidStat();
119 #elif CSV_PROFILER_STATS
120 return CsvStat != NAME_None;
121 #else
122 return false;
123 #endif
124 }
125
126 bool operator == (FRHIBreadcrumbData_Stats const& RHS) const
127 {
128 #if STATS
129 return StatId == RHS.StatId;
130 #elif CSV_PROFILER_STATS
131 return CsvStat == RHS.CsvStat;
132 #else
133 return true;
134 #endif
135 }
136
137 friend uint32 GetTypeHash(FRHIBreadcrumbData_Stats const& Stats)
138 {
139 #if STATS
140 return GetTypeHash(Stats.StatId);
141 #elif CSV_PROFILER_STATS
142 return GetTypeHash(Stats.CsvStat);
143 #else
144 return 0;
145 #endif
146 }
147
148#else
149
150 FRHIBreadcrumbData_Stats() = default;
151
152 bool ShouldComputeStat() const
153 {
154 return false;
155 }
156
157 bool operator == (FRHIBreadcrumbData_Stats const& RHS) const
158 {
159 return true;
160 }
161
162 friend uint32 GetTypeHash(FRHIBreadcrumbData_Stats const& Stats)
163 {
164 return 0;
165 }
166
167#endif
168 };
169
170 //
171 // Container for extra profiling-related data for each RHI breadcrumb.
172 //
174 // Use inheritance for empty-base-optimization.
177 {
178 public:
179 TCHAR const* const StaticName;
180
181 FRHIBreadcrumbData(TCHAR const* StaticName, ANSICHAR const* File, uint32 Line, FRHIBreadcrumbData_Stats&& Stats)
184 , StaticName(StaticName)
185 {}
186 };
187
189 struct FRHIBreadcrumbRange;
190
192 {
193 struct FPipeline
194 {
195 uint32 MarkerIn = 0, MarkerOut = 0;
196 };
197
198 struct FDevice
199 {
201 };
202
204
205 struct FQueueID
206 {
207 uint32 DeviceIndex;
209
210 bool operator == (FQueueID const& RHS) const
211 {
212 return DeviceIndex == RHS.DeviceIndex && Pipeline == RHS.Pipeline;
213 }
214
215 bool operator != (FQueueID const& RHS) const
216 {
217 return !(*this == RHS);
218 }
219
220 friend uint32 GetTypeHash(FQueueID const& ID)
221 {
222 return HashCombineFast(GetTypeHash(ID.DeviceIndex), GetTypeHash(ID.Pipeline));
223 }
224 };
225
226 enum class EVerbosity
227 {
228 Log,
229 Warning,
230 Error
231 };
232
233 RHI_API void DumpActiveBreadcrumbs(TMap<FQueueID, TArray<FRHIBreadcrumbRange>> const& QueueRanges, EVerbosity Verbosity = EVerbosity::Error) const;
234 };
235
236 struct FRHIBreadcrumbNode
237 {
238 RHI_API static std::atomic<uint32> NextID;
239
240 private:
241 FRHIBreadcrumbNode* Parent = Sentinel;
242 FRHIBreadcrumbNode* ListLink = nullptr;
244
245 public:
246 FRHIBreadcrumbAllocator* const Allocator = nullptr;
247
249 #if RHI_BREADCRUMBS_EMIT_CPU
252 #endif
253
254 uint32 const ID = 0;
255
256 #if DO_CHECK
257 // Used to track use of this breadcrumb on each GPU pipeline. Breadcrumbs can only be begun/ended once per pipe.
258 std::atomic<std::underlying_type_t<ERHIPipeline>> BeginPipes = std::underlying_type_t<ERHIPipeline>(ERHIPipeline::None);
259 std::atomic<std::underlying_type_t<ERHIPipeline>> EndPipes = std::underlying_type_t<ERHIPipeline>(ERHIPipeline::None);
260 #endif
261
264 , Data(Data)
265 , ID(NextID.fetch_add(1, std::memory_order_relaxed) | 0x80000000) // Set the top bit to avoid collision with zero (i.e. "no breadcrumb")
266 {}
267
269 FRHIBreadcrumbNode* const& GetNextPtr(ERHIPipeline Pipeline) const { return NextPtrs[GetRHIPipelineIndex(Pipeline)]; }
270
271 FRHIBreadcrumbNode* GetParent() const { return Parent; }
272 inline void SetParent(FRHIBreadcrumbNode* Node);
273
274 virtual void TraceBeginGPU(uint32 QueueId, uint64 GPUTimestampTOP) const = 0;
275 virtual void TraceEndGPU(uint32 QueueId, uint64 GPUTimestampTOP) const = 0;
276
277 inline void TraceBeginCPU() const;
278 inline void TraceEndCPU() const;
279
280 // Calls BeginCPU() on all the breadcrumb nodes between the root and the specified node.
281 // Only valid to call from the bottom-of-pipe, after the dispatch thread has fixed up the breadcrumb tree.
282 static inline void WalkIn(FRHIBreadcrumbNode const* Node);
283
284 // Same as WalkIn, but the root node is specified, allowing it to be called from the top-of-pipe.
285 static inline void WalkInRange(FRHIBreadcrumbNode const* Leaf, FRHIBreadcrumbNode const* Root);
286
287 // Calls EndCPU() on all the breadcrumb nodes between the specified node and the root.
288 // Only valid to call from the bottom-of-pipe, after the dispatch thread has fixed up the breadcrumb tree.
289 static inline void WalkOut(FRHIBreadcrumbNode const* Node);
290
291 // Same as WalkOut, but the root node is specified, allowing it to be called from the top-of-pipe.
292 static inline void WalkOutRange(FRHIBreadcrumbNode const* Leaf, FRHIBreadcrumbNode const* Root);
293
294 // ----------------------------------------------------
295 // Debug logging / crash reporting
296 // ----------------------------------------------------
297
298 #if WITH_ADDITIONAL_CRASH_CONTEXTS
299 // Logs the stack of breadcrumbs to the crash context, starting from the current node.
300 RHI_API void WriteCrashData(struct FCrashContextExtendedWriter& Writer, const TCHAR* ThreadName) const;
301 #endif
302
303 RHI_API FString GetFullPath() const;
304
306 static RHI_API uint32 GetLevel(FRHIBreadcrumbNode const* Node);
307
309
310 // A constant pointer value representing an undefined node. Used as the parent pointer for nodes in sub-trees
311 // that haven't been attached to the root yet, specifically to be distinct from nullptr which is the root.
312 static RHI_API FRHIBreadcrumbNode* const Sentinel;
313
314 // The maximum length of a breadcrumb string, including the null terminator.
315 static constexpr uint32 MaxLength = 128;
316
317 struct FBuffer
318 {
320 };
321
322 virtual TCHAR const* GetTCHAR(FBuffer& Buffer) const = 0;
323
324 TCHAR const* GetTCHARNoFormat() const
325 {
326 return Data.StaticName;
327 }
328
329 protected:
330 // Constructor for the sentinel value
332 friend struct FRHIBreadcrumbList;
333 };
334
335 // Typedef for backwards compatibility. The FRHIBreadcrumb and FRHIBreadcrumbNode have been merged into one type.
337
338 template <typename TDesc, typename... TValues>
339 using TRHIBreadcrumbInitializer = std::tuple<TDesc const*, std::tuple<TValues...>>;
340
341 class FRHIBreadcrumbAllocatorArray : public TArray<TSharedRef<class FRHIBreadcrumbAllocator>, TInlineAllocator<2>>
342 {
343 public:
345 };
346
347 class FRHIBreadcrumbAllocator : public TSharedFromThis<FRHIBreadcrumbAllocator>
348 {
349 friend FRHIBreadcrumbNode;
350
353
354 public:
355 FRHIBreadcrumbAllocatorArray const& GetParents() const { return Parents; }
356
357 template <typename TType, typename... TArgs>
358 TType* Alloc(TArgs&&... Args)
359 {
360 static_assert(std::is_trivially_destructible<TType>::value, "Only trivially destructable types may be used with the RHI breadcrumb allocator.");
361 return new (Inner.Alloc(sizeof(TType), alignof(TType))) TType(Forward<TArgs>(Args)...);
362 }
363
365 {
366 return Inner.Alloc(Size, Align);
367 }
368
369 template <typename TDesc, typename... TValues>
371
372 #if ENABLE_RHI_VALIDATION
373 // Used by RHI validation for circular reference detection.
374 bool bVisited = false;
375 #endif
376 };
377
378 inline void FRHIBreadcrumbAllocatorArray::AddUnique(FRHIBreadcrumbAllocator* Allocator)
379 {
381 {
382 if (Allocator == &Existing.Get())
383 {
384 return;
385 }
386 }
387
388 Add(Allocator->AsShared());
389 }
390
391 //
392 // A linked list of breadcrumb nodes.
393 // Nodes may only be attached to one list at a time.
394 //
395 struct FRHIBreadcrumbList
396 {
397 FRHIBreadcrumbNode* First = nullptr;
398 FRHIBreadcrumbNode* Last = nullptr;
399
400 void Append(FRHIBreadcrumbNode* Node)
401 {
402 check(Node && Node != FRHIBreadcrumbNode::Sentinel);
403 check(!Node->ListLink);
404
405 if (!First)
406 {
407 First = Node;
408 }
409
410 if (Last)
411 {
412 Last->ListLink = Node;
413 }
414 Last = Node;
415 }
416
417 [[nodiscard]] auto IterateAndUnlink()
418 {
419 struct FResult
420 {
422
423 auto begin() const
424 {
425 struct FIterator
426 {
429
430 FIterator& operator++()
431 {
432 Current = Next;
433 if (Current)
434 {
435 Next = Current->ListLink;
436 Current->ListLink = nullptr;
437 }
438 else
439 {
440 Next = nullptr;
441 }
442
443 return *this;
444 }
445
446 bool operator != (std::nullptr_t) const
447 {
448 return Current != nullptr;
449 }
450
452 {
453 return Current;
454 }
455 };
456
457 FIterator Iterator { nullptr, First };
458 ++Iterator;
459 return Iterator;
460 }
461
462 std::nullptr_t end() const { return nullptr; }
463 };
464
465 FResult Result { First };
466 First = nullptr;
467 Last = nullptr;
468 return Result;
469 }
470
471 };
472
473 //
474 // A range of breadcrumb nodes for a given GPU pipeline.
475 //
477 {
480
481 FRHIBreadcrumbRange() = default;
482
484 : First(SingleNode)
485 , Last(SingleNode)
486 {}
487
489 : First(First)
490 , Last(Last)
491 {}
492
493 //
494 // Links the nodes in the 'Other' range into this range, after the node specified by 'Prev'.
495 // If 'Prev' is nullptr, the other nodes will be inserted at the start of the range.
496 //
498 {
499 // Either both are nullptr, or both are valid
500 check(!Other.First == !Other.Last);
501 check(!First == !Last);
502
503 if (!Other.First)
504 {
505 // Other range has no nodes, nothing to do.
506 return;
507 }
508
509 // Other range should not already be linked beyond its end.
510 check(!Other.Last->GetNextPtr(Pipeline));
511
512 if (!Prev)
513 {
514 // Insert at the front of the range
515 Other.Last->GetNextPtr(Pipeline) = First;
516 First = Other.First;
517
518 if (!Last)
519 {
520 Last = Other.Last;
521 }
522 }
523 else
524 {
525 // Insert after 'Prev' node
526
527 // We shouldn't have a 'Prev' node if the outer range is empty.
528 check(First);
529
530 FRHIBreadcrumbNode* Next = Prev->GetNextPtr(Pipeline);
531 Prev->GetNextPtr(Pipeline) = Other.First;
532 Other.Last->GetNextPtr(Pipeline) = Next;
533
534 if (Last == Prev)
535 {
536 // Range was inserted after all other nodes. Update Last pointer.
537 Last = Other.Last;
538 }
539 }
540 }
541
542 class FOuter;
543 FOuter Enumerate(ERHIPipeline Pipeline) const;
544
545 operator bool() const { return First != nullptr; }
546
547 bool operator == (FRHIBreadcrumbRange const& RHS) const
548 {
549 return First == RHS.First && Last == RHS.Last;
550 }
551
552 bool operator != (FRHIBreadcrumbRange const& RHS) const
553 {
554 return !(*this == RHS);
555 }
556
557 friend uint32 GetTypeHash(FRHIBreadcrumbRange const& Range)
558 {
559 return HashCombineFast(GetTypeHash(Range.First), GetTypeHash(Range.Last));
560 }
561 };
562
563 class FRHIBreadcrumbRange::FOuter
564 {
567
568 public:
570 : Range(Range)
572 {}
573
574 auto begin() const
575 {
576 struct FIterator
577 {
580#if DO_CHECK
582#endif
584
585 bool operator != (std::nullptr_t) const
586 {
587 return Current != nullptr;
588 }
589
591 {
592 return Current;
593 }
594
595 FIterator& operator++()
596 {
597 if (Current == Last)
598 {
599 Current = nullptr;
600 }
601 else
602 {
603 FRHIBreadcrumbNode* Next = Current->GetNextPtr(Pipeline);
604
605 // Next should never be null here. When iterating a non-empty range, we should always expect to reach 'Last' rather than nullptr.
606 checkf(Next, TEXT("Nullptr 'Next' breadcrumb found before reaching the 'Last' breadcrumb in the range. (First: 0x%p, Last: 0x%p, Current: 0x%p)"), First, Last, Current);
607
608 Current = Next;
609 }
610
611 return *this;
612 }
613 };
614
615 return FIterator
616 {
617 Range.First
618 , Range.Last
619 #if DO_CHECK
620 , Range.First
621 #endif
622 , Pipeline
623 };
624 }
625
626 constexpr std::nullptr_t end() const
627 {
628 return nullptr;
629 }
630 };
631
632 inline FRHIBreadcrumbRange::FOuter FRHIBreadcrumbRange::Enumerate(ERHIPipeline Pipeline) const
633 {
634 // Either both must be null, or both must be non-null
635 check(!First == !Last);
636
637 return FOuter { *this, Pipeline };
638 }
639
640 inline void FRHIBreadcrumbNode::SetParent(FRHIBreadcrumbNode* Node)
641 {
642 check(Parent == nullptr || Parent == FRHIBreadcrumbNode::Sentinel);
643 Parent = Node;
644
645 if (Parent && Parent != FRHIBreadcrumbNode::Sentinel && Parent->Allocator != Allocator)
646 {
647 Allocator->Parents.AddUnique(Parent->Allocator);
648 }
649 }
650
651 inline void FRHIBreadcrumbNode::TraceBeginCPU() const
652 {
653#if RHI_BREADCRUMBS_EMIT_CPU
654 if (TraceCpuSpecId)
655 {
656 if (TraceCpuMetadataId > 0)
657 {
658 FCpuProfilerTrace::OutputBeginEventWithMetadata(TraceCpuMetadataId);
659 }
660 else
661 {
662 FCpuProfilerTrace::OutputBeginEvent(TraceCpuSpecId);
663 }
664 }
665#endif
666 }
667 inline void FRHIBreadcrumbNode::TraceEndCPU() const
668 {
669#if RHI_BREADCRUMBS_EMIT_CPU
670 if (TraceCpuSpecId)
671 {
672 if (TraceCpuMetadataId > 0)
673 {
674 FCpuProfilerTrace::OutputEndEventWithMetadata();
675 }
676 else
677 {
678 FCpuProfilerTrace::OutputEndEvent();
679 }
680 }
681#endif
682 }
683
684 inline void FRHIBreadcrumbNode::WalkIn(FRHIBreadcrumbNode const* Node)
685 {
686 #if RHI_BREADCRUMBS_EMIT_CPU
688 {
689 auto Recurse = [](auto const& Recurse, FRHIBreadcrumbNode const* Current) -> void
690 {
691 if (!Current || Current == Sentinel)
692 return;
693
694 Recurse(Recurse, Current->GetParent());
695 Current->TraceBeginCPU();
696 };
697 Recurse(Recurse, Node);
698 }
699 #endif
700 }
701
702 inline void FRHIBreadcrumbNode::WalkInRange(FRHIBreadcrumbNode const* Leaf, FRHIBreadcrumbNode const* Root)
703 {
704 check(Leaf && Root);
705
706 #if RHI_BREADCRUMBS_EMIT_CPU
708 {
709 auto Recurse = [&](auto const& Recurse, FRHIBreadcrumbNode const* Current) -> void
710 {
711 if (Current != Root)
712 {
713 Recurse(Recurse, Current->GetParent());
714 }
715
716 Current->TraceBeginCPU();
717 };
719 }
720 #endif
721 }
722
723 inline void FRHIBreadcrumbNode::WalkOut(FRHIBreadcrumbNode const* Node)
724 {
725 #if RHI_BREADCRUMBS_EMIT_CPU
727 {
728 while (Node && Node != Sentinel)
729 {
730 Node->TraceEndCPU();
731 Node = Node->GetParent();
732 }
733 }
734 #endif
735 }
736
737 inline void FRHIBreadcrumbNode::WalkOutRange(FRHIBreadcrumbNode const* Leaf, FRHIBreadcrumbNode const* Root)
738 {
739 check(Leaf && Root);
740
741 #if RHI_BREADCRUMBS_EMIT_CPU
743 {
744 while (true)
745 {
746 Leaf->TraceEndCPU();
747 if (Leaf == Root)
748 break;
749
750 Leaf = Leaf->GetParent();
751 }
752 }
753 #endif
754 }
755
756 namespace UE::RHI::Breadcrumbs::Private
757 {
758 // Replacement for std::remove_cvref, since this isn't available until C++20.
759 template <typename T>
760 using TRemoveCVRef = std::remove_cv_t<std::remove_reference_t<T>>;
761
762 // Used to control the static_asserts below.
763 template <typename...>
764 struct TFalse { static constexpr bool Value = false; };
765
766 //
767 // The TValue types are used to capture and store vararg format values in RHI breadcrumbs. Capturing values like this means we can avoid the string
768 // formatting cost until we actually need the final string (e.g. we're emitting breadcrumbs to an external profiler, we're running 'profilegpu' etc.).
769 //
770 // The inner FConvert type is used to prepare the TValue when calling FCString::Snprintf to generate the final breadcrumb string.
771 //
772 // This base definition catches all integer, float and enum values.
773 //
774 template <typename T>
775 struct TValue
776 {
777 static constexpr bool bValidType = std::is_integral_v<T> || std::is_floating_point_v<T> || std::is_enum_v<T>;
778
779 T const Value;
780
781 template <typename TArg>
782 TValue(TArg const& Value)
783 : Value(Value)
784 {
785 static_assert(bValidType || TFalse<TArg>::Value, "Type is not compatible with RHI breadcrumbs.");
786 }
787
788 struct FConvert
789 {
790 T const& Inner;
791 FConvert(TValue const& Value)
792 : Inner(Value.Value)
793 {}
794 };
795
797 {
798 Serializer.AppendValue(Value);
799 }
800 };
801
802 // Disallow all pointer types. Provide a specific assert message for TCHAR pointers.
803 template <typename T>
804 struct TValue<T*>
805 {
806 static constexpr bool bValidType = false;
807
808 template <typename... TArgs>
809 TValue(TArgs&&...)
810 {
811 if constexpr (std::is_same_v<std::remove_const_t<T>, TCHAR>)
812 {
813 static_assert(TFalse<TArgs...>::Value,
814 "Do not use raw TCHAR pointers with RHI breadcrumbs. Pass the FString, FName, or string literal instead. If you are certain your TCHAR pointer is a string literal "
815 "(e.g. from a function returning a literal) and you cannot pass that literal directly to an RHI breadcrumb, use the RHI_BREADCRUMB_FORCE_STRING_LITERAL macro to silence "
816 "this static assert. Incorrect use of RHI_BREADCRUMB_FORCE_STRING_LITERAL will lead to use-after-free, as only the raw pointer is retained by the breadcrumb."
817 );
818 }
819 else
820 {
821 static_assert(TFalse<TArgs...>::Value, "RHI breadcrumbs do not support arbitrary pointer types.");
822 }
823 }
824
825 struct FConvert
826 {
828 template <typename... TArgs>
829 FConvert(TArgs&&...)
830 : Inner(0)
831 {}
832 };
833
835 };
836
837 // String literal - keep the string pointer
838 template <size_t N>
839 struct TValue<TCHAR[N]>
840 {
841 static constexpr bool bValidType = true;
842
843 TCHAR const(&Value)[N];
844
845 TValue(TCHAR const(&Value)[N])
846 : Value(Value)
847 {}
848
849 struct FConvert
850 {
851 TCHAR const(&Inner)[N];
852 FConvert(TValue const& Value)
853 : Inner(Value.Value)
854 {}
855 };
856
858 {
859 Serializer.AppendValue(Value);
860 }
861 };
862
863 // FName - keep the FName itself and defer resolving
864 template <>
865 struct TValue<FName>
866 {
867 static constexpr bool bValidType = true;
868
869 FName Value;
870 TValue(FName const& Value)
871 : Value(Value)
872 {}
873
874 struct FConvert
875 {
876 TCHAR Inner[FRHIBreadcrumb::MaxLength];
877 FConvert(TValue const& Name)
878 {
879 Name.Value.ToStringTruncate(Inner);
880 }
881 };
882
884 {
885 Serializer.AppendValue(Value);
886 }
887 };
888
889 // FDebugName - keep the FDebugName itself and defer resolving
890 template <>
891 struct TValue<FDebugName>
892 {
893 static constexpr bool bValidType = true;
894
896 TValue(FDebugName const& Value)
897 : Value(Value)
898 {}
899
900 struct FConvert
901 {
902 TCHAR Inner[FRHIBreadcrumb::MaxLength];
903 FConvert(TValue const& Name)
904 {
905 Name.Value.ToStringTruncate(Inner);
906 }
907 };
908
910 {
911 Serializer.AppendValue(Value);
912 }
913 };
914
915 // FString - Take an immediate copy of the string. Total length is limited by fixed buffer size.
916 template <>
917 struct TValue<FString>
918 {
919 static constexpr bool bValidType = true;
920
921 TCHAR Buffer[FRHIBreadcrumb::MaxLength];
922 TValue(FString const& Value)
923 {
925 }
926
927 struct FConvert
928 {
929 TCHAR const* Inner;
930 FConvert(TValue const& String)
932 {}
933 };
934
936 {
937 Serializer.AppendValue(Buffer);
938 }
939 };
940
941 // Determines if a vararg type is a string literal / value tuple, as defined by RHI_BREADCRUMB_FIELD.
942 template < typename T> struct TIsField { static constexpr bool Value = false; };
943 template <size_t N, typename T> struct TIsField<std::tuple<TCHAR const(&)[N], T>> { static constexpr bool Value = true; };
944
945 // Determines the TValue type used to store a vararg value in a breadcrumb. Also unpacks the value type from RHI_BREADCRUMB_FIELD tuples.
946 template < typename T> struct TGetValueType { using TType = TValue<TRemoveCVRef<T>>; };
947 template <size_t N, typename T> struct TGetValueType<std::tuple<TCHAR const(&)[N], T>> { using TType = TValue<TRemoveCVRef<T>>; };
948
949 // Helper to concatenate index sequences.
950 template <size_t , typename... > struct TConcatIndexSequence;
951 template <size_t N, size_t... Seq> struct TConcatIndexSequence<N, std::index_sequence<Seq...>> { using TType = std::index_sequence<N, Seq...>; };
952
953 // Generates a std::index_sequence containing the indices of the RHI_BREADCRUMB_FIELD tuples in a parameter pack.
954 template <size_t N, typename... > struct TFindFieldIndices;
955 template <size_t N > struct TFindFieldIndices<N > { using TType = std::index_sequence<>; };
956 template <size_t N, typename TFirst, typename... TRest> struct TFindFieldIndices<N, TFirst, TRest...>
957 {
958 using TType = typename std::conditional<TIsField<TFirst>::Value
959 , typename TConcatIndexSequence<N, typename TFindFieldIndices<N + 1, TRest...>::TType>::TType
960 , typename TFindFieldIndices<N + 1, TRest...>::TType
961 >::type;
962 };
963
964 struct FForceNoSprintf {};
965
966 // Infers the size of a string literal character array. Size is zero for any type that is not a string literal.
967 template <typename > struct TStringLiteralSize { static constexpr size_t Value = 0; };
968 template <size_t Size> struct TStringLiteralSize<TCHAR const(&)[Size]> { static constexpr size_t Value = Size; };
969
970 //
971 // Helper traits for dealing wth varargs values. The std::tuple specializations are for the RHI_BREADCRUMB_FIELD values.
972 //
973 // - TDescType : Type passed to the TRHIBreadcrumbDesc template.
974 // - TValueType : The under-lying non-reference type of the vararg. Fields are unpacked.
975 // - TValueRef : A reference to the TValueType type.
976 // - ForwardValue : Similar to std::forward, but also unpacks and forwards the value from a std::tuple RHI_BREADCRUMB_FIELD.
977 //
978 template < class T> struct TFieldTraits { using TDescType = TRemoveCVRef<T>; using TValueType = T; using TValueRef = T ; static TValueRef ForwardValue(auto&& Arg) { return std::forward<TValueRef>(Arg); } };
979 template < class T> struct TFieldTraits<T& > { using TDescType = TRemoveCVRef<T>; using TValueType = T; using TValueRef = T& ; static TValueRef ForwardValue(auto&& Arg) { return std::forward<TValueRef>(Arg); } };
980 template < class T> struct TFieldTraits<T&&> { using TDescType = TRemoveCVRef<T>; using TValueType = T; using TValueRef = T&&; static TValueRef ForwardValue(auto&& Arg) { return std::forward<TValueRef>(Arg); } };
981 template <size_t N, class T> struct TFieldTraits<std::tuple<TCHAR const(&)[N], T >&&> { using TDescType = std::tuple<TCHAR const(&)[N], TRemoveCVRef<T>>; using TValueType = T; using TValueRef = T ; static TValueRef ForwardValue(auto&& Arg) { return std::forward<TValueRef>(std::get<1>(Arg)); } };
982 template <size_t N, class T> struct TFieldTraits<std::tuple<TCHAR const(&)[N], T& >&&> { using TDescType = std::tuple<TCHAR const(&)[N], TRemoveCVRef<T>>; using TValueType = T; using TValueRef = T& ; static TValueRef ForwardValue(auto&& Arg) { return std::forward<TValueRef>(std::get<1>(Arg)); } };
983 template <size_t N, class T> struct TFieldTraits<std::tuple<TCHAR const(&)[N], T&&>&&> { using TDescType = std::tuple<TCHAR const(&)[N], TRemoveCVRef<T>>; using TValueType = T; using TValueRef = T&&; static TValueRef ForwardValue(auto&& Arg) { return std::forward<TValueRef>(std::get<1>(Arg)); } };
984
985 // The breadcrumb macros work with both RHI command lists and RHI contexts. This helper retrieves the relevant RHI command list from both types.
988
989 // Returns the full path string for the breadcrumb currently at the top of the CPU stack,
990 // for either RHI command lists or RHI contexts. Used by the breadcrumb check macros.
991 inline FString GetSafeBreadcrumbPath(auto&& RHICmdList_Or_RHIContext);
992
993 template <size_t Size, bool bHasVarargs>
994 struct TFormatString
995 {
996 TCHAR const(&FormatString)[Size];
997
998 TFormatString(TCHAR const(&FormatString)[Size])
999 : FormatString(FormatString)
1000 {}
1001
1002 protected:
1003 template <typename TValuesTuple, size_t... Indices>
1004 TCHAR const* ToStringImpl(TCHAR const* StaticName, FRHIBreadcrumbNode::FBuffer& Buffer, TValuesTuple const& Values, std::index_sequence<Indices...>) const
1005 {
1006 // Perform type conversions (call ToString() on FName etc)
1007 std::tuple<typename std::tuple_element_t<Indices, TValuesTuple>::FConvert...> Converted
1008 {
1009 std::get<Indices>(Values)...
1010 };
1011
1013 Buffer.Data
1014 , UE_ARRAY_COUNT(Buffer.Data)
1015 , FormatString
1016 , (std::get<Indices>(Converted).Inner)...
1017 );
1018
1019 return Buffer.Data;
1020 }
1021 };
1022
1023 template <>
1024 struct TFormatString<0, true>
1025 {
1026 UE_DEPRECATED(5.6,
1027 "Use of the non-\"_F\" versions of the RHI_BREADCRUMB_EVENT family of macros with printf varargs has been deprecated. "
1028 "The additional values passed to these macros will be ignored, and the raw printf format string will form the name of the breadcrumb. "
1029 "Use the \"_F\" versions of these macros instead (which require both a static name and a format string), or remove the varargs.")
1031 {}
1032
1034 {}
1035
1036 template <typename TValuesTuple, size_t... Indices>
1037 TCHAR const* ToStringImpl(TCHAR const* StaticName, FRHIBreadcrumbNode::FBuffer& Buffer, TValuesTuple const& Values, std::index_sequence<Indices...>) const
1038 {
1039 if constexpr (std::tuple_size_v<TValuesTuple> == 1)
1040 {
1041 using TConvert = typename std::tuple_element_t<0, TValuesTuple>::FConvert;
1042
1043 if constexpr (std::is_same_v<decltype(std::declval<TConvert>().Inner), TCHAR const*>)
1044 {
1045 // The breadcrumb has no format string, and a single vararg which looks like a null terminated TCHAR const* string pointer.
1046 // Just return the pointer directly from the value.
1047 return TConvert(std::get<0>(Values)).Inner;
1048 }
1049 else
1050 {
1051 return StaticName;
1052 }
1053 }
1054 else
1055 {
1056 return StaticName;
1057 }
1058 }
1059 };
1060
1061 template <>
1062 struct TFormatString<0, false>
1063 {
1064 TFormatString(std::nullptr_t)
1065 {}
1066
1067 template <typename TValuesTuple, size_t... Indices>
1068 TCHAR const* ToStringImpl(TCHAR const* StaticName, FRHIBreadcrumbNode::FBuffer& Buffer, TValuesTuple const& Values, std::index_sequence<Indices...>) const
1069 {
1070 return StaticName;
1071 }
1072 };
1073
1074 // Returns true if the given args are compatible with RHI breadcrumbs, i.e. they have matching TValue specializations.
1075 template <typename... TArgs> struct TIsValidArgs { static constexpr bool Value = (TGetValueType<typename TFieldTraits<TArgs>::TDescType>::TType::bValidType && ...); };
1076 template < > struct TIsValidArgs<> { static constexpr bool Value = true; };
1077
1078 //
1079 // Contains the definition of an RHI breadcrumb (i.e. the value types, number and name of fields, etc).
1080 // These are instantiated as static objects in RHI_BREADCRUMB_PRIVATE_DEFINE, which is used by the breadcrumb macros.
1081 //
1082 template <size_t FormatStringSize, typename... TValues>
1083 class TRHIBreadcrumbDesc final : public FRHIBreadcrumbData, public TFormatString<FormatStringSize, sizeof...(TValues) != 0>
1084 {
1085 using TFormatString = TFormatString<FormatStringSize, sizeof...(TValues) != 0>;
1086
1087 mutable FCriticalSection Cs;
1088
1089 template <typename TTuple, size_t... Indices>
1090 static auto ExtractFieldNames(TTuple&& Tuple, std::index_sequence<Indices...>)
1091 {
1092 return std::array<TCHAR const*, sizeof...(Indices)>
1093 {
1094 std::get<0>(std::get<Indices>(Tuple))...
1095 };
1096 }
1097
1098 public:
1099 // Tuple of vararg value types to store in the breadcrumb. Field tuples have been unpacked and the name component discarded.
1100 using TValuesTuple = std::tuple<typename TGetValueType<TValues>::TType...>;
1101 static constexpr size_t NumValues = sizeof...(TValues);
1102
1103 // std::index_sequence for all vararg values.
1104 using TValuesIndexSequence = std::index_sequence_for<TValues...>;
1105
1106 // std::index_sequence defining the indices of the tuple field values
1107 using TFieldsIndexSequence = typename TFindFieldIndices<0, TValues...>::TType;
1108 static constexpr size_t NumFields = TFieldsIndexSequence::size();
1109
1110 // Array of the string literal field names
1111 std::array<TCHAR const*, NumFields> const FieldNames;
1112
1113 // Id of the spec of the GPU events associated with this breadcrumb.
1114 mutable uint32 TraceGpuSpecId = 0;
1115
1116 // Id of the spec of the CPU events associated with this breadcrumb.
1117 mutable std::atomic<uint32> TraceCpuSpecId = 0;
1118
1119 template <typename... TInnerValues>
1122 , TFormatString(FormatString)
1124 {}
1125
1126 TCHAR const* ToString(FRHIBreadcrumbNode::FBuffer& Buffer, TValuesTuple const& Values) const
1127 {
1128 return TFormatString::ToStringImpl(StaticName, Buffer, Values, TValuesIndexSequence());
1129 }
1130
1131 void SerializeValues(UE::RHI::GPUProfiler::FMetadataSerializer& Serializer, TValuesTuple const& Values) const
1132 {
1133 std::apply([&](const auto&... Value)
1134 {
1135 (SerializeValue(Value, Serializer), ...);
1136 }, Values);
1137 }
1138
1139 uint32 GetTraceGpuSpec() const
1140 {
1142 {
1143 if constexpr (FormatStringSize > 0)
1144 {
1145 TraceGpuSpecId = UE::RHI::GPUProfiler::FGpuProfilerTrace::BreadcrumbSpec(StaticName, TFormatString::FormatString, FieldNames);
1146 }
1147 else
1148 {
1150 }
1151 }
1152
1153 return TraceGpuSpecId;
1154 }
1155
1156 uint32 GetTraceCpuSpec() const
1157 {
1158#if CPUPROFILERTRACE_ENABLED
1160 {
1161 FScopeLock Lock(&Cs);
1162
1163 if (TraceCpuSpecId == 0)
1164 {
1165 TraceCpuSpecId = FCpuProfilerTrace::OutputEventType(StaticName
1167 , File, Line
1168#endif
1169 );
1170
1171 if constexpr (FormatStringSize > 0)
1172 {
1174 for (const TCHAR* FieldName : FieldNames)
1175 {
1176 Serializer.AppendValue(FieldName);
1177 }
1178 FCpuProfilerTrace::OutputEventMetadataSpec(TraceCpuSpecId, StaticName, TFormatString::FormatString, Serializer.GetData());
1179 }
1180 }
1181 }
1182#endif
1183 return TraceCpuSpecId;
1184 }
1185
1186 private:
1187 template<typename T>
1188 void SerializeValue(const T& Item, UE::RHI::GPUProfiler::FMetadataSerializer& Serializer) const
1189 {
1190 Item.Serialize(Serializer);
1191 }
1192 };
1193
1194 // Breadcrumb implementation for printf formatted names
1195 // Privately inherit from the TValuesTuple to use empty-base-class optimization for breadcrumbs with no varargs.
1196 template <typename TDesc>
1197 class TRHIBreadcrumb final : public FRHIBreadcrumbNode, private TDesc::TValuesTuple
1198 {
1200 friend FRHIBreadcrumbNode;
1201
1202 TDesc const& Desc;
1203
1204 TRHIBreadcrumb(TRHIBreadcrumb const&) = delete;
1205 TRHIBreadcrumb(TRHIBreadcrumb&& ) = delete;
1206
1207 public:
1208 template <typename... TValues>
1211 , TDesc::TValuesTuple(Forward<TValues>(Values)...)
1212 , Desc(Desc)
1213 {
1214#if RHI_BREADCRUMBS_EMIT_CPU
1215 TraceCpuSpecId = Desc.GetTraceCpuSpec();
1216 if (TraceCpuSpecId)
1217 {
1218 if (Desc.NumValues > 0)
1219 {
1221 Desc.SerializeValues(Serializer, *this);
1222 TraceCpuMetadataId = FCpuProfilerTrace::OutputMetadata(TraceCpuSpecId, Serializer.GetData());
1223 }
1224 }
1225#endif
1226 }
1227
1228 TCHAR const* GetTCHAR(FBuffer& Buffer) const override
1229 {
1230 return Desc.ToString(Buffer, *this);
1231 }
1232
1233 virtual void TraceBeginGPU(uint32 QueueId, uint64 GPUTimestampTOP) const override
1234 {
1235 if (uint32 SpecId = Desc.GetTraceGpuSpec())
1236 {
1238 Desc.SerializeValues(Serializer, *this);
1239 UE::RHI::GPUProfiler::FGpuProfilerTrace::BeginBreadcrumb(SpecId, QueueId, GPUTimestampTOP, Serializer.GetData());
1240 }
1241 }
1242
1243 virtual void TraceEndGPU(uint32 QueueId, uint64 GPUTimestampBOP) const override
1244 {
1245 if (Desc.TraceGpuSpecId)
1246 {
1248 }
1249 }
1250
1251 private:
1252 // Constructor for the sentinel value
1253 TRHIBreadcrumb(TDesc const& Desc)
1254 : FRHIBreadcrumbNode(Desc)
1255 , Desc(Desc)
1256 {}
1257 };
1258 }
1259
1261 {
1262 private:
1263 FRHIBreadcrumbNode* Node = nullptr;
1265
1266 public:
1267 FRHIBreadcrumbNodeRef() = default;
1269 : Node(Node)
1270 {
1271 if (Node && Node != FRHIBreadcrumbNode::Sentinel)
1272 {
1273 AllocatorRef = Node->Allocator->AsShared();
1274 }
1275 }
1276
1277 operator FRHIBreadcrumbNode* () const { return Node; }
1278 operator bool() const { return !!Node; }
1279
1280 FRHIBreadcrumbNode* operator -> () const { return Node; }
1281 FRHIBreadcrumbNode* Get() const { return Node; }
1282 };
1283
1284 struct FRHIBreadcrumbScope
1285 {
1286 FRHIComputeCommandList& RHICmdList;
1287 FRHIBreadcrumbNode* const Node;
1288
1289 private:
1291
1292 public:
1293 template <typename TDesc, typename... TValues>
1295 inline ~FRHIBreadcrumbScope();
1296 };
1297
1298 //
1299 // A helper class to manually create, begin and end a breadcrumb on a given RHI command list.
1300 // For use in places where the Begin/End operations are separate, and a scoped breadcrumb event is not appropriate.
1301 //
1303 {
1304 // Must be a reference. End() may be called with a different RHI command list than the one we
1305 // received in the constructor, so we need to keep the underlying RHI breadcrumb allocator alive.
1307 #if DO_CHECK
1308 ERHIPipeline const Pipeline;
1309 uint32 ThreadId;
1310 #endif
1311
1313
1314 public:
1315 template <typename TDesc, typename... TValues>
1317
1318 inline void End(FRHIComputeCommandList& RHICmdList);
1319
1321 };
1322
1323 // Private macro used to define a new breadcrumb type. Also forwards the given values through to a returned TRHIBreadcrumbInitializer,
1324 // which can then be passed to the constructors for FRHIBreadcrumbScope / FRHIBreadcrumbEventManual, or the allocator AllocBreadcrumb() function.
1325 #define RHI_BREADCRUMB_PRIVATE_DEFINE(StaticName, FormatString, ValueType, GPUStat) \
1326 [&](auto&&... Values) \
1327 { \
1328 using namespace UE::RHI::Breadcrumbs::Private; \
1329 \
1330 TRHIBreadcrumbDesc< \
1331 TStringLiteralSize<decltype(FormatString)>::Value, \
1332 typename TFieldTraits<decltype(Values)>::TDescType... \
1333 > static const Desc( \
1334 FRHIBreadcrumbData(StaticName, __FILE__, __LINE__, GPUStat) \
1335 , FormatString \
1336 , Values... \
1337 ); \
1338 \
1339 return std::tuple( \
1340 &Desc, \
1341 std::tuple<typename TFieldTraits<decltype(Values)>::ValueType...>( \
1342 TFieldTraits<decltype(Values)>::ForwardValue(Values)... \
1343 ) \
1344 ); \
1345 }
1346
1347 #define RHI_BREADCRUMB_DESC_FORWARD_VALUES(StaticName, FormatString, GPUStat) RHI_BREADCRUMB_PRIVATE_DEFINE(StaticName, FormatString, TValueRef , GPUStat)
1348 #define RHI_BREADCRUMB_DESC_COPY_VALUES( StaticName, FormatString, GPUStat) RHI_BREADCRUMB_PRIVATE_DEFINE(StaticName, FormatString, TValueType, GPUStat)
1349
1350 #if RHI_NEW_GPU_PROFILER && HAS_GPU_STATS
1351 #define RHI_GPU_STAT_ARGS(StatName) FRHIBreadcrumbData_Stats(&GPUStat_##StatName)
1352 #define RHI_GPU_STAT_ARGS_NONE FRHIBreadcrumbData_Stats(nullptr)
1353 #elif HAS_GPU_STATS
1354 #define RHI_GPU_STAT_ARGS(StatName) FRHIBreadcrumbData_Stats(GET_STATID(Stat_GPU_##StatName), CSV_STAT_FNAME(StatName))
1355 #define RHI_GPU_STAT_ARGS_NONE FRHIBreadcrumbData_Stats(TStatId(), NAME_None)
1356 #else
1357 #define RHI_GPU_STAT_ARGS(StatName) FRHIBreadcrumbData_Stats()
1358 #define RHI_GPU_STAT_ARGS_NONE FRHIBreadcrumbData_Stats()
1359 #endif
1360
1361 // Varargs in breadcrumb macros can be given a name by wrapping them with this macro.
1362 // Named fields are exposed to Unreal Insights as metadata on event markers.
1363 #define RHI_BREADCRUMB_FIELD(Name, Value) std::forward_as_tuple(TEXT(Name), Value)
1364
1365 #define RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, Stat, Condition, StaticName, Format, ...)\
1366 TOptional<FRHIBreadcrumbScope> PREPROCESSOR_JOIN(BreadcrumbScope, __LINE__); \
1367 do \
1368 { \
1369 if (Condition) \
1370 { \
1371 PREPROCESSOR_JOIN(BreadcrumbScope, __LINE__).Emplace( \
1372 UE::RHI::Breadcrumbs::Private::GetRHICmdList(RHICmdList_Or_RHIContext), \
1373 RHI_BREADCRUMB_DESC_FORWARD_VALUES( \
1374 StaticName \
1375 , Format \
1376 , Stat \
1377 )(__VA_ARGS__) \
1378 ); \
1379 } \
1380 } while(false)
1381
1382#endif // WITH_RHI_BREADCRUMBS
1383
1384#if WITH_RHI_BREADCRUMBS_FULL
1385
1386 // Note, the varargs are deprecated and ignored in these macros.
1387 #define RHI_BREADCRUMB_EVENT( RHICmdList_Or_RHIContext, StaticName, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS_NONE , true, TEXT(StaticName), nullptr, ##__VA_ARGS__)
1388 #define RHI_BREADCRUMB_EVENT_CONDITIONAL( RHICmdList_Or_RHIContext, Condition, StaticName, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS_NONE , Condition, TEXT(StaticName), nullptr, ##__VA_ARGS__)
1389 #define RHI_BREADCRUMB_EVENT_STAT( RHICmdList_Or_RHIContext, Stat, StaticName, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), true, TEXT(StaticName), nullptr, ##__VA_ARGS__)
1390 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_STAT( RHICmdList_Or_RHIContext, Stat, Condition, StaticName, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), Condition, TEXT(StaticName), nullptr, ##__VA_ARGS__)
1391
1392 // Format versions of the breadcrumb macros.
1393 #define RHI_BREADCRUMB_EVENT_F( RHICmdList_Or_RHIContext, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS_NONE , true, TEXT(StaticName), TEXT(Format), ##__VA_ARGS__)
1394 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_F( RHICmdList_Or_RHIContext, Condition, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS_NONE , Condition, TEXT(StaticName), TEXT(Format), ##__VA_ARGS__)
1395 #define RHI_BREADCRUMB_EVENT_STAT_F( RHICmdList_Or_RHIContext, Stat, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), true, TEXT(StaticName), TEXT(Format), ##__VA_ARGS__)
1396 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_STAT_F( RHICmdList_Or_RHIContext, Stat, Condition, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), Condition, TEXT(StaticName), TEXT(Format), ##__VA_ARGS__)
1397
1398 // Used only for back compat with SCOPED_DRAW_EVENTF
1399 #define RHI_BREADCRUMB_EVENT_F_STR_DEPRECATED( RHICmdList_Or_RHIContext, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS_NONE , true, TEXT(StaticName), Format , ##__VA_ARGS__)
1400 #define RHI_BREADCRUMB_EVENT_F_CONDITIONAL_STR_DEPRECATED(RHICmdList_Or_RHIContext, Condition, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS_NONE , Condition, TEXT(StaticName), Format , ##__VA_ARGS__)
1401
1402#elif WITH_RHI_BREADCRUMBS_MINIMAL
1403
1404 //
1405 // Keep only the STAT breadcrumbs enabled in MINIMAL mode.
1406 // Also disable the varargs. We don't capture the format strings and varargs in MINIMAL mode
1407 //
1408
1409 // Note, the varargs are deprecated and ignored in these macros.
1410 #define RHI_BREADCRUMB_EVENT( RHICmdList_Or_RHIContext, StaticName, ...)
1411 #define RHI_BREADCRUMB_EVENT_CONDITIONAL( RHICmdList_Or_RHIContext, Condition, StaticName, ...)
1412 #define RHI_BREADCRUMB_EVENT_STAT( RHICmdList_Or_RHIContext, Stat, StaticName, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), true, TEXT(StaticName), nullptr)
1413 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_STAT( RHICmdList_Or_RHIContext, Stat, Condition, StaticName, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), Condition, TEXT(StaticName), nullptr)
1414
1415 // Format versions of the breadcrumb macros.
1416 #define RHI_BREADCRUMB_EVENT_F( RHICmdList_Or_RHIContext, StaticName, Format, ...)
1417 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_F( RHICmdList_Or_RHIContext, Condition, StaticName, Format, ...)
1418 #define RHI_BREADCRUMB_EVENT_STAT_F( RHICmdList_Or_RHIContext, Stat, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), true, TEXT(StaticName), nullptr)
1419 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_STAT_F( RHICmdList_Or_RHIContext, Stat, Condition, StaticName, Format, ...) RHI_BREADCRUMB_EVENT_PRIVATE_IMPL(RHICmdList_Or_RHIContext, RHI_GPU_STAT_ARGS(Stat), Condition, TEXT(StaticName), nullptr)
1420
1421 // Used only for back compat with SCOPED_DRAW_EVENTF
1422 #define RHI_BREADCRUMB_EVENT_F_STR_DEPRECATED( RHICmdList_Or_RHIContext, StaticName, Format, ...)
1423 #define RHI_BREADCRUMB_EVENT_F_CONDITIONAL_STR_DEPRECATED(RHICmdList_Or_RHIContext, Condition, StaticName, Format, ...)
1424
1425#else
1426
1427 #define RHI_BREADCRUMB_FIELD( ...)
1428 #define RHI_BREADCRUMB_EVENT( ...)
1429 #define RHI_BREADCRUMB_EVENT_CONDITIONAL( ...)
1430 #define RHI_BREADCRUMB_EVENT_STAT( ...)
1431 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_STAT( ...)
1432 #define RHI_BREADCRUMB_EVENT_F( ...)
1433 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_F( ...)
1434 #define RHI_BREADCRUMB_EVENT_STAT_F( ...)
1435 #define RHI_BREADCRUMB_EVENT_CONDITIONAL_STAT_F( ...)
1436 #define RHI_BREADCRUMB_EVENT_F_STR_DEPRECATED( ...)
1437 #define RHI_BREADCRUMB_EVENT_F_CONDITIONAL_STR_DEPRECATED(...)
1438
1439#endif
1440
1441#if WITH_RHI_BREADCRUMBS_FULL
1442
1443 // Log and check macros that include the current breadcrumb path
1444 #define RHI_BREADCRUMB_LOG( RHICmdList_Or_RHIContext, CategoryName, Verbosity, Format, ...) UE_LOG( CategoryName, Verbosity, Format TEXT("\nBreadcrumbs: %s"), ##__VA_ARGS__, *UE::RHI::Breadcrumbs::Private::GetSafeBreadcrumbPath(RHICmdList_Or_RHIContext))
1445 #define RHI_BREADCRUMB_CLOG( RHICmdList_Or_RHIContext, Condition, CategoryName, Verbosity, Format, ...) UE_CLOG(Condition, CategoryName, Verbosity, Format TEXT("\nBreadcrumbs: %s"), ##__VA_ARGS__, *UE::RHI::Breadcrumbs::Private::GetSafeBreadcrumbPath(RHICmdList_Or_RHIContext))
1446 #define RHI_BREADCRUMB_CHECKF(RHICmdList_Or_RHIContext, Condition, Format, ...) checkf( Condition, Format TEXT("\nBreadcrumbs: %s"), ##__VA_ARGS__, *UE::RHI::Breadcrumbs::Private::GetSafeBreadcrumbPath(RHICmdList_Or_RHIContext))
1447
1448#else
1449
1450 // Log and check macros fall back to regular UE_LOG / check when breadcrumbs are not available
1451 #define RHI_BREADCRUMB_LOG( RHICmdList_Or_RHIContext, CategoryName, Verbosity, Format, ...) UE_LOG( CategoryName, Verbosity, Format, ##__VA_ARGS__)
1452 #define RHI_BREADCRUMB_CLOG( RHICmdList_Or_RHIContext, Condition, CategoryName, Verbosity, Format, ...) UE_CLOG(Condition, CategoryName, Verbosity, Format, ##__VA_ARGS__)
1453 #define RHI_BREADCRUMB_CHECKF(RHICmdList_Or_RHIContext, Condition, Format, ...) checkf( Condition, Format, ##__VA_ARGS__)
1454
1455#endif
1456
1457#if DO_CHECK
1458 #define RHI_BREADCRUMB_CHECK_SHIPPINGF(RHICmdList_Or_RHIContext, Condition, Format, ...) RHI_BREADCRUMB_CHECKF(RHICmdList_Or_RHIContext, Condition, Format, ##__VA_ARGS__)
1459#else
1460 #define RHI_BREADCRUMB_CHECK_SHIPPINGF(RHICmdList_Or_RHIContext, Condition, Format, ...) RHI_BREADCRUMB_CLOG(RHICmdList_Or_RHIContext, !(Condition), LogRHI, Error, TEXT("Check '%s' failed. ") Format, TEXT(#Condition), ##__VA_ARGS__)
1461#endif
1462
1463#define RHI_BREADCRUMB_CHECK_SHIPPING(RHICmdList_Or_RHIContext, Condition) RHI_BREADCRUMB_CHECK_SHIPPINGF(RHICmdList_Or_RHIContext, Condition, TEXT(""))
1464#define RHI_BREADCRUMB_CHECK( RHICmdList_Or_RHIContext, Condition) RHI_BREADCRUMB_CHECKF( RHICmdList_Or_RHIContext, Condition, TEXT(""))
1465
1466//
1467// Used to override the static_assert check for string literals in RHI breadcrumbs. This is required when using
1468// literals returned by functions, or choosing between two string literals with a ternary operator, like so:
1469//
1470// SCOPED_DRAW_EVENTF(RHICmdList, EventName, TEXT("Name=%s"), RHI_BREADCRUMB_FORCE_STRING_LITERAL(bCondition ? TEXT("True") : TEXT("False"))
1471//
1472// !! DO NOT USE THIS MACRO FOR NON-STRING LITERALS !!
1473//
1474#define RHI_BREADCRUMB_FORCE_STRING_LITERAL [](auto&& TCharPointer) -> TCHAR const(&)[1]\
1475 { \
1476 return *reinterpret_cast<TCHAR const(*)[1]>(TCharPointer); \
1477 }
constexpr T Align(T Val, uint64 Alignment)
Definition AlignmentTemplates.h:18
#define check(expr)
Definition AssertionMacros.h:314
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
UE_FORCEINLINE_HINT FLinearColor operator*(float Scalar, const FLinearColor &Color)
Definition Color.h:473
#define UE_DEPRECATED(Version, Message)
Definition CoreMiscDefines.h:302
@ InPlace
Definition CoreMiscDefines.h:162
#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::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
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define TRACE_CPUPROFILER_EVENT_MANUAL_IS_ENABLED()
Definition CpuProfilerTrace.h:520
UE::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
return true
Definition ExternalRpcRegistry.cpp:601
UE_FORCEINLINE_HINT bool operator!=(const FIndexedPointer &Other) const
Definition LockFreeList.h:76
const bool
Definition NetworkReplayStreaming.h:178
#define RHI_BREADCRUMBS_EMIT_LOCATION
Definition RHIBreadcrumbs.h:38
constexpr uint32 GetRHIPipelineIndex(ERHIPipeline Pipeline)
Definition RHIPipeline.h:28
ERHIPipeline
Definition RHIPipeline.h:13
constexpr uint32 HashCombineFast(uint32 A, uint32 B)
Definition TypeHash.h:74
uint32 PointerHash(const void *Key)
Definition TypeHash.h:91
#define UE_ARRAY_COUNT(array)
Definition UnrealTemplate.h:212
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
FRWLock Lock
Definition UnversionedPropertySerialization.cpp:921
uint32 Size
Definition VulkanMemory.cpp:4034
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition RHIDefinitions.h:95
Definition MemStack.h:78
Definition NameTypes.h:617
Definition RHICommandList.h:2735
Definition ScopeLock.h:141
Definition RHIContext.h:257
Definition Array.h:670
UE_FORCEINLINE_HINT SizeType AddUnique(ElementType &&Item)
Definition Array.h:2993
Definition UnrealString.h.inl:34
Definition RHIPipeline.h:55
Definition SharedPointer.h:1640
Definition SharedPointer.h:692
Definition SharedPointer.h:153
Definition StaticArray.h:26
Definition GpuProfilerTrace.h:69
RHI_API void AppendValue(const ANSICHAR *Value)
Definition GpuProfilerTrace.cpp:490
const TArray< uint8 > & GetData() const
Definition GpuProfilerTrace.h:115
FORCEINLINE void InsertAfter(const ElementType &NewNode, ListType &List, NodeType *Node)
Definition ChunkSearch.h:32
GeometryCollection::Facades::FMuscleActivationData Data
Definition MuscleActivationConstraints.h:15
@ Range
Definition EnvQueryTypes.h:81
uint32 GetTypeHash(const FKey &Key)
Definition BlackboardKey.h:35
FString ToString(uint16 Value)
Definition PathFollowingComponent.cpp:82
void * Alloc(int size)
FORCEINLINE T * Get(const FObjectPtr &ObjectPtr)
Definition ObjectPtr.h:426
FORCEINLINE FStridedReferenceIterator begin(FStridedReferenceView View)
Definition FastReferenceCollector.h:490
FORCEINLINE FStridedReferenceIterator end(FStridedReferenceView View)
Definition FastReferenceCollector.h:491
TValueOrError< void, UE::UnifiedError::FError > FResult
Definition OnDemandError.h:32
FString ToStringImpl(const IdType &Id)
Definition CoreOnline.cpp:495
Definition GpuProfilerTrace.cpp:270
UE_STRING_CLASS Result(Forward< LhsType >(Lhs), RhsLen)
Definition String.cpp.inl:732
@ MaxLength
[PropertyMetadata] Used for FString and FText properties. Indicates the maximum length of the value t...
Definition ObjectMacros.h:1449
@ false
Definition radaudio_common.h:23
Definition GenericPlatformCrashContext.h:300
static int32 Snprintf(CharType *Dest, int32 DestSize, const FmtType &Fmt, Types... Args)
Definition CString.h:581
static CharType * Strncpy(CharType *Dest, const CharType *Src, SIZE_T MaxLen)
Definition CString.h:991
Definition LightweightStats.h:416
Definition Tuple.h:652
Definition GPUProfiler.h:524
static void BeginBreadcrumb(uint32 SpecId, uint32 QueueId, uint64 GPUTimestampTOP, const TArray< uint8 > &CborData)
Definition GpuProfilerTrace.h:211
static void EndBreadcrumb(uint32 QueueId, uint64 GPUTimestampBOP)
Definition GpuProfilerTrace.h:212
static uint32 BreadcrumbSpec(const TCHAR *StaticName, const TCHAR *NameFormat, const std::array< const TCHAR *, Size > &FieldNames)
Definition GpuProfilerTrace.h:221
static bool IsAvailable()
Definition GpuProfilerTrace.h:207