// Copyright Epic Games, Inc. All Rights Reserved. #include "MassInsightsTimingTrack.h" #include "Framework/MultiBox/MultiBoxBuilder.h" // TraceServices #include "Common/ProviderLock.h" // TraceInsights #include "MassInsightsUIModule.h" #include "Framework/Commands/Commands.h" #include "InsightsCore/Common/TimeUtils.h" #include "Insights/ITimingViewSession.h" #include "Insights/IUnrealInsightsModule.h" #include "Insights/ViewModels/TimingEventSearch.h" #include "Insights/ViewModels/TimingTrackViewport.h" #include "Insights/ViewModels/TooltipDrawState.h" #include "Insights/ViewModels/TimingEvent.h" #include "MassInsightsUI/Widgets/SMassInsightsAnalysisTab.h" #include "Modules/ModuleManager.h" #define LOCTEXT_NAMESPACE "RegionsTimingTrack" class IUnrealInsightsModule; class FTimingEvent; namespace MassInsightsUI { //////////////////////////////////////////////////////////////////////////////////////////////////// // FFileActivityTimingViewCommands //////////////////////////////////////////////////////////////////////////////////////////////////// FMassInsightsViewCommands::FMassInsightsViewCommands() : ::TCommands( TEXT("FMassInsightsViewCommands"), NSLOCTEXT("Contexts", "FMassInsightsViewCommands", "Insights - Timing View - Mass Processor"), NAME_None, TEXT("InsightsStyle")) { } //////////////////////////////////////////////////////////////////////////////////////////////////// FMassInsightsViewCommands::~FMassInsightsViewCommands() { } //////////////////////////////////////////////////////////////////////////////////////////////////// UE_DISABLE_OPTIMIZATION_SHIP void FMassInsightsViewCommands::RegisterCommands() { UI_COMMAND(ShowHideRegionTrack, "Mass Processor Phase Tracks", "Shows/hides the Tracks demarcating the begin and end of MassProcessor phases.", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::Y)); } UE_ENABLE_OPTIMIZATION_SHIP //////////////////////////////////////////////////////////////////////////////////////////////////// void FMassInsightsSharedState::OnBeginSession(UE::Insights::Timing::ITimingViewSession& InSession) { BindCommands(); MassInsightsTrack.Reset(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FMassInsightsSharedState::OnEndSession(UE::Insights::Timing::ITimingViewSession& InSession) { MassInsightsTrack.Reset(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FMassInsightsSharedState::Tick(UE::Insights::Timing::ITimingViewSession& InSession, const TraceServices::IAnalysisSession& InAnalysisSession) { if (!MassInsightsTrack.IsValid()) { MassInsightsTrack = MakeShared(*this); MassInsightsTrack->SetOrder(FTimingTrackOrder::First); MassInsightsTrack->SetVisibilityFlag(true); InSession.AddScrollableTrack(MassInsightsTrack); } if (TSharedPtr Analysis = FMassInsightsUIModule::Get().GetAnalysisTab()) { Analysis->SetSession(&InSession, &InAnalysisSession); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FMassInsightsSharedState::ShowHideRegionsTrack() { bShowHideRegionsTrack = !bShowHideRegionsTrack; if (MassInsightsTrack.IsValid()) { MassInsightsTrack->SetVisibilityFlag(bShowHideRegionsTrack); } if (bShowHideRegionsTrack) { MassInsightsTrack->SetDirtyFlag(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FMassInsightsSharedState::ExtendOtherTracksFilterMenu(UE::Insights::Timing::ITimingViewSession& InSession, FMenuBuilder& InOutMenuBuilder) { InOutMenuBuilder.BeginSection("Mass Processor Regions", LOCTEXT("ContextMenu_Section_Regions", "Mass Processor Regions")); InOutMenuBuilder.AddMenuEntry(FMassInsightsViewCommands::Get().ShowHideRegionTrack); InOutMenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FMassInsightsSharedState::BindCommands() { FMassInsightsViewCommands::Register(); } ////////////////////////////////////////////////////////////////////////// INSIGHTS_IMPLEMENT_RTTI(FMassInsightsTrack) FMassInsightsTrack::FMassInsightsTrack(FMassInsightsSharedState& InSharedState) : FTimingEventsTrack(TEXT("Mass Phases")), SharedState(InSharedState) { IUnrealInsightsModule& UnrealInsightsModule = FModuleManager::LoadModuleChecked("TraceInsights"); AnalysisSession = UnrealInsightsModule.GetAnalysisSession(); } FMassInsightsTrack::~FMassInsightsTrack() { } void FMassInsightsTrack::BuildContextMenu(FMenuBuilder& MenuBuilder) { FTimingEventsTrack::BuildContextMenu(MenuBuilder); } ////////////////////////////////////////////////////////////////////////// void FMassInsightsTrack::InitTooltip(FTooltipDrawState& InOutTooltip, const ITimingEvent& InTooltipEvent) const { if (InTooltipEvent.CheckTrack(this) && InTooltipEvent.Is()) { const FTimingEvent& TooltipEvent = InTooltipEvent.As(); auto MatchEvent = [this, &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); FindRegionEvent(SearchParameters, [this, &InOutTooltip, &TooltipEvent] ( double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const MassInsightsAnalysis::FMassInsights& InRegion) { InOutTooltip.Reset(); InOutTooltip.AddTitle(InRegion.Text, FLinearColor::White); InOutTooltip.AddNameValueTextLine(TEXT("Duration:"), UE::Insights::FormatTimeAuto(InRegion.EndTime-InRegion.BeginTime)); InOutTooltip.AddNameValueTextLine(TEXT("Depth:"), FString::FromInt(InRegion.Depth)); InOutTooltip.UpdateLayout(); }); } } void FMassInsightsTrack::BuildDrawState(ITimingEventsTrackDrawStateBuilder& Builder, const ITimingTrackUpdateContext& Context) { const FTimingTrackViewport& Viewport = Context.GetViewport(); const MassInsightsAnalysis::IMassInsightsProvider& RegionProvider = MassInsightsAnalysis::ReadMassInsightsProvider(*AnalysisSession); TraceServices::FProviderReadScopeLock RegionProviderScopedLock(RegionProvider); // whe're counting only non-empty lanes, so we can collapse empty ones in the visualization. int32 CurDepth = 0; RegionProvider.EnumerateLanes([this, Viewport, &CurDepth, &Builder](const MassInsightsAnalysis::FMassInsightsLane& Lane, const int32 Depth) { bool RegionHadEvents = false; Lane.EnumerateRegions(Viewport.GetStartTime(), Viewport.GetEndTime(), [&Builder, &RegionHadEvents, &CurDepth](const MassInsightsAnalysis::FMassInsights& Region) -> bool { RegionHadEvents = true; Builder.AddEvent(Region.BeginTime, Region.EndTime,CurDepth, Region.Text); return true; }); if (RegionHadEvents) CurDepth++; }); } ////////////////////////////////////////////////////////////////////////// void FMassInsightsTrack::BuildFilteredDrawState(ITimingEventsTrackDrawStateBuilder& Builder, const ITimingTrackUpdateContext& Context) { const TSharedPtr EventFilterPtr = Context.GetEventFilter(); if (EventFilterPtr.IsValid() && EventFilterPtr->FilterTrack(*this)) { bool bFilterOnlyByEventType = false; // this is the most often use case, so the below code tries to optimize it TCHAR* FilterEventType = 0; if (EventFilterPtr->Is()) { bFilterOnlyByEventType = true; const FTimingEventFilterByEventType& EventFilter = EventFilterPtr->As(); FilterEventType = reinterpret_cast(EventFilter.GetEventType()); } if (AnalysisSession.IsValid()) { const MassInsightsAnalysis::IMassInsightsProvider& RegionProvider = MassInsightsAnalysis::ReadMassInsightsProvider(*AnalysisSession); TraceServices::FProviderReadScopeLock RegionProviderScopedLock(RegionProvider); const FTimingTrackViewport& Viewport = Context.GetViewport(); if (bFilterOnlyByEventType) { int32 CurDepth = 0; RegionProvider.EnumerateLanes([this, Viewport, &CurDepth, &Builder, FilterEventType](const MassInsightsAnalysis::FMassInsightsLane& Lane, const int32 Depth) { bool RegionHadEvents = false; Lane.EnumerateRegions(Viewport.GetStartTime(), Viewport.GetEndTime(), [&Builder, &RegionHadEvents, &CurDepth, FilterEventType](const MassInsightsAnalysis::FMassInsights& Region) -> bool { RegionHadEvents = true; if (Region.Text == FilterEventType) { Builder.AddEvent(Region.BeginTime, Region.EndTime, CurDepth, Region.Text); } return true; }); if (RegionHadEvents) CurDepth++; }); } else // generic filter { //TODO: if (EventFilterPtr->FilterEvent(TimingEvent)) } } } } ////////////////////////////////////////////////////////////////////////// const TSharedPtr FMassInsightsTrack::SearchEvent( const FTimingEventSearchParameters& InSearchParameters) const { TSharedPtr FoundEvent; FindRegionEvent(InSearchParameters, [this, &FoundEvent](double InFoundStartTime, double InFoundEndTime, uint32 InFoundDepth, const MassInsightsAnalysis::FMassInsights& InEvent) { FoundEvent = MakeShared(SharedThis(this), InFoundStartTime, InFoundEndTime, InFoundDepth, reinterpret_cast(InEvent.Text)); }); return FoundEvent; } ////////////////////////////////////////////////////////////////////////// bool FMassInsightsTrack::FindRegionEvent(const FTimingEventSearchParameters& InParameters, TFunctionRef InFoundPredicate) const { // If the query start time is larger than the end of the session return false. { TraceServices::FAnalysisSessionReadScope SessionReadScope(*AnalysisSession.Get()); if (AnalysisSession.IsValid() && InParameters.StartTime > AnalysisSession->GetDurationSeconds()) { return false; } } return TTimingEventSearch::Search( InParameters, [this](TTimingEventSearch::FContext& InContext) { const MassInsightsAnalysis::IMassInsightsProvider& RegionProvider = MassInsightsAnalysis::ReadMassInsightsProvider(*AnalysisSession); TraceServices::FProviderReadScopeLock RegionProviderScopedLock(RegionProvider); RegionProvider.EnumerateRegions(InContext.GetParameters().StartTime, InContext.GetParameters().EndTime, [&InContext](const MassInsightsAnalysis::FMassInsights& Region) { InContext.Check(Region.BeginTime, Region.EndTime, Region.Depth, Region); if (!InContext.ShouldContinueSearching()) { return false; } return true; }); }, TTimingEventSearch::NoMatch); } ////////////////////////////////////////////////////////////////////////// void FMassInsightsTrack::SetFilterConfigurator(TSharedPtr InFilterConfigurator) { if (FilterConfigurator != InFilterConfigurator) { FilterConfigurator = InFilterConfigurator; SetDirtyFlag(); } } ////////////////////////////////////////////////////////////////////////// } // namespace Insights #undef LOCTEXT_NAMESPACE