Files
UnrealEngine/Engine/Source/Developer/Profiler/Private/Widgets/SMultiDumpBrowser.cpp
2025-05-18 13:04:45 +08:00

201 lines
5.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Widgets/SMultiDumpBrowser.h"
#if STATS
#define LOCTEXT_NAMESPACE "SMultiDumpBrowser"
void SMultiDumpBrowser::Construct(const FArguments& InArgs)
{
this->SetEnabled(true);
ChildSlot
[
SNew(SOverlay)
+ SOverlay::Slot()
.Padding(4)
.VAlign(VAlign_Fill)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot().AutoHeight().Padding(2)
[
SNew(SBorder).BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")).Padding(4).HAlign(HAlign_Fill)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(STextBlock).Text(LOCTEXT("MultiDumpBrowserThreadTotals", "Show thread totals for:"))
]
+ SHorizontalBox::Slot().HAlign(HAlign_Fill)
[
SAssignNew(DisplayTotalsBox, SEditableTextBox).OnTextCommitted(this, &SMultiDumpBrowser::PrefilterTextCommitted).ToolTipText(LOCTEXT("MultiDumpBrowserTooltip", "Use \"Load Folder\" above to load a folder of stats dumps. GT/RT totals for stats matching text entered here will be displayed in the list below - e.g. enter \"Particle\" here to show total thread times for particle emitters."))
]
]
]
+ SVerticalBox::Slot().Padding(2).FillHeight(1.0f)
[
SNew(SBorder).BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")).Padding(4).HAlign(HAlign_Fill).VAlign(VAlign_Fill)
[
SAssignNew(FileList, SListView<TSharedPtr<FFileDescriptor>>)
.ListItemsSource(&StatsFiles)
.OnGenerateRow(this, &SMultiDumpBrowser::GenerateFileRow)
.OnSelectionChanged(this, &SMultiDumpBrowser::SelectionChanged)
]
]
]
];
}
TSharedRef<ITableRow> SMultiDumpBrowser::GenerateFileRow(TSharedPtr<FFileDescriptor> FileItem, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(SFileTableRow, OwnerTable, FileItem);
}
void SMultiDumpBrowser::SelectionChanged(TSharedPtr<FFileDescriptor> SelectedItem, ESelectInfo::Type SelType)
{
FProfilerManager::Get()->LoadProfilerCapture(SelectedItem->FullPath);
}
void SMultiDumpBrowser::Update()
{
FileList->RequestListRefresh();
}
void SMultiDumpBrowser::AddFile(FFileDescriptor *InFileDesc)
{
StatsFiles.Add(MakeShareable(InFileDesc));
}
void SMultiDumpBrowser::Clear()
{
StatsFiles.Empty();
}
void SMultiDumpBrowser::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
static bool bCurrentlyLoading = false;
// iterate over all stats files, load one at a time, analyze
for (auto Desc : StatsFiles)
{
// if we're not currently loading one and we've found one in the list that was added but not analyzed, load it
if (Desc->Status == FFileDescriptor::EQueued && bCurrentlyLoading == false)
{
FProfilerManager::Get()->LoadProfilerCapture(Desc->FullPath);
Desc->Status = FFileDescriptor::ELoading;
Desc->TimeString = "Getting timings, please wait...";
bCurrentlyLoading = true;
}
// if the current load is completed, start summing up thread totals for the term we're looking for in the DisplayTotalsBox
if (FProfilerManager::Get()->IsCaptureFileFullyProcessed() && Desc->Status == FFileDescriptor::ELoading)
{
Desc->Status = FFileDescriptor::ELoaded;
bCurrentlyLoading = false;
FindTotalsForPrefilter(Desc);
Desc->Status = FFileDescriptor::EAnalyzed;
}
}
if (bCurrentlyLoading == true)
{
this->SetEnabled(false);
}
else
{
this->SetEnabled(true);
}
SWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
}
void SMultiDumpBrowser::FindTotalsForPrefilterRecursive(TSharedPtr<FEventGraphSample> Event, float &OutTotalTime)
{
FString EventName = Event->_StatName.ToString().ToUpper();
if (EventName.Contains(TotalsFilteringText.ToUpper()))
{
OutTotalTime += static_cast<float>(Event->_InclusiveTimeMS);
}
else
{
for (auto Child : Event->GetChildren())
{
FindTotalsForPrefilterRecursive(Child, OutTotalTime);
}
}
}
void SMultiDumpBrowser::FindTotalsForPrefilter(TSharedPtr<FFileDescriptor> Desc)
{
float TotalRenderThreadTime = 0;
float TotalGameThreadTime = 0;
// if we don't have a totals filtering text, display the entire RT and GT times
bool bGetBaseThreadTimes = false;
if (TotalsFilteringText == "")
{
bGetBaseThreadTimes = true;
}
FEventGraphSamplePtr RootPtr = FProfilerManager::Get()->GetProfilerSession()->GetEventGraphDataAverage()->GetRoot();
// start at the root's children, which are the threads
for (auto Child : RootPtr->GetChildren())
{
TArray<FString>EventNameTokens;
Child->_ThreadName.ToString().ParseIntoArray(EventNameTokens, TEXT(" "));
// get cumulative times for stats on the render thread
if (EventNameTokens[0] == "RenderThread")
{
if (bGetBaseThreadTimes)
{
TotalsFilteringText = "RenderThread";
}
FindTotalsForPrefilterRecursive(Child, TotalRenderThreadTime);
}
// get cumulative times for stats on the game thread
else if (EventNameTokens[0] == "GameThread")
{
if (bGetBaseThreadTimes)
{
TotalsFilteringText = "GameThread";
}
FindTotalsForPrefilterRecursive(Child, TotalGameThreadTime);
}
if (bGetBaseThreadTimes)
{
TotalsFilteringText = "";
}
}
Desc->TimeString = "RT " + FString::Printf(TEXT("%.2f"), TotalRenderThreadTime) + " / GT " + FString::Printf(TEXT("%.2f"), TotalGameThreadTime);
}
void SMultiDumpBrowser::PrefilterTextCommitted(const FText& InText, const ETextCommit::Type InTextAction)
{
TotalsFilteringText = InText.ToString();
for (auto Desc : StatsFiles)
{
Desc->Status = FFileDescriptor::EQueued;
}
}
#undef LOCTEXT_NAMESPACE
#endif // STATS