// Copyright Epic Games, Inc. All Rights Reserved. #include "LoadingProfilerManager.h" #include "MessageLogModule.h" #include "Modules/ModuleManager.h" #include "Widgets/Docking/SDockTab.h" #include "WorkspaceMenuStructure.h" #include "WorkspaceMenuStructureModule.h" // TraceServices #include "TraceServices/Model/LoadTimeProfiler.h" // TraceInsightsCore #include "InsightsCore/Table/Widgets/STableTreeView.h" // TraceInsights #include "Insights/Common/InsightsMenuBuilder.h" #include "Insights/InsightsManager.h" #include "Insights/InsightsStyle.h" #include "Insights/LoadingProfiler/Widgets/SLoadingProfilerWindow.h" #include "Insights/Widgets/STimingView.h" #define LOCTEXT_NAMESPACE "UE::Insights::LoadingProfiler" namespace UE::Insights::LoadingProfiler { //////////////////////////////////////////////////////////////////////////////////////////////////// DEFINE_LOG_CATEGORY(LoadingProfiler); TSharedPtr FLoadingProfilerManager::Instance = nullptr; //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FLoadingProfilerManager::Get() { return FLoadingProfilerManager::Instance; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FLoadingProfilerManager::CreateInstance() { ensure(!FLoadingProfilerManager::Instance.IsValid()); if (FLoadingProfilerManager::Instance.IsValid()) { FLoadingProfilerManager::Instance.Reset(); } FLoadingProfilerManager::Instance = MakeShared(FInsightsManager::Get()->GetCommandList()); return FLoadingProfilerManager::Instance; } //////////////////////////////////////////////////////////////////////////////////////////////////// FLoadingProfilerManager::FLoadingProfilerManager(TSharedRef InCommandList) : bIsInitialized(false) , bIsAvailable(false) , CommandList(InCommandList) , ActionManager(this) , ProfilerWindowWeakPtr() , bIsTimingViewVisible(false) , bIsEventAggregationTreeViewVisible(false) , bIsObjectTypeAggregationTreeViewVisible(false) , bIsPackageDetailsTreeViewVisible(false) , bIsExportDetailsTreeViewVisible(false) , bIsRequestsTreeViewVisible(false) , LogListingName(TEXT("LoadingInsights")) { } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::Initialize(IUnrealInsightsModule& InsightsModule) { ensure(!bIsInitialized); if (bIsInitialized) { return; } bIsInitialized = true; UE_LOG(LoadingProfiler, Log, TEXT("Initialize")); // Register tick functions. OnTick = FTickerDelegate::CreateSP(this, &FLoadingProfilerManager::Tick); OnTickHandle = FTSTicker::GetCoreTicker().AddTicker(OnTick, 0.0f); FLoadingProfilerCommands::Register(); BindCommands(); FInsightsManager::Get()->GetSessionChangedEvent().AddSP(this, &FLoadingProfilerManager::OnSessionChanged); OnSessionChanged(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::Shutdown() { if (!bIsInitialized) { return; } bIsInitialized = false; // If the MessageLog module was already unloaded as part of the global Shutdown process, do not load it again. if (FModuleManager::Get().IsModuleLoaded("MessageLog")) { FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); if (MessageLogModule.IsRegisteredLogListing(GetLogListingName())) { MessageLogModule.UnregisterLogListing(GetLogListingName()); } } FInsightsManager::Get()->GetSessionChangedEvent().RemoveAll(this); FLoadingProfilerCommands::Unregister(); // Unregister tick function. FTSTicker::GetCoreTicker().RemoveTicker(OnTickHandle); FLoadingProfilerManager::Instance.Reset(); UE_LOG(LoadingProfiler, Log, TEXT("Shutdown")); } //////////////////////////////////////////////////////////////////////////////////////////////////// FLoadingProfilerManager::~FLoadingProfilerManager() { ensure(!bIsInitialized); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::BindCommands() { ActionManager.Map_ToggleTimingViewVisibility_Global(); ActionManager.Map_ToggleEventAggregationTreeViewVisibility_Global(); ActionManager.Map_ToggleObjectTypeAggregationTreeViewVisibility_Global(); ActionManager.Map_TogglePackageDetailsTreeViewVisibility_Global(); ActionManager.Map_ToggleExportDetailsTreeViewVisibility_Global(); ActionManager.Map_ToggleRequestsTreeViewVisibility_Global(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::RegisterMajorTabs(IUnrealInsightsModule& InsightsModule) { const FInsightsMajorTabConfig& Config = InsightsModule.FindMajorTabConfig(FInsightsManagerTabs::LoadingProfilerTabId); if (Config.bIsAvailable) { // Register tab spawner for the Asset Loading Insights. FTabSpawnerEntry& TabSpawnerEntry = FGlobalTabmanager::Get()->RegisterNomadTabSpawner(FInsightsManagerTabs::LoadingProfilerTabId, FOnSpawnTab::CreateRaw(this, &FLoadingProfilerManager::SpawnTab), FCanSpawnTab::CreateRaw(this, &FLoadingProfilerManager::CanSpawnTab)) .SetDisplayName(Config.TabLabel.IsSet() ? Config.TabLabel.GetValue() : LOCTEXT("LoadingProfilerTabTitle", "Asset Loading Insights")) .SetTooltipText(Config.TabTooltip.IsSet() ? Config.TabTooltip.GetValue() : LOCTEXT("LoadingProfilerTooltipText", "Open the Asset Loading Insights tab.")) .SetIcon(Config.TabIcon.IsSet() ? Config.TabIcon.GetValue() : FSlateIcon(FInsightsStyle::GetStyleSetName(), "Icons.LoadingProfiler")); TSharedRef Group = Config.WorkspaceGroup.IsValid() ? Config.WorkspaceGroup.ToSharedRef() : FInsightsManager::Get()->GetInsightsMenuBuilder()->GetInsightsToolsGroup(); TabSpawnerEntry.SetGroup(Group); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::UnregisterMajorTabs() { FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(FInsightsManagerTabs::LoadingProfilerTabId); } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedRef FLoadingProfilerManager::SpawnTab(const FSpawnTabArgs& Args) { const TSharedRef DockTab = SNew(SDockTab) .TabRole(ETabRole::NomadTab); // Register OnTabClosed to handle I/O profiler manager shutdown. DockTab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateRaw(this, &FLoadingProfilerManager::OnTabClosed)); // Create the SLoadingProfilerWindow widget. TSharedRef Window = SNew(SLoadingProfilerWindow, DockTab, Args.GetOwnerWindow()); DockTab->SetContent(Window); AssignProfilerWindow(Window); return DockTab; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FLoadingProfilerManager::CanSpawnTab(const FSpawnTabArgs& Args) const { return bIsAvailable; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::OnTabClosed(TSharedRef TabBeingClosed) { RemoveProfilerWindow(); // Disable TabClosed delegate. TabBeingClosed->SetOnTabClosed(SDockTab::FOnTabClosedCallback()); } //////////////////////////////////////////////////////////////////////////////////////////////////// const TSharedRef FLoadingProfilerManager::GetCommandList() const { return CommandList; } //////////////////////////////////////////////////////////////////////////////////////////////////// const FLoadingProfilerCommands& FLoadingProfilerManager::GetCommands() { return FLoadingProfilerCommands::Get(); } //////////////////////////////////////////////////////////////////////////////////////////////////// FLoadingProfilerActionManager& FLoadingProfilerManager::GetActionManager() { return FLoadingProfilerManager::Instance->ActionManager; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool FLoadingProfilerManager::Tick(float DeltaTime) { // Check if session has Load Time events (to spawn the tab), but not too often. if (!bIsAvailable && AvailabilityCheck.Tick()) { bool bIsProviderAvailable = false; TSharedPtr Session = FInsightsManager::Get()->GetSession(); if (Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); if (Session->IsAnalysisComplete()) { // Never check again during this session. AvailabilityCheck.Disable(); } const TraceServices::ILoadTimeProfilerProvider* LoadTimeProfilerProvider = TraceServices::ReadLoadTimeProfilerProvider(*Session.Get()); if (LoadTimeProfilerProvider) { bIsProviderAvailable = (LoadTimeProfilerProvider->GetTimelineCount() > 0); } } else { // Do not check again until the next session changed event (see OnSessionChanged). AvailabilityCheck.Disable(); } if (bIsProviderAvailable) { bIsAvailable = true; FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked("MessageLog"); MessageLogModule.RegisterLogListing(GetLogListingName(), LOCTEXT("LoadingInsights", "Loading Insights")); MessageLogModule.EnableMessageLogDisplay(true); const FName& TabId = FInsightsManagerTabs::LoadingProfilerTabId; if (FGlobalTabmanager::Get()->HasTabSpawner(TabId)) { UE_LOG(LoadingProfiler, Log, TEXT("Opening the \"Asset Loading Insights\" tab...")); FGlobalTabmanager::Get()->TryInvokeTab(TabId); } } } return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::OnSessionChanged() { UE_LOG(LoadingProfiler, Log, TEXT("OnSessionChanged")); bIsAvailable = false; if (FInsightsManager::Get()->GetSession().IsValid()) { AvailabilityCheck.Enable(0.3); } else { AvailabilityCheck.Disable(); } TSharedPtr Wnd = GetProfilerWindow(); if (Wnd.IsValid()) { Wnd->Reset(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::ShowHideTimingView(const bool bIsVisible) { bIsTimingViewVisible = bIsVisible; TSharedPtr Wnd = GetProfilerWindow(); if (Wnd.IsValid()) { Wnd->ShowHideTab(FLoadingProfilerTabs::TimingViewID, bIsVisible); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::ShowHideEventAggregationTreeView(const bool bIsVisible) { bIsEventAggregationTreeViewVisible = bIsVisible; TSharedPtr Wnd = GetProfilerWindow(); if (Wnd.IsValid()) { Wnd->ShowHideTab(FLoadingProfilerTabs::EventAggregationTreeViewID, bIsVisible); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::ShowHideObjectTypeAggregationTreeView(const bool bIsVisible) { bIsObjectTypeAggregationTreeViewVisible = bIsVisible; TSharedPtr Wnd = GetProfilerWindow(); if (Wnd.IsValid()) { Wnd->ShowHideTab(FLoadingProfilerTabs::ObjectTypeAggregationTreeViewID, bIsVisible); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::ShowHidePackageDetailsTreeView(const bool bIsVisible) { bIsPackageDetailsTreeViewVisible = bIsVisible; TSharedPtr Wnd = GetProfilerWindow(); if (Wnd.IsValid()) { Wnd->ShowHideTab(FLoadingProfilerTabs::PackageDetailsTreeViewID, bIsVisible); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::ShowHideExportDetailsTreeView(const bool bIsVisible) { bIsExportDetailsTreeViewVisible = bIsVisible; TSharedPtr Wnd = GetProfilerWindow(); if (Wnd.IsValid()) { Wnd->ShowHideTab(FLoadingProfilerTabs::ExportDetailsTreeViewID, bIsVisible); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::ShowHideRequestsTreeView(const bool bIsVisible) { bIsRequestsTreeViewVisible = bIsVisible; TSharedPtr Wnd = GetProfilerWindow(); if (Wnd.IsValid()) { Wnd->ShowHideTab(FLoadingProfilerTabs::RequestsTreeViewID, bIsVisible); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLoadingProfilerManager::OnWindowClosedEvent() { TSharedPtr Wnd = GetProfilerWindow(); if (Wnd) { TSharedPtr TimingView = Wnd->GetTimingView(); if (TimingView.IsValid()) { TimingView->CloseQuickFindTab(); } } } //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace UE::Insights::LoadingProfiler #undef LOCTEXT_NAMESPACE