// 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 #include #include class STextureGraphInsightDeviceListViewRow : public SMultiColumnTableRow { 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; 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& InOwnerTableView) { _item = InArgs._Item; _recordID = InArgs._Item->_recordID; SMultiColumnTableRow::Construct(FSuperRowType::FArguments(), InOwnerTableView); Refresh(); } virtual TSharedRef 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 _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 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(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 STextureGraphInsightDeviceListView::OnGenerateRowForView(FItem item, const TSharedRef& 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(this->AsShared()); TextureGraphInsight::Instance()->GetSession()->OnUpdateIdle().AddSP(sr, &STextureGraphInsightDeviceManagerView::OnUpdate); TextureGraphInsight::Instance()->GetSession()->OnEngineReset().AddSP(sr, &STextureGraphInsightDeviceManagerView::OnEngineReset); } void STextureGraphInsightDeviceManagerView::Reset() { _deviceViews.clear(); TSharedPtr 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(); }