// Copyright Epic Games, Inc. All Rights Reserved. #include "LoadingTimingTrack.h" // TraceServices #include "TraceServices/Containers/Timelines.h" #include "TraceServices/Model/AnalysisSession.h" #include "TraceServices/Model/LoadTimeProfiler.h" // TraceInsightsCore #include "InsightsCore/Common/TimeUtils.h" #include "InsightsCore/Filter/ViewModels/FilterConfigurator.h" #include "InsightsCore/Filter/ViewModels/Filters.h" // TraceInsights #include "Insights/InsightsManager.h" #include "Insights/LoadingProfiler/ViewModels/LoadingSharedState.h" #include "Insights/ViewModels/TimingEvent.h" #include "Insights/ViewModels/TimingEventSearch.h" #include "Insights/ViewModels/TimingTrackViewport.h" #include "Insights/ViewModels/TooltipDrawState.h" #define LOCTEXT_NAMESPACE "UE::Insights::LoadingProfiler" namespace UE::Insights::LoadingProfiler { //////////////////////////////////////////////////////////////////////////////////////////////////// INSIGHTS_IMPLEMENT_RTTI(FLoadingTimingTrack) //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingTimingTrack::BuildDrawState(ITimingEventsTrackDrawStateBuilder& Builder, const ITimingTrackUpdateContext& Context) { TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (Session.IsValid() && TraceServices::ReadLoadTimeProfilerProvider(*Session.Get())) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::ILoadTimeProfilerProvider& LoadTimeProfilerProvider = *TraceServices::ReadLoadTimeProfilerProvider(*Session.Get()); const FTimingTrackViewport& Viewport = Context.GetViewport(); LoadTimeProfilerProvider.ReadTimeline(TimelineIndex, [this, &Builder, &Viewport](const TraceServices::ILoadTimeProfilerProvider::CpuTimeline& Timeline) { if (FTimingEventsTrack::bUseDownSampling) { const double SecondsPerPixel = 1.0 / Viewport.GetScaleX(); Timeline.EnumerateEventsDownSampled(Viewport.GetStartTime(), Viewport.GetEndTime(), SecondsPerPixel, [this, &Builder](double StartTime, double EndTime, uint32 Depth, const TraceServices::FLoadTimeProfilerCpuEvent& Event) { if (Event.Package) { const TCHAR* Name = SharedState.GetEventName(Depth, Event); const uint64 Type = static_cast(Event.EventType); const uint32 Color = 0; Builder.AddEvent(StartTime, EndTime, Depth, Name, Type, Color); } return TraceServices::EEventEnumerate::Continue; }); } else { Timeline.EnumerateEvents(Viewport.GetStartTime(), Viewport.GetEndTime(), [this, &Builder](double StartTime, double EndTime, uint32 Depth, const TraceServices::FLoadTimeProfilerCpuEvent& Event) { if (Event.Package) { const TCHAR* Name = SharedState.GetEventName(Depth, Event); const uint64 Type = static_cast(Event.EventType); const uint32 Color = 0; Builder.AddEvent(StartTime, EndTime, Depth, Name, Type, Color); } return TraceServices::EEventEnumerate::Continue; }); } }); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingTimingTrack::BuildFilteredDrawState(ITimingEventsTrackDrawStateBuilder& Builder, const ITimingTrackUpdateContext& Context) { if (HasCustomFilter()) { using EFilterField = UE::Insights::EFilterField; UE::Insights::FFilterContext FilterConfiguratorContext; FilterConfiguratorContext.SetReturnValueForUnsetFilters(false); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::StartTime), 0.0f); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::EndTime), 0.0f); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::Duration), 0.0f); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::TrackName), this->GetName()); TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (Session.IsValid() && TraceServices::ReadLoadTimeProfilerProvider(*Session.Get())) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::ILoadTimeProfilerProvider& LoadTimeProfilerProvider = *TraceServices::ReadLoadTimeProfilerProvider(*Session.Get()); const FTimingTrackViewport& Viewport = Context.GetViewport(); LoadTimeProfilerProvider.ReadTimeline(TimelineIndex, [this, &Builder, &Viewport, &FilterConfiguratorContext](const TraceServices::ILoadTimeProfilerProvider::CpuTimeline& Timeline) { Timeline.EnumerateEvents(Viewport.GetStartTime(), Viewport.GetEndTime(), [this, &Builder, &FilterConfiguratorContext](double StartTime, double EndTime, uint32 Depth, const TraceServices::FLoadTimeProfilerCpuEvent& Event) { if (Event.Package) { FilterConfiguratorContext.SetFilterData(static_cast(EFilterField::StartTime), StartTime); FilterConfiguratorContext.SetFilterData(static_cast(EFilterField::EndTime), EndTime); FilterConfiguratorContext.SetFilterData(static_cast(EFilterField::Duration), EndTime - StartTime); if (FilterConfigurator->ApplyFilters(FilterConfiguratorContext)) { const TCHAR* Name = SharedState.GetEventName(Depth, Event); const uint64 Type = static_cast(Event.EventType); const uint32 Color = 0; Builder.AddEvent(StartTime, EndTime, Depth, Name, Type, Color); } } return TraceServices::EEventEnumerate::Continue; }); }); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingTimingTrack::InitTooltip(FTooltipDrawState& InOutTooltip, const ITimingEvent& InTooltipEvent) const { if (InTooltipEvent.CheckTrack(this) && InTooltipEvent.Is()) { const FTimingEvent& TooltipEvent = InTooltipEvent.As(); auto MatchEvent = [&TooltipEvent](double InStartTime, double InEndTime, uint32 InDepth) { return InDepth == TooltipEvent.GetDepth() && InStartTime == TooltipEvent.GetStartTime() && InEndTime == TooltipEvent.GetEndTime(); }; FTimingEventSearchParameters SearchParameters(TooltipEvent.GetStartTime(), TooltipEvent.GetEndTime(), ETimingEventSearchFlags::StopAtFirstMatch, MatchEvent); FindLoadTimeProfilerCpuEvent(SearchParameters, [this, &InOutTooltip, &TooltipEvent](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const TraceServices::FLoadTimeProfilerCpuEvent& InFoundEvent) { InOutTooltip.ResetContent(); InOutTooltip.AddTitle(SharedState.GetEventName(TooltipEvent.GetDepth(), InFoundEvent)); const TraceServices::FPackageExportInfo* Export = InFoundEvent.Export; const TraceServices::FPackageInfo* Package = InFoundEvent.Export ? InFoundEvent.Export->Package : InFoundEvent.Package; InOutTooltip.AddNameValueTextLine(TEXT("Duration:"), UE::Insights::FormatTimeAuto(TooltipEvent.GetDuration())); InOutTooltip.AddNameValueTextLine(TEXT("Depth:"), FString::Printf(TEXT("%d"), TooltipEvent.GetDepth())); if (Package) { InOutTooltip.AddNameValueTextLine(TEXT("Package Name:"), Package->Name); InOutTooltip.AddNameValueTextLine(TEXT("Header Size:"), FString::Printf(TEXT("%s bytes"), *FText::AsNumber(Package->Summary.TotalHeaderSize).ToString())); InOutTooltip.AddNameValueTextLine(TEXT("Package Summary:"), FString::Printf(TEXT("%d imports, %d exports"), Package->Summary.ImportCount, Package->Summary.ExportCount)); InOutTooltip.AddNameValueTextLine(TEXT("Request Priority:"), FString::Printf(TEXT("%d"), Package->Summary.Priority)); if (!Export) { InOutTooltip.AddNameValueTextLine(TEXT("Event:"), TEXT("ProcessPackageSummary")); } } if (Export) { InOutTooltip.AddNameValueTextLine(TEXT("Event:"), FString::Printf(TEXT("%s"), TraceServices::GetLoadTimeProfilerObjectEventTypeString(InFoundEvent.EventType))); InOutTooltip.AddNameValueTextLine(TEXT("Export Class:"), Export->Class ? Export->Class->Name : TEXT("N/A")); InOutTooltip.AddNameValueTextLine(TEXT("Serial Size:"), FString::Printf(TEXT("%s bytes"), *FText::AsNumber(Export->SerialSize).ToString())); } InOutTooltip.UpdateLayout(); }); } } //////////////////////////////////////////////////////////////////////////////////////////////////// const TSharedPtr FLoadingTimingTrack::SearchEvent(const FTimingEventSearchParameters& InSearchParameters) const { TSharedPtr FoundEvent; FindLoadTimeProfilerCpuEvent(InSearchParameters, [this, &FoundEvent](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const TraceServices::FLoadTimeProfilerCpuEvent& InFoundEvent) { FoundEvent = MakeShared(SharedThis(this), InFoundStartTime, InFoundEndTime, InFoundDepth); }); return FoundEvent; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FLoadingTimingTrack::FindLoadTimeProfilerCpuEvent(const FTimingEventSearchParameters& InParameters, TFunctionRef InFoundPredicate) const { using EFilterField = UE::Insights::EFilterField; UE::Insights::FFilterContext FilterConfiguratorContext; FilterConfiguratorContext.SetReturnValueForUnsetFilters(false); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::StartTime), 0.0f); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::EndTime), 0.0f); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::Duration), 0.0f); FilterConfiguratorContext.AddFilterData(static_cast(EFilterField::TrackName), this->GetName()); return TTimingEventSearch::Search( InParameters, [this](TTimingEventSearch::FContext& InContext) { TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); if (TraceServices::ReadLoadTimeProfilerProvider(*Session.Get())) { const TraceServices::ILoadTimeProfilerProvider& LoadTimeProfilerProvider = *TraceServices::ReadLoadTimeProfilerProvider(*Session.Get()); LoadTimeProfilerProvider.ReadTimeline(TimelineIndex, [&InContext](const TraceServices::ILoadTimeProfilerProvider::CpuTimeline& Timeline) { auto Callback = [&InContext](double EventStartTime, double EventEndTime, uint32 EventDepth, const TraceServices::FLoadTimeProfilerCpuEvent& Event) { if (Event.Package) { InContext.Check(EventStartTime, EventEndTime, EventDepth, Event); return InContext.ShouldContinueSearching() ? TraceServices::EEventEnumerate::Continue : TraceServices::EEventEnumerate::Stop; } else { return TraceServices::EEventEnumerate::Continue; } }; if (InContext.GetParameters().SearchDirection == FTimingEventSearchParameters::ESearchDirection::Forward) { Timeline.EnumerateEvents(InContext.GetParameters().StartTime, InContext.GetParameters().EndTime, Callback); } else { Timeline.EnumerateEventsBackwards(InContext.GetParameters().EndTime, InContext.GetParameters().StartTime, Callback); } }); } } }, [&FilterConfiguratorContext, &InParameters](double EventStartTime, double EventEndTime, uint32 EventDepth, const TraceServices::FLoadTimeProfilerCpuEvent& Event) { if (!InParameters.FilterExecutor.IsValid()) { return true; } TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); if (TraceServices::ReadLoadTimeProfilerProvider(*Session.Get())) { FilterConfiguratorContext.SetFilterData(static_cast(EFilterField::StartTime), EventStartTime); FilterConfiguratorContext.SetFilterData(static_cast(EFilterField::EndTime), EventEndTime); FilterConfiguratorContext.SetFilterData(static_cast(EFilterField::Duration), EventEndTime - EventStartTime); return InParameters.FilterExecutor->ApplyFilters(FilterConfiguratorContext); } } return false; }, [&InFoundPredicate](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const TraceServices::FLoadTimeProfilerCpuEvent& InEvent) { InFoundPredicate(InFoundStartTime, InFoundEndTime, InFoundDepth, InEvent); }, TTimingEventSearch::NoMatch); } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FLoadingTimingTrack::HasCustomFilter() const { return FilterConfigurator.IsValid() && !FilterConfigurator->IsEmpty(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingTimingTrack::SetFilterConfigurator(TSharedPtr InFilterConfigurator) { if (FilterConfigurator != InFilterConfigurator) { FilterConfigurator = InFilterConfigurator; SetDirtyFlag(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace UE::Insights::LoadingProfiler #undef LOCTEXT_NAMESPACE