// Copyright Epic Games, Inc. All Rights Reserved. #include "GpuProfilerTraceAnalysis.h" #include "CborReader.h" #include "CborWriter.h" #include "HAL/LowLevelMemTracker.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" // TraceServices #include "AnalysisServicePrivate.h" #include "Common/Utils.h" #include "Model/CountersPrivate.h" #include "Model/TimingProfilerPrivate.h" #define GPUPROFILER_LOG_API_L1(Format, ...) UE_LOG(LogTraceServices, Log, Format, ##__VA_ARGS__) #define GPUPROFILER_LOG_API_L2(Format, ...) //UE_LOG(LogTraceServices, Verbose, Format, ##__VA_ARGS__) #define GPUPROFILER_LOG_API_L3(Format, ...) //UE_LOG(LogTraceServices, VeryVerbose, Format, ##__VA_ARGS__) namespace TraceServices { //////////////////////////////////////////////////////////////////////////////////////////////////// FGpuProfilerAnalyzer::FGpuProfilerAnalyzer(FAnalysisSession& InSession, FTimingProfilerProvider& InTimingProfilerProvider, IEditableCounterProvider& InEditableCounterProvider) : Session(InSession) , TimingProfilerProvider(InTimingProfilerProvider) , EditableCounterProvider(InEditableCounterProvider) { } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::OnAnalysisBegin(const FOnAnalysisContext& Context) { auto& Builder = Context.InterfaceBuilder; Builder.RouteEvent(RouteId_Init, "GpuProfiler", "Init"); Builder.RouteEvent(RouteId_QueueSpec, "GpuProfiler", "QueueSpec"); Builder.RouteEvent(RouteId_EventFrameBoundary, "GpuProfiler", "EventFrameBoundary"); Builder.RouteEvent(RouteId_EventBreadcrumbSpec, "GpuProfiler", "EventBreadcrumbSpec"); Builder.RouteEvent(RouteId_EventBeginBreadcrumb, "GpuProfiler", "EventBeginBreadcrumb"); Builder.RouteEvent(RouteId_EventEndBreadcrumb, "GpuProfiler", "EventEndBreadcrumb"); Builder.RouteEvent(RouteId_EventBeginWork, "GpuProfiler", "EventBeginWork"); Builder.RouteEvent(RouteId_EventEndWork, "GpuProfiler", "EventEndWork"); Builder.RouteEvent(RouteId_EventWait, "GpuProfiler", "EventWait"); Builder.RouteEvent(RouteId_EventStats, "GpuProfiler", "EventStats"); Builder.RouteEvent(RouteId_SignalFence, "GpuProfiler", "SignalFence"); Builder.RouteEvent(RouteId_WaitFence, "GpuProfiler", "WaitFence"); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::OnAnalysisEnd() { if (ErrorData.NumInterleavedEvents > 0) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Number of interleaved events: %u, max error %f"), ErrorData.NumInterleavedEvents, ErrorData.InterleavedEventsMaxDelta); } if (ErrorData.NumInterleavedAndReversedEvents > 0) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Number of interleaved and reversed events: %u, max error %f"), ErrorData.NumInterleavedAndReversedEvents, ErrorData.InterleavedAndReversedEventsMaxDelta); } if (ErrorData.NumMismatchedEvents > 0) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Number of mismatched events: %u"), ErrorData.NumMismatchedEvents); } if (ErrorData.NumNegativeDurationEvents > 0) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Number of negative duration events: %u, max error %f"), ErrorData.NumNegativeDurationEvents, ErrorData.NegativeDurationEventsMaxDelta); } if (ErrorData.NumTimerWarnings > 0) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Number of timer/metadata warnings: %u"), ErrorData.NumTimerWarnings); } if (Queues.Num() > 0 || TimerMap.Num() > 0) { UE_LOG(LogTraceServices, Log, TEXT("[GpuProfiler] Analysis completed (%u queues, %d timers, %d breadcrumb specs, %d breadcrumb names)."), Queues.Num(), TimerMap.Num(), SpecIdToTimerIdMap.Num(), BreadcrumbMap.Num()); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FGpuProfilerAnalyzer::OnEvent(uint16 RouteId, EStyle Style, const FOnEventContext& Context) { LLM_SCOPE_BYNAME(TEXT("Insights/FGpuProfilerAnalyzer")); const auto& EventData = Context.EventData; switch (RouteId) { case RouteId_Init: { Version = (uint32)EventData.GetValue("Version"); GPUPROFILER_LOG_API_L1(TEXT("[GpuProfiler] Init Version=%u"), Version); break; } case RouteId_QueueSpec: { const uint32 QueueId = EventData.GetValue("QueueId"); const uint8 GPU = (QueueId >> 8) & 0xFF; const uint8 Index = (QueueId >> 16) & 0xFF; const uint8 Type = QueueId & 0xFF; FString Name; EventData.GetString("TypeString", Name); GPUPROFILER_LOG_API_L2(TEXT("[GpuProfiler] QueueSpec QueueId=%u, GPU=%u, Index=%u, Type=%d \"%s\""), QueueId, (uint32)GPU, (uint32)Index, (uint32)Type, *Name); const TCHAR* PersistentName = Session.StoreString(Name); { FAnalysisSessionEditScope _(Session); TimingProfilerProvider.AddGpuQueue(QueueId, GPU, Index, Type, PersistentName); } FQueue& Queue = GetOrAddQueue(QueueId); InitCountersDesc(Queue, GPU, Index, *Name); break; } case RouteId_EventFrameBoundary: { const uint32 QueueId = EventData.GetValue("QueueId"); const uint32 FrameNumber = EventData.GetValue("FrameNumber"); GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] EventFrameBoundary QueueId=%u, FrameNumber=%u"), QueueId, FrameNumber); FQueue& Queue = GetOrAddQueue(QueueId); Queue.FrameNumber = FrameNumber; if (Queue.LastTime > 0) { FAnalysisSessionEditScope _(Session); Queue.NumDrawsCounter->SetValue(Queue.LastTime, (int64)Queue.NumDraws); Queue.NumPrimitivesCounter->SetValue(Queue.LastTime, (int64)Queue.NumPrimitives); } Queue.NumDraws = 0; Queue.NumPrimitives = 0; Queue.LastTime = 0; break; } case RouteId_EventBreadcrumbSpec: { OnEventBreadcrumbSpec(Context); break; } case RouteId_EventBeginBreadcrumb: { const uint32 SpecId = EventData.GetValue("SpecId"); const uint32 QueueId = EventData.GetValue("QueueId"); const uint64 GPUTimestampTOP = EventData.GetValue("GPUTimestampTOP"); // Ignore events for which the timestamp could not be determined. if (GPUTimestampTOP == 0) { break; } const double Time = Context.EventTime.AsSeconds(GPUTimestampTOP); TArray Metadata(EventData.GetArrayView("Metadata")); GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] EventBeginBreadcrumb QueueId=%u, Time=%f, SpecId=\"%u\""), QueueId, Time, SpecId); uint32 TimerId = GetOrAddTimer(SpecId, TEXT("")); if (Metadata.Num() > 0) // excludes empty metadata { FAnalysisSessionEditScope _(Session); TimerId = TimingProfilerProvider.AddMetadata(TimerId, MoveTemp(Metadata)); } #if 0 // for debugging only else { FAnalysisSessionEditScope _(Session); TimingProfilerProvider.ReadTimers( [TimerId] (const ITimingProfilerTimerReader& TimerReader) { const FTimingProfilerTimer* Timer = TimerReader.GetTimer(TimerId); check(Timer); if (Timer->HasValidMetadataSpecId()) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Empty metadata for a timing event while its timer %u (%s) has valid metadata spec id (%u) !!!"), TimerId, Timer->Name, Timer->MetadataSpecId); } }); } #endif FQueue& Queue = GetOrAddQueue(QueueId); BeginEvent(Queue, 0, Time, TimerId); break; } case RouteId_EventEndBreadcrumb: { const uint32 QueueId = EventData.GetValue("QueueId"); const uint64 GPUTimestampBOP = EventData.GetValue("GPUTimestampBOP"); // Ignore events for which the timestamp could not be determined. if (GPUTimestampBOP == 0) { break; } const double Time = Context.EventTime.AsSeconds(GPUTimestampBOP); GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] EventEndBreadcrumb QueueId=%u, Time=%f"), QueueId, Time); FQueue& Queue = GetOrAddQueue(QueueId); EndEvent(Queue, 0, Time, InvalidTimerId); break; } case RouteId_EventBeginWork: { const uint32 QueueId = EventData.GetValue("QueueId"); const uint64 GPUTimestampTOP = EventData.GetValue("GPUTimestampTOP"); double Time = Context.EventTime.AsSeconds(GPUTimestampTOP); //const uint64 CPUTimestamp = EventData.GetValue("CPUTimestamp"); // not used GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] EventBeginWork QueueId=%u, GPUTimestampTOP=%f"), QueueId, Time); if (GpuWorkTimerId == InvalidTimerId) { const FString GpuWorkTimerName(TEXT("GpuWork")); GpuWorkTimerId = GetOrAddTimer(~0u, GpuWorkTimerName); } FQueue& Queue = GetOrAddQueue(QueueId); TArray CborData; { CborData.Reserve(256); FMemoryWriter MemoryWriter(CborData, false, true); FCborWriter CborWriter(&MemoryWriter, ECborEndianness::StandardCompliant); CborWriter.WriteContainerStart(ECborCode::Map, 1); // 1 is the FieldCount CborWriter.WriteValue("Frame", 5); CborWriter.WriteValue((int64)Queue.FrameNumber); } uint32 MetadataTimerId; { FAnalysisSessionEditScope _(Session); MetadataTimerId = TimingProfilerProvider.AddMetadata(GpuWorkTimerId, MoveTemp(CborData)); } BeginEvent(Queue, 1, Time, MetadataTimerId); break; } case RouteId_EventEndWork: { uint32 QueueId = EventData.GetValue("QueueId"); const uint64 GPUTimestampBOP = EventData.GetValue("GPUTimestampBOP"); const double Time = Context.EventTime.AsSeconds(GPUTimestampBOP); GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] EventEndWork QueueId=%u, GPUTimestampBOP=%f"), QueueId, Time); FQueue& Queue = GetOrAddQueue(QueueId); EndEvent(Queue, 1, Time, GpuWorkTimerId); break; } case RouteId_EventWait: { uint32 QueueId = EventData.GetValue("QueueId"); const uint64 StartTimestamp = EventData.GetValue("StartTime"); const double StartTime = Context.EventTime.AsSeconds(StartTimestamp); const uint64 EndTimestamp = EventData.GetValue("EndTime"); const double EndTime = Context.EventTime.AsSeconds(EndTimestamp); GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] EventWait QueueId=%u, StartTime=%f, EndTime=%f"), QueueId, StartTime, EndTime); if (GpuWaitTimerId == InvalidTimerId) { const FString GpuWaitTimerName(TEXT("GpuWait")); GpuWaitTimerId = GetOrAddTimer(~0u, GpuWaitTimerName); } FQueue& Queue = GetOrAddQueue(QueueId); TArray CborData; { CborData.Reserve(256); FMemoryWriter MemoryWriter(CborData, false, true); FCborWriter CborWriter(&MemoryWriter, ECborEndianness::StandardCompliant); CborWriter.WriteContainerStart(ECborCode::Map, 1); // 1 is the FieldCount CborWriter.WriteValue("Frame", 5); CborWriter.WriteValue((int64)Queue.FrameNumber); } uint32 MetadataTimerId; { FAnalysisSessionEditScope _(Session); MetadataTimerId = TimingProfilerProvider.AddMetadata(GpuWaitTimerId, MoveTemp(CborData)); } BeginEvent(Queue, 1, StartTime, MetadataTimerId); EndEvent(Queue, 1, EndTime, GpuWaitTimerId); break; } case RouteId_EventStats: { const uint32 QueueId = EventData.GetValue("QueueId"); const uint32 NumDraws = EventData.GetValue("NumDraws"); const uint32 NumPrimitives = EventData.GetValue("NumPrimitives"); FQueue& Queue = GetOrAddQueue(QueueId); Queue.NumDraws += NumDraws; Queue.NumPrimitives += NumPrimitives; break; } case RouteId_SignalFence: { uint32 QueueId = EventData.GetValue("QueueId"); uint64 Timestamp = EventData.GetValue("CPUTimestamp"); uint64 Value = EventData.GetValue("Value");; FQueue& Queue = GetOrAddQueue(QueueId); FGpuSignalFence Fence; Fence.Timestamp = Context.EventTime.AsSeconds(Timestamp); Fence.Value = Value; FAnalysisSessionEditScope _(Session); TimingProfilerProvider.AddGpuSignalFence(QueueId, Fence); break; } case RouteId_WaitFence: { uint32 QueueId = EventData.GetValue("QueueId"); uint64 Timestamp = EventData.GetValue("CPUTimestamp"); uint32 QueueToWaitForId = EventData.GetValue("QueueToWaitForId"); uint64 Value = EventData.GetValue("Value");; FGpuWaitFence Fence; Fence.Timestamp = Context.EventTime.AsSeconds(Timestamp); Fence.Value = Value; Fence.QueueToWaitForId = QueueToWaitForId; FAnalysisSessionEditScope _(Session); TimingProfilerProvider.AddGpuWaitFence(QueueId, Fence); break; } } // switch (RouteId) return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// FGpuProfilerAnalyzer::FQueue& FGpuProfilerAnalyzer::GetOrAddQueue(uint32 QueueId) { FQueue* FoundQueue = Queues.Find(QueueId); if (FoundQueue) { return *FoundQueue; } FQueue& NewQueue = Queues.Add(QueueId); NewQueue.Id = QueueId; InitCounters(NewQueue); return NewQueue; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::BeginEvent(FQueue& Queue, int32 StackIndex, double BeginEventTime, uint32 BeginEventTimerId) { Queue.Stack[StackIndex].Push({ BeginEventTime, BeginEventTimerId }); Queue.LastTime = BeginEventTime; FAnalysisSessionEditScope _(Session); IEditableTimeline* Timeline = (StackIndex == 1) ? TimingProfilerProvider.GetGpuQueueWorkEditableTimeline(Queue.Id) : TimingProfilerProvider.GetGpuQueueEditableTimeline(Queue.Id); if (ensure(Timeline)) { FTimingProfilerEvent Event; Event.TimerIndex = BeginEventTimerId; const double LastTimestamp = Timeline->GetLastTimestamp(); if (BeginEventTime < LastTimestamp) { ++ErrorData.NumInterleavedEvents; ErrorData.InterleavedEventsMaxDelta = FMath::Max(ErrorData.InterleavedEventsMaxDelta, LastTimestamp - BeginEventTime); if (++ErrorData.NumWarnings < ErrorData.NumMaxWarnings) { uint32 OriginalBeginEventTimerId = TimingProfilerProvider.GetOriginalTimerIdFromMetadata(BeginEventTimerId); UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Queue %u %s BEGIN %d \"%s\" : Time %f < %f (last) !!!"), Queue.Id, StackIndex == 1 ? TEXT("WORK") : TEXT("BREADCRUMB"), int32(BeginEventTimerId), GetTimerName(OriginalBeginEventTimerId), BeginEventTime, LastTimestamp); } BeginEventTime = LastTimestamp; } Timeline->AppendBeginEvent(BeginEventTime, Event); Session.UpdateDurationSeconds(BeginEventTime); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::EndEvent(FQueue& Queue, int32 StackIndex, double EndEventTime, uint32 EndEventTimerId) { Queue.LastTime = EndEventTime; if (Queue.Stack[StackIndex].IsEmpty()) { return; } FOpenEvent BeginEvent = Queue.Stack[StackIndex].Pop(EAllowShrinking::No); uint32 BeginEventTimerId; { FAnalysisSessionReadScope _(Session); BeginEventTimerId = TimingProfilerProvider.GetOriginalTimerIdFromMetadata(BeginEvent.TimerId); } if (EndEventTimerId != InvalidTimerId && EndEventTimerId != BeginEventTimerId) { ++ErrorData.NumMismatchedEvents; if (++ErrorData.NumWarnings < ErrorData.NumMaxWarnings) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Queue %u %s END %d \"%s\" (%f) != BEGIN %d \"%s\" (%f) !!!"), Queue.Id, StackIndex == 1 ? TEXT("WORK") : TEXT("BREADCRUMB"), int32(EndEventTimerId), GetTimerName(EndEventTimerId), EndEventTime, int32(BeginEvent.TimerId), GetTimerName(BeginEventTimerId), BeginEvent.Time); } } if (BeginEvent.Time > EndEventTime) { ++ErrorData.NumNegativeDurationEvents; ErrorData.NegativeDurationEventsMaxDelta = FMath::Max(ErrorData.NegativeDurationEventsMaxDelta, BeginEvent.Time - EndEventTime); if (++ErrorData.NumWarnings < ErrorData.NumMaxWarnings) { if (EndEventTimerId == InvalidTimerId) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Queue %u %s END : Time %f < %f (BEGIN %d \"%s\") !!!"), Queue.Id, StackIndex == 1 ? TEXT("WORK") : TEXT("BREADCRUMB"), EndEventTime, BeginEvent.Time, int32(BeginEvent.TimerId), GetTimerName(BeginEventTimerId)); } else { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Queue %u %s END %d \"%s\" : Time %f < %f (BEGIN %d \"%s\") !!!"), Queue.Id, StackIndex == 1 ? TEXT("WORK") : TEXT("BREADCRUMB"), int32(EndEventTimerId), GetTimerName(EndEventTimerId), EndEventTime, BeginEvent.Time, int32(BeginEvent.TimerId), GetTimerName(BeginEventTimerId)); } } } FAnalysisSessionEditScope _(Session); IEditableTimeline* Timeline = (StackIndex == 1) ? TimingProfilerProvider.GetGpuQueueWorkEditableTimeline(Queue.Id) : TimingProfilerProvider.GetGpuQueueEditableTimeline(Queue.Id); if (ensure(Timeline)) { const double LastTimestamp = Timeline->GetLastTimestamp(); if (EndEventTime < LastTimestamp) { ++ErrorData.NumInterleavedAndReversedEvents; ErrorData.InterleavedAndReversedEventsMaxDelta = FMath::Max(ErrorData.InterleavedAndReversedEventsMaxDelta, LastTimestamp - EndEventTime); if (++ErrorData.NumWarnings < ErrorData.NumMaxWarnings) { if (EndEventTimerId == InvalidTimerId) { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Queue %u %s END : Time %f < %f (last) !!!"), Queue.Id, StackIndex == 1 ? TEXT("WORK") : TEXT("BREADCRUMB"), EndEventTime, LastTimestamp); } else { UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] Queue %u %s END %d \"%s\" : Time %f < %f (last) !!!"), Queue.Id, StackIndex == 1 ? TEXT("WORK") : TEXT("BREADCRUMB"), int32(EndEventTimerId), GetTimerName(EndEventTimerId), EndEventTime, LastTimestamp); } } EndEventTime = LastTimestamp; } Timeline->AppendEndEvent(EndEventTime); Session.UpdateDurationSeconds(EndEventTime); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::OnEventBreadcrumbSpec(const FOnEventContext& Context) { const auto& EventData = Context.EventData; uint32 SpecId = EventData.GetValue("SpecId"); FString Name, NameFormat; EventData.GetString("StaticName", Name); EventData.GetString("NameFormat", NameFormat); TArrayView FieldNames = EventData.GetArrayView("FieldNames"); GPUPROFILER_LOG_API_L2(TEXT("[GpuProfiler] EventBreadcrumbSpec Id=%u Name=\"%s\" NameFormat=\"%s\" Fields[]={%u bytes}"), SpecId, *Name, *NameFormat, FieldNames.Num()); if (Name.Compare(NameFormat) == 0) { Name.Empty(); } int Index = 0; while (Index < NameFormat.Len() && Index < Name.Len()) { if (Name[Index] != NameFormat[Index]) { break; } ++Index; } if (Index > 1) { NameFormat.MidInline(Index); } if (Name.IsEmpty()) { const FString Separators = TEXT("% (="); Index = 0; bool bIsInFormatSpecifier = false; while (Index < NameFormat.Len()) { int32 SpecIndex = -1; if (Separators.FindChar(NameFormat[Index], SpecIndex)) { Name = NameFormat.Left(Index); NameFormat.MidInline(Index); break; } ++Index; } if (Name.IsEmpty()) { if (!NameFormat.IsEmpty()) { Name = NameFormat; NameFormat.Empty(); } else { Name = Session.StoreString(TEXT("Unknown")); } } } FMetadataSpec Spec; if (!NameFormat.IsEmpty()) { Spec.Format = Session.StoreString(NameFormat); } if (FieldNames.Num() > 0) { FMemoryReaderView MemoryReader(FieldNames); FCborReader CborReader(&MemoryReader, ECborEndianness::StandardCompliant); FCborContext CborContext; while (CborReader.ReadNext(CborContext)) { if (CborContext.MajorType() == ECborCode::TextString) { FString Field = CborContext.AsString(); Spec.FieldNames.Add(Session.StoreString(Field)); } } } uint32 TimerId; if (const uint32* FoundTimerIdBySpecId = SpecIdToTimerIdMap.Find(SpecId)) { TimerId = *FoundTimerIdBySpecId; SetTimerName(SpecId, TimerId, Name); } else { GPUPROFILER_LOG_API_L2(TEXT("[GpuProfiler] EventBreadcrumbSpec with a new timer (\"%s\")"), *Name); TimerId = AddGpuTimer(SpecId, Name); } if (Spec.FieldNames.Num() > 0 || Spec.Format != nullptr) { FAnalysisSessionEditScope _(Session); uint32 MetadataSpecId = TimingProfilerProvider.AddMetadataSpec(MoveTemp(Spec)); FTimingProfilerTimer PreviousTimer; TimingProfilerProvider.ReadTimers( [TimerId, &PreviousTimer] (const ITimingProfilerTimerReader& TimerReader) { const FTimingProfilerTimer* PreviousTimerPtr = TimerReader.GetTimer(TimerId); if (PreviousTimerPtr) { PreviousTimer = *PreviousTimerPtr; } }); if (PreviousTimer.HasValidMetadataSpecId()) { TimerId = AddGpuTimer(SpecId, Name); if (++ErrorData.NumTimerWarnings < ErrorData.NumMaxWarnings) { const FMetadataSpec* PreviousMetadataSpec = TimingProfilerProvider.GetMetadataSpec(PreviousTimer.MetadataSpecId); check(PreviousMetadataSpec); UE_LOG(LogTraceServices, Warning, TEXT("[GpuProfiler] EventBreadcrumbSpec -- de-duplicated timer for SpecId %u -- old: %u (%s) metadata=\"%s\" (%d fields) --> new: %u (%s) metadata=\"%s\" (%d fields) !!!"), SpecId, PreviousTimer.Id, PreviousTimer.Name, PreviousMetadataSpec->Format, PreviousMetadataSpec->FieldNames.Num(), TimerId, *Name, Spec.Format, Spec.FieldNames.Num()); } } TimingProfilerProvider.SetMetadataSpec(TimerId, MetadataSpecId); } } //////////////////////////////////////////////////////////////////////////////////////////////////// uint32 FGpuProfilerAnalyzer::GetOrAddTimer(uint32 SpecId, const FString& Breadcrumb) { if (SpecId != ~0u) { if (const uint32* FoundTimerIdBySpecId = SpecIdToTimerIdMap.Find(SpecId)) { return *FoundTimerIdBySpecId; } } // Merge by name for the GPU timers is disabled as it is not safe to merge when metdata can be attached late. //if (const uint32* FoundTimerId = BreadcrumbMap.Find(Breadcrumb)) //{ // return *FoundTimerId; //} return AddGpuTimer(SpecId, Breadcrumb); } //////////////////////////////////////////////////////////////////////////////////////////////////// uint32 FGpuProfilerAnalyzer::AddGpuTimer(uint32 SpecId, const FString& Breadcrumb) { uint32 TimerId; const TCHAR* TimerName = nullptr; { FAnalysisSessionEditScope _(Session); TimerId = TimingProfilerProvider.AddGpuTimer(Breadcrumb); TimingProfilerProvider.ReadTimers( [TimerId, &TimerName] (const ITimingProfilerTimerReader& TimerReader) { const FTimingProfilerTimer* Timer = TimerReader.GetTimer(TimerId); if (Timer) { TimerName = Timer->Name; } }); } TimerMap.Add(TimerId, TimerName); BreadcrumbMap.Add(Breadcrumb, TimerId); if (SpecId != ~0u) { SpecIdToTimerIdMap.Add(SpecId, TimerId); GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] --> AddGpuTimer(SpecId=%u, Name=\"%s\") --> TimerId=%u"), SpecId, TimerName, TimerId); } else { GPUPROFILER_LOG_API_L3(TEXT("[GpuProfiler] --> AddGpuTimer(Name=\"%s\") --> TimerId=%u"), TimerName, TimerId); } return TimerId; } //////////////////////////////////////////////////////////////////////////////////////////////////// const TCHAR* FGpuProfilerAnalyzer::GetTimerName(uint32 TimerId) const { if (TimerId == InvalidTimerId) { return TEXT(""); } const TCHAR* const* TimerNamePtr = TimerMap.Find(TimerId); return TimerNamePtr ? *TimerNamePtr : TEXT(""); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::SetTimerName(uint32 SpecId, uint32 TimerId, FStringView TimerName) { FAnalysisSessionEditScope _(Session); TimingProfilerProvider.SetTimerName(TimerId, TimerName); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::InitCounters(FQueue& FoundQueue) { FoundQueue.NumDrawsCounter = EditableCounterProvider.CreateEditableCounter(); FoundQueue.NumDrawsCounter->SetIsFloatingPoint(false); FoundQueue.NumPrimitivesCounter = EditableCounterProvider.CreateEditableCounter(); FoundQueue.NumPrimitivesCounter->SetIsFloatingPoint(false); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FGpuProfilerAnalyzer::InitCountersDesc(FQueue& FoundQueue, uint8 Gpu, uint8 Index, const TCHAR* Name) { const FString DrawsCounterName = FString::Printf(TEXT("NumDraws_GPU%u_%s_%u"), Gpu, Name, Index); const FString PrimitivesCounterName = FString::Printf(TEXT("NumPrimitives_GPU%u_%s_%u"), Gpu, Name, Index); FoundQueue.NumDrawsCounter->SetName(Session.StoreString(DrawsCounterName)); FoundQueue.NumDrawsCounter->SetDescription(TEXT("The number of draw calls on the specified queue.")); FoundQueue.NumPrimitivesCounter->SetName(Session.StoreString(PrimitivesCounterName)); FoundQueue.NumPrimitivesCounter->SetDescription(TEXT("The number of primitives on the specified queue.")); } //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace TraceServices #undef GPUPROFILER_LOG_API_L3 #undef GPUPROFILER_LOG_API_L2 #undef GPUPROFILER_LOG_API_L1