// Copyright Epic Games, Inc. All Rights Reserved. #include "STaskTableTreeView.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ISourceCodeAccessModule.h" #include "ISourceCodeAccessor.h" #include "Misc/Paths.h" #include "Modules/ModuleManager.h" #include "SlateOptMacros.h" #include "Widgets/Input/SComboBox.h" // TraceServices #include "TraceServices/Model/TasksProfiler.h" // TraceInsights #include "Insights/InsightsStyle.h" #include "Insights/TaskGraphProfiler/TaskGraphProfilerManager.h" #include "Insights/TaskGraphProfiler/ViewModels/TaskEntry.h" #include "Insights/TaskGraphProfiler/ViewModels/TaskNode.h" #include "Insights/TaskGraphProfiler/ViewModels/TaskTimingTrack.h" #include "Insights/TimingProfiler/TimingProfilerManager.h" #include "Insights/TimingProfiler/Tracks/CpuTimingTrack.h" #include "Insights/TimingProfiler/ViewModels/ThreadTimingSharedState.h" #include "Insights/TimingProfiler/Widgets/STimingProfilerWindow.h" #include "Insights/Widgets/STimingView.h" #define LOCTEXT_NAMESPACE "UE::Insights::TaskGraphProfiler::STaskTableTreeView" namespace UE::Insights::TaskGraphProfiler { //////////////////////////////////////////////////////////////////////////////////////////////////// // FTaskTableTreeViewCommands //////////////////////////////////////////////////////////////////////////////////////////////////// class FTaskTableTreeViewCommands : public TCommands { public: FTaskTableTreeViewCommands() : TCommands( TEXT("TaskTableTreeViewCommands"), NSLOCTEXT("Contexts", "TaskTableTreeViewCommands", "Insights - Task Table Tree View"), NAME_None, FInsightsStyle::GetStyleSetName()) { } virtual ~FTaskTableTreeViewCommands() { } // UI_COMMAND takes long for the compiler to optimize UE_DISABLE_OPTIMIZATION_SHIP virtual void RegisterCommands() override { UI_COMMAND(Command_GoToTask, "Go To Task", "Pans and zooms to the task in the Timing View.", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(Command_OpenInIDE, "Open in IDE", "Opens the source location where the selected task was launched, in IDE.", EUserInterfaceActionType::Button, FInputChord()); } UE_ENABLE_OPTIMIZATION_SHIP TSharedPtr Command_GoToTask; TSharedPtr Command_OpenInIDE; }; //////////////////////////////////////////////////////////////////////////////////////////////////// STaskTableTreeView::STaskTableTreeView() { bRunInAsyncMode = true; } //////////////////////////////////////////////////////////////////////////////////////////////////// STaskTableTreeView::~STaskTableTreeView() { } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::Construct(const FArguments& InArgs, TSharedPtr InTablePtr) { ConstructWidget(InTablePtr); AddCommmands(); // Make sure the default value is applied. TimestampOptions_OnSelectionChanged(SelectedTimestampOption); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::ExtendMenu(FMenuBuilder& MenuBuilder) { MenuBuilder.BeginSection("Task", LOCTEXT("ContextMenu_Section_Task", "Task")); { MenuBuilder.AddMenuEntry( FTaskTableTreeViewCommands::Get().Command_GoToTask, NAME_None, TAttribute(), TAttribute(), FSlateIcon(FInsightsStyle::GetStyleSetName(), "Icons.GoToTask")); } { FString File; uint32 Line = 0; bool bIsValidSource = GetSourceFileAndLineForSelectedTask(File, Line); ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked("SourceCodeAccess"); ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor(); FText ItemLabel; FText ItemToolTip; if (SourceCodeAccessor.CanAccessSourceCode()) { ItemLabel = FText::Format(LOCTEXT("ContextMenu_OpenSource", "Open Source in {0}"), SourceCodeAccessor.GetNameText()); if (bIsValidSource) { ItemToolTip = FText::Format(LOCTEXT("ContextMenu_OpenSource_Desc1", "Opens the source location where the selected task was launched, in {0}.\n{1} ({2})"), SourceCodeAccessor.GetNameText(), FText::FromString(File), FText::AsNumber(Line, &FNumberFormattingOptions::DefaultNoGrouping())); } else { ItemToolTip = FText::Format(LOCTEXT("ContextMenu_OpenSource_Desc2", "Opens the source location where the selected task was launched, in {0}."), SourceCodeAccessor.GetNameText()); } } else { ItemLabel = LOCTEXT("ContextMenu_OpenSourceNA", "Open Source"); if (bIsValidSource) { ItemToolTip = FText::Format(LOCTEXT("ContextMenu_OpenSourceNA_Desc1", "{0} ({1})\nSource Code Accessor is not available."), FText::FromString(File), FText::AsNumber(Line, &FNumberFormattingOptions::DefaultNoGrouping())); } else { ItemToolTip = LOCTEXT("ContextMenu_OpenSourceNA_Desc2", "Source Code Accessor is not available."); } } MenuBuilder.AddMenuEntry( FTaskTableTreeViewCommands::Get().Command_OpenInIDE, NAME_None, ItemLabel, ItemToolTip, FSlateIcon(SourceCodeAccessor.GetStyleSet(), SourceCodeAccessor.GetOpenIconName())); } MenuBuilder.EndSection(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::AddCommmands() { FTaskTableTreeViewCommands::Register(); CommandList->MapAction(FTaskTableTreeViewCommands::Get().Command_GoToTask, FExecuteAction::CreateSP(this, &STaskTableTreeView::ContextMenu_GoToTask_Execute), FCanExecuteAction::CreateSP(this, &STaskTableTreeView::ContextMenu_GoToTask_CanExecute)); CommandList->MapAction(FTaskTableTreeViewCommands::Get().Command_OpenInIDE, FExecuteAction::CreateSP(this, &STaskTableTreeView::ContextMenu_OpenInIDE_Execute), FCanExecuteAction::CreateSP(this, &STaskTableTreeView::ContextMenu_OpenInIDE_CanExecute)); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::Reset() { STableTreeView::Reset(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { STableTreeView::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); if (!bIsUpdateRunning) { RebuildTree(false); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::RebuildTree(bool bResync) { using namespace UE::Insights::TimingProfiler; double NewQueryStartTime = FTimingProfilerManager::Get()->GetSelectionStartTime(); double NewQueryEndTime = FTimingProfilerManager::Get()->GetSelectionEndTime(); if (NewQueryStartTime >= NewQueryEndTime) { return; } if (bResync || NewQueryStartTime != QueryStartTime || NewQueryEndTime != QueryEndTime) { StopAllTableDataTasks(); QueryStartTime = NewQueryStartTime; QueryEndTime = NewQueryEndTime; TSharedPtr TaskTable = GetTaskTable(); TArray& Tasks = TaskTable->GetTaskEntries(); Tasks.Empty(); TableRowNodes.Empty(); if (QueryStartTime < QueryEndTime && Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::ITasksProvider* TasksProvider = TraceServices::ReadTasksProvider(*Session.Get()); if (TasksProvider) { FName BaseNodeName(TEXT("task")); TArray* Nodes = &TableRowNodes; TasksProvider->EnumerateTasks(QueryStartTime, QueryEndTime, SelectedTasksSelectionOption, [&Tasks, &TaskTable, &BaseNodeName, Nodes](const TraceServices::FTaskInfo& TaskInfo) { Tasks.Emplace(TaskInfo); uint32 Index = Tasks.Num() - 1; FName NodeName(BaseNodeName, static_cast(TaskInfo.Id + 1)); FTaskNodePtr NodePtr = MakeShared(NodeName, TaskTable, Index); Nodes->Add(NodePtr); return TraceServices::ETaskEnumerationResult::Continue; }); } } UpdateTree(); TreeView->RebuildList(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STaskTableTreeView::IsRunning() const { return STableTreeView::IsRunning(); } //////////////////////////////////////////////////////////////////////////////////////////////////// double STaskTableTreeView::GetAllOperationsDuration() { return STableTreeView::GetAllOperationsDuration(); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText STaskTableTreeView::GetCurrentOperationName() const { return STableTreeView::GetCurrentOperationName(); } //////////////////////////////////////////////////////////////////////////////////////////////////// BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void STaskTableTreeView::ConstructHeaderArea(TSharedRef InWidgetContent) { InWidgetContent->AddSlot() .VAlign(VAlign_Center) .AutoHeight() .Padding(2.0f) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1.0f) .VAlign(VAlign_Center) [ ConstructSearchBox() ] + SHorizontalBox::Slot() .AutoWidth() .Padding(4.0f, 0.0f, 0.0f, 0.0f) .VAlign(VAlign_Center) [ ConstructFilterConfiguratorButton() ] ]; InWidgetContent->AddSlot() .VAlign(VAlign_Center) .AutoHeight() .Padding(2.0f) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Left) .Padding(0.0f, 0.0f, 4.0f, 0.0f) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("Tasks", "Tasks")) ] + SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Left) .Padding(4.0f, 0.0f, 10.0f, 0.0f) [ SNew(SBox) .MinDesiredWidth(160.0f) [ SNew(SComboBox>) .OptionsSource(GetAvailableTasksSelectionOptions()) .OnSelectionChanged(this, &STaskTableTreeView::TasksSelectionOptions_OnSelectionChanged) .OnGenerateWidget(this, &STaskTableTreeView::TasksSelectionOptions_OnGenerateWidget) .IsEnabled(this, &STaskTableTreeView::TasksSelectionOptions_IsEnabled) [ SNew(STextBlock) .Text(this, &STaskTableTreeView::TasksSelectionOptions_GetSelectionText) ] ] ] + SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(0.0f, 0.0f, 4.0f, 0.0f) .VAlign(VAlign_Center) .HAlign(HAlign_Right) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(0.0f, 0.0f, 4.0f, 0.0f) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("Timestamps", "Timestamps")) ] + SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Right) .Padding(4.0f, 0.0f, 0.0f, 0.0f) [ SNew(SBox) .MinDesiredWidth(160.0f) [ SNew(SComboBox>) .OptionsSource(GetAvailableTimestampOptions()) .OnSelectionChanged(this, &STaskTableTreeView::TimestampOptions_OnSelectionChanged) .OnGenerateWidget(this, &STaskTableTreeView::TimestampOptions_OnGenerateWidget) .IsEnabled(this, &STaskTableTreeView::TimestampOptions_IsEnabled) [ SNew(STextBlock) .Text(this, &STaskTableTreeView::TimestampOptions_GetSelectionText) ] ] ] ] ]; InWidgetContent->AddSlot() .VAlign(VAlign_Center) .AutoHeight() .Padding(2.0f) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1.0f) .VAlign(VAlign_Center) [ ConstructHierarchyBreadcrumbTrail() ] ]; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr STaskTableTreeView::ConstructFooter() { return nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedRef STaskTableTreeView::TimestampOptions_OnGenerateWidget(TSharedPtr InOption) { auto GetTooltipText = [](ETimestampOptions InOption) { switch (InOption) { case ETimestampOptions::Absolute: { return LOCTEXT("AbsoluteValueTooltip", "The timestamps for all columns will show absolute values."); } case ETimestampOptions::RelativeToPrevious: { return LOCTEXT("RelativeToPreviousTooltip", "The timestamps for all columns will show values relative to the previous stage. Ex: Scheduled will be relative to Launched."); } case ETimestampOptions::RelativeToCreated: { return LOCTEXT("RelativeToCreatedTooltip", "The timestamps for all columns will show values relative to the created time."); } } return FText(); }; TSharedRef Widget = SNew(SHorizontalBox); Widget->AddSlot() .AutoWidth() [ SNew(STextBlock) .Text(TimestampOptions_GetText(*InOption)) .ToolTipText(GetTooltipText(*InOption)) .Margin(2.0f) ]; return Widget; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedRef STaskTableTreeView::TasksSelectionOptions_OnGenerateWidget(TSharedPtr InOption) { using namespace TraceServices; auto GetTooltipText = [](ETaskEnumerationOption InOption) { switch (InOption) { case ETaskEnumerationOption::Alive: { return LOCTEXT("AliveTooltip", "Tasks that were created before the selection and destroyed after."); } case ETaskEnumerationOption::Launched: { return LOCTEXT("LaunchedTooltip", "Tasks that were launched during the selection."); } case ETaskEnumerationOption::Active: { return LOCTEXT("ActiveTooltip", "Tasks that were active (being executed) at any moment of the selection."); } case ETaskEnumerationOption::WaitingForPrerequisites: { return LOCTEXT("WaitingForPrerequisitesTooltip", "Tasks that are blocked by prerequisites for the entire duration of the selection."); } case ETaskEnumerationOption::Queued: { return LOCTEXT("QueuedTooltip", "Tasks that for the entire duration of the selection are waiting in the scheduler queue."); } case ETaskEnumerationOption::Executing: { return LOCTEXT("ExecutingTooltip", "Tasks that for the entire duration of the selection have been executed."); } case ETaskEnumerationOption::WaitingForNested: { return LOCTEXT("WaitingForNestedTooltip", "Tasks that for the entire duration of the selection have been waiting for nested tasks."); } case ETaskEnumerationOption::Completed: { return LOCTEXT("CompletedTooltip", "Tasks that for the entire duration of the selection have been already completed but not destroyed."); } } return FText(); }; TSharedRef Widget = SNew(SHorizontalBox); Widget->AddSlot() .AutoWidth() [ SNew(STextBlock) .Text(TasksSelectionOptions_GetText(*InOption)) .ToolTipText(GetTooltipText(*InOption)) .Margin(2.0f) ]; return Widget; } END_SLATE_FUNCTION_BUILD_OPTIMIZATION //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::InternalCreateGroupings() { STableTreeView::InternalCreateGroupings(); } //////////////////////////////////////////////////////////////////////////////////////////////////// const TArray>* STaskTableTreeView::GetAvailableTimestampOptions() { if (AvailableTimestampOptions.Num() == 0) { AvailableTimestampOptions.Add(MakeShared(ETimestampOptions::Absolute)); AvailableTimestampOptions.Add(MakeShared(ETimestampOptions::RelativeToPrevious)); AvailableTimestampOptions.Add(MakeShared(ETimestampOptions::RelativeToCreated)); } return &AvailableTimestampOptions; } //////////////////////////////////////////////////////////////////////////////////////////////////// const TArray>* STaskTableTreeView::GetAvailableTasksSelectionOptions() { if (AvailableTasksSelectionOptions.Num() == 0) { using namespace TraceServices; AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::Alive)); AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::Launched)); AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::Active)); AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::WaitingForPrerequisites)); AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::Queued)); AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::Executing)); AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::WaitingForNested)); AvailableTasksSelectionOptions.Add(MakeShared(ETaskEnumerationOption::Completed)); } return &AvailableTasksSelectionOptions; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::TimestampOptions_OnSelectionChanged(TSharedPtr InOption, ESelectInfo::Type SelectInfo) { TimestampOptions_OnSelectionChanged(*InOption); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::TimestampOptions_OnSelectionChanged(ETimestampOptions InOption) { SelectedTimestampOption = InOption; switch (SelectedTimestampOption) { case ETimestampOptions::Absolute: { GetTaskTable()->SwitchToAbsoluteTimestamps(); break; } case ETimestampOptions::RelativeToPrevious: { GetTaskTable()->SwitchToRelativeToPreviousTimestamps(); break; } case ETimestampOptions::RelativeToCreated: { GetTaskTable()->SwitchToRelativeToCreatedTimestamps(); break; } } UpdateTree(); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::TasksSelectionOptions_OnSelectionChanged(TSharedPtr InOption, ESelectInfo::Type SelectInfo) { TasksSelectionOptions_OnSelectionChanged(*InOption); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::TasksSelectionOptions_OnSelectionChanged(TraceServices::ETaskEnumerationOption InOption) { SelectedTasksSelectionOption = InOption; RebuildTree(true); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText STaskTableTreeView::TimestampOptions_GetSelectionText() const { return TimestampOptions_GetText(SelectedTimestampOption); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText STaskTableTreeView::TimestampOptions_GetText(ETimestampOptions InOption) const { switch (InOption) { case ETimestampOptions::Absolute: { return LOCTEXT("Absolute", "Absolute"); } case ETimestampOptions::RelativeToPrevious: { return LOCTEXT("RelativeToPrevious", "Relative To Previous"); } case ETimestampOptions::RelativeToCreated: { return LOCTEXT("RelativeToCreated", "Relative To Created"); } } return FText(); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText STaskTableTreeView::TasksSelectionOptions_GetSelectionText() const { return TasksSelectionOptions_GetText(SelectedTasksSelectionOption); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText STaskTableTreeView::TasksSelectionOptions_GetText(TraceServices::ETaskEnumerationOption InOption) const { using namespace TraceServices; switch (InOption) { case ETaskEnumerationOption::Alive: { return LOCTEXT("Alive", "Alive"); } case ETaskEnumerationOption::Launched: { return LOCTEXT("Launched", "Launched"); } case ETaskEnumerationOption::Active: { return LOCTEXT("Active", "Active"); } case ETaskEnumerationOption::WaitingForPrerequisites: { return LOCTEXT("WaitingForPrerequisites", "Waiting for prerequisites"); } case ETaskEnumerationOption::Queued: { return LOCTEXT("Queued", "Queued"); } case ETaskEnumerationOption::Executing: { return LOCTEXT("Executing", "Executing"); } case ETaskEnumerationOption::WaitingForNested: { return LOCTEXT("WaitingForNested", "Waiting for nested"); } case ETaskEnumerationOption::Completed: { return LOCTEXT("Completed", "Completed"); } } return FText(); } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STaskTableTreeView::TimestampOptions_IsEnabled() const { return !bIsUpdateRunning; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STaskTableTreeView::TasksSelectionOptions_IsEnabled() const { return !bIsUpdateRunning; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STaskTableTreeView::ContextMenu_GoToTask_CanExecute() const { TArray SelectedItems; TreeView->GetSelectedItems(SelectedItems); if (SelectedItems.Num() != 1) { return false; } FTaskNodePtr SelectedTask = StaticCastSharedPtr(SelectedItems[0]); if (!SelectedTask.IsValid() || SelectedTask->IsGroup()) { return false; } return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::ContextMenu_GoToTask_Execute() { TArray SelectedItems; TreeView->GetSelectedItems(SelectedItems); if (SelectedItems.Num() != 1) { return; } FTaskNodePtr SelectedTask = StaticCastSharedPtr(SelectedItems[0]); const FTaskEntry* TaskEntry = SelectedTask.IsValid() ? SelectedTask->GetTask() : nullptr; if (TaskEntry == nullptr) { return; } using namespace UE::Insights::TimingProfiler; TSharedPtr TimingWindow = FTimingProfilerManager::Get()->GetProfilerWindow(); if (!TimingWindow.IsValid()) { return; } TSharedPtr TimingView = TimingWindow->GetTimingView(); if (!TimingView.IsValid()) { return; } double Duration = (TaskEntry->GetFinishedTimestamp() - TaskEntry->GetCreatedTimestamp()) * 1.5; TimingView->ZoomOnTimeInterval(TaskEntry->GetCreatedTimestamp() - Duration * 0.15, Duration); TSharedPtr TaskSharedState = FTaskGraphProfilerManager::Get()->GetTaskTimingSharedState(); if (TaskSharedState.IsValid() && FTaskGraphProfilerManager::Get()->GetShowAnyRelations()) { TaskSharedState->SetTaskId(TaskEntry->GetId()); } TSharedPtr ThreadTimingState = TimingView->GetThreadTimingSharedState(); if (!ThreadTimingState.IsValid()) { return; } TSharedPtr Track = ThreadTimingState->GetCpuTrack(TaskEntry->GetStartedThreadId()); if (!Track.IsValid()) { return; } TimingView->SelectTimingTrack(Track, true); auto SearchFilter = [TaskEntry](double StartTime, double EndTime, uint32 Depth) { if (StartTime >= TaskEntry->GetStartedTimestamp() && EndTime <= TaskEntry->GetFinishedTimestamp()) { return true; } return false; }; FTimingEventSearchParameters SearchParams(TaskEntry->StartedTimestamp, TaskEntry->FinishedTimestamp, ETimingEventSearchFlags::StopAtFirstMatch, SearchFilter); const TSharedPtr FoundEvent = Track->SearchEvent(SearchParams); if (FoundEvent.IsValid() && Track->IsVisible()) { TimingView->SelectTimingEvent(FoundEvent, true); } else { FTaskGraphProfilerManager::Get()->ShowTaskRelations(TaskEntry->GetId()); } } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STaskTableTreeView::ContextMenu_OpenInIDE_CanExecute() const { ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked("SourceCodeAccess"); ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor(); if (!SourceCodeAccessor.CanAccessSourceCode()) { return false; } FString File; uint32 Line = 0; return GetSourceFileAndLineForSelectedTask(File, Line); } //////////////////////////////////////////////////////////////////////////////////////////////////// bool STaskTableTreeView::GetSourceFileAndLineForSelectedTask(FString& OutFile, uint32& OutLine) const { TArray SelectedItems; TreeView->GetSelectedItems(SelectedItems); if (SelectedItems.Num() != 1) { return false; } FTableTreeNodePtr SelectedTreeNode = SelectedItems[0]; if (!SelectedTreeNode.IsValid() || !SelectedTreeNode->Is()) { return false; } FTaskNodePtr SelectedTask = StaticCastSharedPtr(SelectedTreeNode); const FTaskEntry* TaskEntry = SelectedTask->GetTask(); if (TaskEntry == nullptr || TaskEntry->DebugName == nullptr) { return false; } const FString DebugName = TaskEntry->DebugName; if (DebugName.Len() < 4 || DebugName[DebugName.Len() - 1] != TEXT(')')) { return false; } int32 OpenBracketPos; if (!DebugName.FindChar(TEXT('('), OpenBracketPos) || OpenBracketPos > DebugName.Len() - 3) { return false; } OutFile = DebugName.Left(OpenBracketPos); FString LineStr = DebugName.Mid(OpenBracketPos + 1, DebugName.Len() - OpenBracketPos - 2); OutLine = FCString::Atoi(*LineStr); return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::ContextMenu_OpenInIDE_Execute() { FString File; uint32 Line = 0; if (!GetSourceFileAndLineForSelectedTask(File, Line)) { return; } ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked("SourceCodeAccess"); if (FPaths::FileExists(File)) { ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor(); SourceCodeAccessor.OpenFileAtLine(File, Line); } else { SourceCodeAccessModule.OnOpenFileFailed().Broadcast(File); } } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::TreeView_OnMouseButtonDoubleClick(FTableTreeNodePtr TreeNode) { if (!TreeNode->IsGroup()) { ContextMenu_GoToTask_Execute(); } STableTreeView::TreeView_OnMouseButtonDoubleClick(TreeNode); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::SelectTaskEntry(TaskTrace::FId InId) { TaskIdToSelect = InId; StartTableDataTask(SharedThis(this)); } //////////////////////////////////////////////////////////////////////////////////////////////////// void STaskTableTreeView::SearchForItem(TSharedPtr CancellationToken) { TSharedPtr TaskTable = GetTaskTable(); TArray& Tasks = TaskTable->GetTaskEntries(); uint32 NumEntries = (uint32)Tasks.Num(); for (uint32 Index = 0; Index < NumEntries; ++Index) { if (CancellationToken->ShouldCancel()) { break; } if (Tasks[Index].Id == TaskIdToSelect) { TGraphTask::CreateTask().ConstructAndDispatchWhenReady(CancellationToken, SharedThis(this), Index); break; } } } //////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace UE::Insights::TaskGraphProfiler #undef LOCTEXT_NAMESPACE