Files
2025-05-18 13:04:45 +08:00

154 lines
5.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "FrameStatsHelper.h"
// TraceServices
#include "TraceServices/Model/TimingProfiler.h"
// TraceInsights
#include "Insights/InsightsManager.h"
namespace UE::Insights::TimingProfiler
{
////////////////////////////////////////////////////////////////////////////////////////////////////
void FFrameStatsHelper::ComputeFrameStatsForTimer(TArray<FFrameStatsCachedEvent>& FrameStatsEvents, uint32 TimerId, const TSet<uint32>& Timelines)
{
TSharedPtr<const TraceServices::IAnalysisSession> Session = FInsightsManager::Get()->GetSession();
if (Session.IsValid())
{
for (uint32 TimelineIndex : Timelines)
{
ProcessTimeline(FrameStatsEvents, TimerId, TimelineIndex);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FFrameStatsHelper::ComputeFrameStatsForTimer(TArray<FFrameStatsCachedEvent>& FrameStatsEvents, uint32 TimerId)
{
TSharedPtr<const TraceServices::IAnalysisSession> Session = FInsightsManager::Get()->GetSession();
if (Session.IsValid())
{
Session->ReadAccessCheck();
const TraceServices::ITimingProfilerProvider& TimingProfilerProvider = *TraceServices::ReadTimingProfilerProvider(*Session.Get());
for (uint32 TimelineIndex = 0; TimelineIndex < TimingProfilerProvider.GetTimelineCount(); ++TimelineIndex)
{
ProcessTimeline(FrameStatsEvents, TimerId, TimelineIndex);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void FFrameStatsHelper::ProcessTimeline(TArray<FFrameStatsCachedEvent>& FrameStatsEvents, uint32 TimerId, uint32 TimelineIndex)
{
TSharedPtr<const TraceServices::IAnalysisSession> Session = FInsightsManager::Get()->GetSession();
if (Session.IsValid())
{
Session->ReadAccessCheck();
const double SessionDuration = Session->GetDurationSeconds();
const TraceServices::ITimingProfilerProvider& TimingProfilerProvider = *TraceServices::ReadTimingProfilerProvider(*Session.Get());
const TraceServices::ITimingProfilerTimerReader* TimerReader;
TimingProfilerProvider.ReadTimers([&TimerReader](const TraceServices::ITimingProfilerTimerReader& Out) { TimerReader = &Out; });
TimingProfilerProvider.ReadTimeline(TimelineIndex,
[SessionDuration, &FrameStatsEvents, TimerReader, TimerId](const TraceServices::ITimingProfilerProvider::Timeline& Timeline)
{
struct TaskData
{
double StartTime;
int32 NestedDepth = 0;
};
TArray<TaskData> DataArray;
TraceServices::ITimeline<TraceServices::FTimingProfilerEvent>::EnumerateAsyncParams Params;
Params.IntervalStart = 0;
Params.IntervalEnd = SessionDuration;
Params.Resolution = 0.0;
Params.SetupCallback = [&DataArray](uint32 NumTasks) { DataArray.AddDefaulted(NumTasks); };
Params.EventCallback = [TimerReader, &FrameStatsEvents, TimerId, &DataArray](bool bIsEnter, double Time, const TraceServices::FTimingProfilerEvent& Event, uint32 TaskIndex)
{
const TraceServices::FTimingProfilerTimer* Timer = TimerReader->GetTimer(Event.TimerIndex);
if (ensure(Timer != nullptr))
{
if (Timer->Id == TimerId)
{
TaskData& CurrentTaskData = DataArray[TaskIndex];
if (bIsEnter)
{
if (CurrentTaskData.NestedDepth == 0)
{
CurrentTaskData.StartTime = Time;
}
++CurrentTaskData.NestedDepth;
}
else
{
check(CurrentTaskData.NestedDepth > 0);
if (--CurrentTaskData.NestedDepth > 0)
{
return TraceServices::EEventEnumerate::Continue;
}
int32 Index = Algo::UpperBoundBy(FrameStatsEvents, CurrentTaskData.StartTime, &FFrameStatsCachedEvent::FrameStartTime);
if (Index > 0)
{
--Index;
}
// This can can happen when the event is between frames.
if (CurrentTaskData.StartTime > FrameStatsEvents[Index].FrameEndTime)
{
Index++;
if (Index >= FrameStatsEvents.Num())
{
return TraceServices::EEventEnumerate::Continue;
}
}
double EndTime = Time;
do
{
FFrameStatsCachedEvent& Entry = FrameStatsEvents[Index];
if (EndTime < Entry.FrameStartTime)
{
return TraceServices::EEventEnumerate::Continue;
}
if (CurrentTaskData.StartTime < Entry.FrameStartTime)
{
CurrentTaskData.StartTime = Entry.FrameStartTime;
}
const double Duration = FMath::Min(EndTime, Entry.FrameEndTime) - CurrentTaskData.StartTime;
ensure(Duration >= 0.0f);
for (double Value = Entry.Duration.load(); !Entry.Duration.compare_exchange_strong(Value, Value + Duration););
Index++;
} while (Index < FrameStatsEvents.Num());
}
}
}
return TraceServices::EEventEnumerate::Continue;
};
Timeline.EnumerateEventsDownSampledAsync(Params);
});
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace UE::Insights::TimingProfiler