Files
UnrealEngine/Engine/Plugins/TextureGraph/Source/TextureGraphInsight/Private/View/STextureGraphInsightDeviceView.cpp
2025-05-18 13:04:45 +08:00

644 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "View/STextureGraphInsightDeviceView.h"
#include "TextureGraphInsight.h"
#include "Model/TextureGraphInsightSession.h"
#include "Device/DeviceManager.h"
#include "Data/Blobber.h"
#include "TextureGraphEngine.h"
#include <Widgets/Views/ITableRow.h>
#include <Widgets/Views/STableViewBase.h>
#include <Widgets/Text/STextBlock.h>
class STextureGraphInsightDeviceListViewRow : public SMultiColumnTableRow<STextureGraphInsightDeviceListView::FItem>
{
public:
using FItem = STextureGraphInsightDeviceListView::FItem;
using FItemW = STextureGraphInsightDeviceListView::FItemW;
SLATE_BEGIN_ARGS(STextureGraphInsightDeviceListViewRow) {}
SLATE_ARGUMENT(FItem, Item)
SLATE_END_ARGS()
public:
enum Column {
Main = 0,
Count,
Hash,
Hash1,
Hash2,
MemSize,
Pixels,
NUM_COLUMNS,
};
using ColumnInfo = std::pair<FName, float>;
static ColumnInfo s_columnNames[NUM_COLUMNS];
static Column NameToColumn(const FName& name) {
for (int i = 0; i < NUM_COLUMNS; ++i)
{
if (name == s_columnNames[i].first)
return Column(i);
}
return NUM_COLUMNS;
}
void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView)
{
_item = InArgs._Item;
_recordID = InArgs._Item->_recordID;
SMultiColumnTableRow<FItem >::Construct(FSuperRowType::FArguments(), InOwnerTableView);
Refresh();
}
virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& columnName) override
{
Column column = NameToColumn(columnName);
if (column == Main) {
// Rows in a TreeView need an expander button and some indentation
return SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Right)
.VAlign(VAlign_Fill)
[
SNew(SExpanderArrow, SharedThis(this))
.StyleSet(ExpanderStyleSet)
]
+ SHorizontalBox::Slot()
.FillWidth(1)
.Padding(FMargin(4.0f, 0.0f))
.VAlign(VAlign_Center)
[
SAssignNew(_textBoxes[column], STextBlock)
.Text(GetTextForColumn(column))
];
}
else
{
return SNew(SBox)
.Padding(FMargin(4.0f, 0.0f))
.VAlign(VAlign_Center)
[
SAssignNew(_textBoxes[column], STextBlock)
.Text(GetTextForColumn(column))
.OnDoubleClicked(this, &STextureGraphInsightDeviceListViewRow::OnDoubleClickedResultColumn)
];
}
// default to null widget if property cannot be found
return SNullWidget::NullWidget;
}
FText GetTextForColumn(Column column) const
{
bool isDevice = (_recordID.IsDevice());
const auto& br = TextureGraphInsight::Instance()->GetSession()->GetRecord().GetBuffer(_recordID);
auto memSize = br.RawBufferMemSize;
FString s;
switch (column)
{
case Main:
if (isDevice)
{
auto item = _item.Pin();
if (item->_metaItem == 1)
s = FString(TEXT("erased"));
else
s = (Device::DeviceType_Names((DeviceType) _recordID.Buffer_DeviceType()));
}
else
s = br.Descriptor.Name;
// s = FString::FromInt(_recordID.Buffer());
break;
case Count:
if (isDevice)
{
auto item = _item.Pin();
s = FString::FromInt(item->_children.Num());
}
else
{
s = FString::FromInt(br.Rank);
}
break;
case Hash:
if (isDevice)
{
}
else
{
auto& h = br.ID; // the key used to find this buffer (ptr)
s = HashToFString(h);
}
break;
case Hash1:
if (isDevice)
{
}
else
{
auto& h = br.HashValue;
s = HashToFString(h);
}
break;
case Hash2:
if (isDevice)
{
}
else
{
auto& h = br.PrevHashValue;
s = HashToFString(h);
}
break;
case MemSize:
if (isDevice)
{
}
else
{
s = FString::FromInt(memSize / 1024) + " kb";
}
break;
case Pixels:
if (isDevice)
{
}
else
{
if (br.Descriptor.Width || br.Descriptor.Height)
{
s = FString::FromInt(br.Descriptor.Width) + " x " + FString::FromInt(br.Descriptor.Height);
}
}
break;
}
return FText::FromString(s);
}
void Refresh()
{
if (_recordID.IsDevice())
{
if (!TextureGraphEngine::GetInstance())
{
SetColorAndOpacity(FLinearColor(0.3, 0.3, 0.3));
}
else
{
auto item = _item.Pin();
auto device = TextureGraphEngine::GetInstance()->GetDeviceManager()->GetDevice(_recordID.Buffer_DeviceType());
if (!device)
{
SetColorAndOpacity(FLinearColor(0.2, 0.2, 0.2));
}
else if (item->_metaItem == 1)
{
SetColorAndOpacity(FLinearColor(0.5, 0.5, 0.5));
_textBoxes[Count]->SetText(FText::FromString(FString::FromInt(item->_children.Num())));
}
else
{ // This is a true device item, update the data
auto count = device->GetNumDeviceBuffersUsed();
if (count == 0)
SetColorAndOpacity(FLinearColor(0.7, 0.7, 0.7));
if (count != item->_children.Num())
SetColorAndOpacity(FLinearColor(1.0, 0.5, 0.5));
else
SetColorAndOpacity(FLinearColor(1.0, 1.0, 1.0));
_textBoxes[Count]->SetText(FText::FromString(FString::FromInt(item->_children.Num())));
_textBoxes[Hash]->SetText(FText::FromString(FString::FromInt(count)));
}
}
}
else
{
const auto& br = TextureGraphInsight::Instance()->GetSession()->GetRecord().GetBuffer(_recordID);
if (br.bLeaked)
SetColorAndOpacity(FLinearColor(1.0, 0.8, 0.5));
else if (br.bErased)
SetColorAndOpacity(FLinearColor(0.5, 0.5, 0.5));
else
SetColorAndOpacity(FLinearColor(0.5, 1.0, 1.0));
_textBoxes[Count]->SetText(GetTextForColumn(Count));
}
}
FReply OnDoubleClickedResultColumn(/** The geometry of the widget*/
const FGeometry&,
/** The Mouse Event that we are processing */
const FPointerEvent&)
{
TextureGraphInsight::Instance()->GetSession()->SendToInspector(_recordID);
return FReply::Handled();
}
protected:
FItemW _item;
RecordID _recordID;
TSharedPtr<STextBlock> _textBoxes[NUM_COLUMNS];
};
STextureGraphInsightDeviceListViewRow::ColumnInfo STextureGraphInsightDeviceListViewRow::s_columnNames[] = {
{ FName(TEXT("Name")), 0.5}, // main
{ FName(TEXT("Rank")), 0.1},
{ FName(TEXT("Ptr")), 0.1},
{ FName(TEXT("Hash")), 0.1},
{ FName(TEXT("HashPrev")), 0.1},
{ FName(TEXT("Size")), 0.05},
{ FName(TEXT("Pixels")), 0.05}
};
void STextureGraphInsightDeviceListView::Construct(const FArguments& Args)
{
_inspectOnSimpleClick = Args._inspectOnSimpleClick;
_inspectDevices = Args._inspectDevices;
TSharedPtr<SHeaderRow> headerRow = SNew(SHeaderRow);
for (int i = 0; i < STextureGraphInsightDeviceListViewRow::NUM_COLUMNS; i++)
{
headerRow->AddColumn(
SHeaderRow::Column(STextureGraphInsightDeviceListViewRow::s_columnNames[i].first)
.DefaultLabel(FText::FromString(STextureGraphInsightDeviceListViewRow::s_columnNames[i].first.ToString()))
.FillWidth(STextureGraphInsightDeviceListViewRow::s_columnNames[i].second)
);
}
ChildSlot
[
SAssignNew(_tableView, SItemTableView)
.TreeItemsSource(&_rootItems)
.OnGenerateRow(this, &STextureGraphInsightDeviceListView::OnGenerateRowForView)
.OnGetChildren(this, &STextureGraphInsightDeviceListView::OnGetChildrenForView)
.OnMouseButtonDoubleClick(this, &STextureGraphInsightDeviceListView::OnDoubleClickItemForView)
.OnMouseButtonClick(this, &STextureGraphInsightDeviceListView::OnClickItemForView)
.HeaderRow(headerRow)
];
// If a valid recordID was specified, let's use it as the root device type,
// meaning we only care about that device type
// the value of the buffer index doesn't matter
if (Args._recordID.IsBuffer())
_rootDevice = Args._recordID;
// Allocate the root items
for (int i = 0; i < (int)DeviceType::Count; ++i)
{
auto deviceType = (DeviceType)i;
auto rid = RecordID::fromBuffer(deviceType, INVALID_INDEX);
FItem item = MakeShareable(new FItemData(rid));
_rootItems.Add(item);
}
// install the observer notifications:
auto sr = StaticCastSharedRef<STextureGraphInsightDeviceListView>(this->AsShared());
TextureGraphInsight::Instance()->GetSession()->OnDeviceBufferAdded().AddSP(sr, &STextureGraphInsightDeviceListView::OnDeviceBufferAdded);
TextureGraphInsight::Instance()->GetSession()->OnDeviceBufferRemoved().AddSP(sr, &STextureGraphInsightDeviceListView::OnDeviceBufferRemoved);
TextureGraphInsight::Instance()->GetSession()->OnEngineReset().AddSP(sr, &STextureGraphInsightDeviceListView::OnEngineReset);
}
void STextureGraphInsightDeviceListView::Reset()
{
for (int i = 0; i < (int)DeviceType::Count; ++i)
{
auto deviceType = (DeviceType)i;
_rootItems[i]->_children.Empty();
_rootItems[i]->_removedRoot.Reset();
if (_rootItems[i]->_widget) _rootItems[i]->_widget->Refresh();
if (TextureGraphInsight::Instance()->GetSession())
{
auto deviceBufferIDs = TextureGraphInsight::Instance()->GetSession()->GetRecord().FetchActiveDeviceBufferIDs((DeviceType)i);
for (auto rid : deviceBufferIDs)
{
_rootItems[i]->_children.Add(MakeShareable(new FItemData(rid)));
}
}
}
if (_rootDevice.IsDevice())
{
_tableView->SetItemExpansion(_rootItems[_rootDevice.Buffer_DeviceType()], true);
}
_tableView->RequestListRefresh();
RefreshRootItems();
}
TSharedRef<ITableRow> STextureGraphInsightDeviceListView::OnGenerateRowForView(FItem item, const TSharedRef<STableViewBase>& OwnerTable)
{
return SAssignNew(item->_widget, STextureGraphInsightDeviceListViewRow, OwnerTable).Item(item);
}
void STextureGraphInsightDeviceListView::OnClickItemForView(FItem item)
{
if (_inspectOnSimpleClick)
OnInspect(item);
}
void STextureGraphInsightDeviceListView::OnDoubleClickItemForView(FItem item)
{
OnInspect(item);
}
void STextureGraphInsightDeviceListView::OnInspect(FItem item)
{
if (item->_recordID.IsDevice())
{
if (item->_metaItem > 0)
return;
if (!_inspectDevices)
return;
}
auto record = item->_recordID;
TextureGraphInsight::Instance()->GetSession()->SendToInspector(record);
}
void STextureGraphInsightDeviceListView::OnDeviceBufferAdded(const RecordIDArray& rids)
{
for (const auto& rid : rids)
{
if (!rid.IsBuffer() || (rid.Buffer() == INVALID_INDEX))
continue;
auto i = rid.Buffer_DeviceType();
FItem item = MakeShareable(new FItemData(rid));
_rootItems[i]->_children.Add(item);
}
RefreshRootItems();
_tableView->RequestListRefresh();
}
void STextureGraphInsightDeviceListView::OnDeviceBufferRemoved(const RecordIDArray& rids)
{
for (const auto& rid : rids)
{
if (!rid.IsBuffer() || (rid.Buffer() == INVALID_INDEX))
continue;
// First let's find the position of the record in the existing model:
auto& root = _rootItems[rid.Buffer_DeviceType()];
auto& children = root->_children;
auto index = children.IndexOfByKey(rid);
if (index == INDEX_NONE)
continue; /// This should not happen
// Then let's move the removed item to the sub group in removedRoot
// First make sure the removedRoot exists
if (!root->_removedRoot)
{
root->_removedRoot = MakeShareable(new FItemData(root->_recordID, 1));
//_tableView->SetItemExpansion(root->_removedRoot, true);
}
// Then move the item there
root->_removedRoot->_children.Add(children[index]);
children.RemoveAt(index);
}
RefreshRootItems();
_tableView->RequestListRefresh();
}
void STextureGraphInsightDeviceListView::OnEngineReset(int id)
{
Reset();
}
bool operator <(const STextureGraphInsightDeviceListView::FItem& a, const STextureGraphInsightDeviceListView::FItem& b)
{
return ((uint32) a->_rank) < ((uint32) b->_rank);
}
void STextureGraphInsightDeviceListView::RefreshRootItems()
{
for (auto& i: _rootItems)
{
for (auto& c : i->_children)
{
const auto& br = TextureGraphInsight::Instance()->GetSession()->GetRecord().GetBuffer(c->_recordID);
c->_rank = br.Rank;
}
i->_children.Sort();
if (i->_widget)
{
i->_widget->Refresh();
for (auto& c : i->_children)
{
if (c->_widget)
{
c->_widget->Refresh();
}
}
}
if (i->_removedRoot && i->_removedRoot->_widget)
{
i->_removedRoot->_widget->Refresh();
}
}
}
void STextureGraphInsightDeviceView::Construct(const FArguments& Args)
{
_deviceType = (DeviceType) Args._deviceType;
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(FText::FromString(Device::DeviceType_Names(_deviceType)))
]
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(FText::FromString("Threads"))
.Justification(ETextJustify::Right)
]
+ SHorizontalBox::Slot()
[
SAssignNew(_threadCount, STextBlock)
.Text(FText::FromString("__"))
.AutoWrapText(true)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(FText::FromString("Memory"))
]
+ SHorizontalBox::Slot()
[
SAssignNew(_mem_percent, STextBlock)
.Text(FText::FromString("___%"))
.AutoWrapText(true)
.Justification(ETextJustify::Right)
]
+ SHorizontalBox::Slot()
[
SAssignNew(_mem_number, STextBlock)
.Text(FText::FromString("______ Kb"))
.AutoWrapText(true)
.Justification(ETextJustify::Right)
]
+ SHorizontalBox::Slot()
[
SAssignNew(_mem_budget, STextBlock)
.Text(FText::FromString("______ Kb"))
.AutoWrapText(true)
.Justification(ETextJustify::Right)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(FText::FromString("Buffers"))
]
+ SHorizontalBox::Slot()
[
SAssignNew(_buffer_percent, STextBlock)
.Text(FText::FromString("___%"))
.AutoWrapText(true)
.Justification(ETextJustify::Right)
]
+ SHorizontalBox::Slot()
[
SAssignNew(_buffer_number, STextBlock)
.Text(FText::FromString("______"))
.AutoWrapText(true)
.Justification(ETextJustify::Right)
]
+ SHorizontalBox::Slot()
[
SAssignNew(_buffer_budget, STextBlock)
.Text(FText::FromString("______"))
.AutoWrapText(true)
.Justification(ETextJustify::Right)
]
]
];
}
void STextureGraphInsightDeviceView::Refresh()
{
auto device = TextureGraphEngine::GetInstance()->GetDeviceManager()->GetDevice(_deviceType);
_threadCount->SetText(FText::FromString(FString::FromInt(device->GetMaxThreads())));
auto memMax = device->GetMaxMemory();
auto memUsed = device->GetMemUsed();
auto memPercent = 0;
if (memMax > 0)
memPercent = (memUsed * 100) / memMax;
auto bufferMax = device->GetNumDeviceBuffersMax();
auto bufferUsed = device->GetNumDeviceBuffersUsed();
auto bufferPercent = 0;
if (bufferMax > 0)
bufferPercent = (bufferUsed * 100) / bufferMax;
_mem_percent->SetText(FText::FromString(FString::FromInt(memPercent)));
_mem_number->SetText(FText::FromString(FString::FromInt(memUsed / (1024 * 1024)) + " Mb"));
_mem_budget->SetText(FText::FromString(FString::FromInt(memMax / (1024 * 1024)) + " Mb"));
_buffer_percent->SetText(FText::FromString(FString::FromInt(bufferPercent)));
_buffer_number->SetText(FText::FromString(FString::FromInt(bufferUsed)));
_buffer_budget->SetText(FText::FromString(FString::FromInt(bufferMax)));
}
void STextureGraphInsightDeviceManagerView::Construct(const FArguments& Args)
{
// install the observer notifications
auto sr = StaticCastSharedRef<STextureGraphInsightDeviceManagerView>(this->AsShared());
TextureGraphInsight::Instance()->GetSession()->OnUpdateIdle().AddSP(sr, &STextureGraphInsightDeviceManagerView::OnUpdate);
TextureGraphInsight::Instance()->GetSession()->OnEngineReset().AddSP(sr, &STextureGraphInsightDeviceManagerView::OnEngineReset);
}
void STextureGraphInsightDeviceManagerView::Reset()
{
_deviceViews.clear();
TSharedPtr<SVerticalBox> vbox;
ChildSlot
[
SAssignNew(vbox, SVerticalBox)
];
if (TextureGraphEngine::GetInstance())
{
for (int i = 0; i < (int)DeviceType::Count; ++i)
{
//if (i == (int)DeviceType::Null)
// continue;
auto deviceType = (DeviceType)i;
auto device = TextureGraphEngine::GetInstance()->GetDeviceManager()->GetDevice(deviceType);
if (device)
{
TSharedPtr< STextureGraphInsightDeviceView> deviceView;
vbox->AddSlot()
[
SAssignNew(deviceView, STextureGraphInsightDeviceView)
.deviceType(deviceType)
];
_deviceViews.push_back(deviceView);
}
}
}
}
void STextureGraphInsightDeviceManagerView::OnUpdate()
{
for (auto& d : _deviceViews)
{
d->Refresh();
}
}
void STextureGraphInsightDeviceManagerView::OnEngineReset(int32 id)
{
Reset();
}