From 66f1cbfb3412839bde38e6598570953b89068d02 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 8 Jun 2020 12:33:40 +0200 Subject: [PATCH] kernel_explorer: keep existing trees expanded --- rpcs3/rpcs3qt/kernel_explorer.cpp | 433 ++++++++++++++++++++---------- rpcs3/rpcs3qt/kernel_explorer.h | 18 +- rpcs3/rpcs3qt/qt_utils.cpp | 23 ++ rpcs3/rpcs3qt/qt_utils.h | 7 + 4 files changed, 338 insertions(+), 143 deletions(-) diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 0cf0c05cf6..77c2f3880c 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -1,5 +1,12 @@ #include "stdafx.h" +#include + +#include +#include +#include +#include + #include "Emu/IdManager.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/SPUThread.h" @@ -23,10 +30,106 @@ #include "Emu/RSX/RSXThread.h" #include "kernel_explorer.h" +#include "qt_utils.h" -#include -#include -#include +constexpr auto qstr = QString::fromStdString; + +enum kernel_item_role +{ + name_role = Qt::UserRole + 0, + expanded_role = Qt::UserRole + 1, + type_role = Qt::UserRole + 2, + id_role = Qt::UserRole + 3, +}; + +enum kernel_item_type : int +{ + root, + node, + volatile_node, + leaf +}; + +static QTreeWidgetItem* add_child(QTreeWidgetItem *parent, const QString& text, int column, kernel_item_type type) +{ + if (parent) + { + for (int i = 0; i < parent->childCount(); i++) + { + if (parent->child(i)->data(0, kernel_item_role::name_role).toString() == text) + { + return parent->child(i); + } + } + } + QTreeWidgetItem* item = gui::utils::add_child(parent, text, column); + if (item) + { + item->setData(0, kernel_item_role::name_role, text); + item->setData(0, kernel_item_role::type_role, type); + } + return item; +} + +static QTreeWidgetItem* add_leaf(QTreeWidgetItem *parent, const QString& text, int column = 0) +{ + return add_child(parent, text, column, kernel_item_type::leaf); +} + +static QTreeWidgetItem* add_node(u32 id, QTreeWidgetItem *parent, const QString& text, int column = 0) +{ + QTreeWidgetItem* node = add_child(parent, text, column, kernel_item_type::node); + if (node) + { + node->setData(0, kernel_item_role::id_role, id); + } + return node; +} + +static QTreeWidgetItem* find_first_node(QTreeWidget* tree, QTreeWidgetItem *parent, const QString& regexp) +{ + if (tree && parent) + { + for (auto item : tree->findItems(regexp, Qt::MatchFlag::MatchRegExp | Qt::MatchFlag::MatchRecursive)) + { + if (item->parent() == parent && item->data(0, kernel_item_role::type_role).toInt() != kernel_item_type::leaf) + { + return item; + } + } + } + return nullptr; +} + +static QTreeWidgetItem* find_node(QTreeWidget* tree, u32 id) +{ + if (tree) + { + for (auto item : tree->findItems(".*", Qt::MatchFlag::MatchRegExp | Qt::MatchFlag::MatchRecursive)) + { + if (item->data(0, kernel_item_role::type_role).toInt() == kernel_item_type::node && + item->data(0, kernel_item_role::id_role).toUInt() == id) + { + return item; + } + } + } + return nullptr; +} + +static QTreeWidgetItem* add_volatile_node(QTreeWidget* tree, QTreeWidgetItem *parent, const QString& base_text, const QString& text = "", int column = 0) +{ + QTreeWidgetItem* node = find_first_node(tree, parent, base_text + ".*"); + if (!node) + { + node = add_child(parent, base_text, column, kernel_item_type::volatile_node); + } + if (node) + { + node->setText(0, text.isEmpty() ? base_text : text); + } + return node; +} kernel_explorer::kernel_explorer(QWidget* parent) : QDialog(parent) { @@ -60,12 +163,8 @@ kernel_explorer::kernel_explorer(QWidget* parent) : QDialog(parent) Update(); } -constexpr auto qstr = QString::fromStdString; - void kernel_explorer::Update() { - m_tree->clear(); - const auto dct = g_fxo->get(); if (!dct) @@ -73,122 +172,171 @@ void kernel_explorer::Update() return; } + static const size_t additional_size = 6; + + const std::unordered_map tree_item_names = + { + { SYS_MEM_OBJECT , tr("Memory")}, + { SYS_MUTEX_OBJECT , tr("Mutexes")}, + { SYS_COND_OBJECT , tr("Condition Variables")}, + { SYS_RWLOCK_OBJECT , tr("Reader Writer Locks")}, + { SYS_INTR_TAG_OBJECT , tr("Interrupt Tags")}, + { SYS_INTR_SERVICE_HANDLE_OBJECT , tr("Interrupt Service Handles")}, + { SYS_EVENT_QUEUE_OBJECT , tr("Event Queues")}, + { SYS_EVENT_PORT_OBJECT , tr("Event Ports")}, + { SYS_TRACE_OBJECT , tr("Traces")}, + { SYS_SPUIMAGE_OBJECT , tr("SPU Images")}, + { SYS_PRX_OBJECT , tr("PRX Modules")}, + { SYS_SPUPORT_OBJECT , tr("SPU Ports")}, + { SYS_OVERLAY_OBJECT , tr("Overlay Modules")}, + { SYS_LWMUTEX_OBJECT , tr("Light Weight Mutexes")}, + { SYS_TIMER_OBJECT , tr("Timers")}, + { SYS_SEMAPHORE_OBJECT , tr("Semaphores")}, + { SYS_FS_FD_OBJECT , tr("File Descriptors ?")}, + { SYS_LWCOND_OBJECT , tr("Light Weight Condition Variables")}, + { SYS_EVENT_FLAG_OBJECT , tr("Event Flags")}, + + { memory_containers , tr("Memory Containers")}, + { ppu_threads , tr("PPU Threads")}, + { spu_threads , tr("SPU Threads")}, + { spu_thread_groups , tr("SPU Thread Groups")}, + { rsx_contexts , tr("RSX Contexts")}, + { file_descriptors , tr("File Descriptors")}, + }; + + QTreeWidgetItem* root = m_tree->topLevelItem(0); + if (!root) + { + root = new QTreeWidgetItem(); + root->setData(0, kernel_item_role::type_role, kernel_item_type::root); + m_tree->addTopLevelItem(root); + + for (const auto& [key, name] : tree_item_names) + { + add_node(key, root, name); + } + } + else + { + std::function clean_up_tree; + clean_up_tree = [&clean_up_tree](QTreeWidgetItem* item) + { + if (item && item->data(0, kernel_item_role::type_role).toInt() != kernel_item_type::leaf) + { + item->setText(0, item->data(0, kernel_item_role::name_role).toString()); + item->setData(0, kernel_item_role::expanded_role, item->isExpanded()); + + for (int i = item->childCount() - 1; i >= 0; i--) + { + switch (item->child(i)->data(0, kernel_item_role::type_role).toInt()) + { + case kernel_item_type::leaf: + { + delete item->takeChild(i); + break; + } + case kernel_item_type::volatile_node: + { + if (item->child(i)->childCount() <= 0) + { + // Cleanup volatile node if it has no children + delete item->takeChild(i); + break; + } + [[fallthrough]]; + } + case kernel_item_type::node: + case kernel_item_type::root: + default: + { + clean_up_tree(item->child(i)); + break; + } + } + } + } + }; + clean_up_tree(root); + } + const u32 total_memory_usage = dct->used; - QTreeWidgetItem* root = new QTreeWidgetItem(); root->setText(0, qstr(fmt::format("Process 0x%08x: Total Memory Usage: 0x%x/0x%x (%0.2f/%0.2f MB)", process_getpid(), total_memory_usage, dct->size, 1. * total_memory_usage / (1024 * 1024) , 1. * dct->size / (1024 * 1024)))); - m_tree->addTopLevelItem(root); // TODO: FileSystem - struct lv2_obj_rec - { - QTreeWidgetItem* node; - u32 count{ 0 }; - - lv2_obj_rec() = default; - lv2_obj_rec(QTreeWidgetItem* node) - : node(node) - { - } - }; - - auto l_addTreeChild = [this](QTreeWidgetItem *parent, QString text) - { - QTreeWidgetItem *treeItem = new QTreeWidgetItem(); - treeItem->setText(0, text); - parent->addChild(treeItem); - return treeItem; - }; - - std::vector lv2_types(256); - lv2_types[SYS_MEM_OBJECT] = l_addTreeChild(root, "Memory"); - lv2_types[SYS_MUTEX_OBJECT] = l_addTreeChild(root, "Mutexes"); - lv2_types[SYS_COND_OBJECT] = l_addTreeChild(root, "Condition Variables"); - lv2_types[SYS_RWLOCK_OBJECT] = l_addTreeChild(root, "Reader Writer Locks"); - lv2_types[SYS_INTR_TAG_OBJECT] = l_addTreeChild(root, "Interrupt Tags"); - lv2_types[SYS_INTR_SERVICE_HANDLE_OBJECT] = l_addTreeChild(root, "Interrupt Service Handles"); - lv2_types[SYS_EVENT_QUEUE_OBJECT] = l_addTreeChild(root, "Event Queues"); - lv2_types[SYS_EVENT_PORT_OBJECT] = l_addTreeChild(root, "Event Ports"); - lv2_types[SYS_TRACE_OBJECT] = l_addTreeChild(root, "Traces"); - lv2_types[SYS_SPUIMAGE_OBJECT] = l_addTreeChild(root, "SPU Images"); - lv2_types[SYS_PRX_OBJECT] = l_addTreeChild(root, "PRX Modules"); - lv2_types[SYS_SPUPORT_OBJECT] = l_addTreeChild(root, "SPU Ports"); - lv2_types[SYS_OVERLAY_OBJECT] = l_addTreeChild(root, "Overlay Modules"); - lv2_types[SYS_LWMUTEX_OBJECT] = l_addTreeChild(root, "Light Weight Mutexes"); - lv2_types[SYS_TIMER_OBJECT] = l_addTreeChild(root, "Timers"); - lv2_types[SYS_SEMAPHORE_OBJECT] = l_addTreeChild(root, "Semaphores"); - lv2_types[SYS_LWCOND_OBJECT] = l_addTreeChild(root, "Light Weight Condition Variables"); - lv2_types[SYS_EVENT_FLAG_OBJECT] = l_addTreeChild(root, "Event Flags"); - idm::select([&](u32 id, lv2_obj& obj) { - lv2_types[id >> 24].count++; + auto node = find_node(m_tree, id >> 24); + if (!node) + { + return; + } - if (auto& node = lv2_types[id >> 24].node) switch (id >> 24) + switch (id >> 24) { case SYS_MEM_OBJECT: { auto& mem = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Memory 0x%08x: Size: 0x%x (%0.2f MB), Granularity: %s, Mappings: %u", id, mem.size, mem.size * 1. / (1024 * 1024), mem.align == 0x10000u ? "64K" : "1MB", +mem.counter))); + add_leaf(node, qstr(fmt::format("Memory 0x%08x: Size: 0x%x (%0.2f MB), Granularity: %s, Mappings: %u", id, mem.size, mem.size * 1. / (1024 * 1024), mem.align == 0x10000u ? "64K" : "1MB", +mem.counter))); break; } case SYS_MUTEX_OBJECT: { auto& mutex = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Mutex 0x%08x: “%s”,%s Owner: %#x, Locks: %u, Key: %#llx, Conds: %u, Wq: %zu", id, lv2_obj::name64(mutex.name), + add_leaf(node, qstr(fmt::format(u8"Mutex 0x%08x: “%s”,%s Owner: %#x, Locks: %u, Key: %#llx, Conds: %u, Wq: %zu", id, lv2_obj::name64(mutex.name), mutex.recursive == SYS_SYNC_RECURSIVE ? " Recursive," : "", mutex.owner >> 1, +mutex.lock_count, mutex.key, +mutex.cond_count, mutex.sq.size()))); break; } case SYS_COND_OBJECT: { auto& cond = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Cond 0x%08x: “%s”, Key: %#llx, Wq: %u", id, lv2_obj::name64(cond.name), cond.key, +cond.waiters))); + add_leaf(node, qstr(fmt::format(u8"Cond 0x%08x: “%s”, Key: %#llx, Wq: %u", id, lv2_obj::name64(cond.name), cond.key, +cond.waiters))); break; } case SYS_RWLOCK_OBJECT: { auto& rw = static_cast(obj); const s64 val = rw.owner; - l_addTreeChild(node, qstr(fmt::format(u8"RW Lock 0x%08x: “%s”, Owner: %#x(%d), Key: %#llx, Rq: %zu, Wq: %zu", id, lv2_obj::name64(rw.name), + add_leaf(node, qstr(fmt::format(u8"RW Lock 0x%08x: “%s”, Owner: %#x(%d), Key: %#llx, Rq: %zu, Wq: %zu", id, lv2_obj::name64(rw.name), std::max(0, val >> 1), -std::min(0, val >> 1), rw.key, rw.rq.size(), rw.wq.size()))); break; } case SYS_INTR_TAG_OBJECT: { // auto& tag = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Intr Tag 0x%08x", id))); + add_leaf(node, qstr(fmt::format("Intr Tag 0x%08x", id))); break; } case SYS_INTR_SERVICE_HANDLE_OBJECT: { // auto& serv = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Intr Svc 0x%08x", id))); + add_leaf(node, qstr(fmt::format("Intr Svc 0x%08x", id))); break; } case SYS_EVENT_QUEUE_OBJECT: { auto& eq = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Event Queue 0x%08x: “%s”, %s, Key: %#llx, Events: %zu/%d, Wq: %zu", id, lv2_obj::name64(eq.name), + add_leaf(node, qstr(fmt::format(u8"Event Queue 0x%08x: “%s”, %s, Key: %#llx, Events: %zu/%d, Wq: %zu", id, lv2_obj::name64(eq.name), eq.type == SYS_SPU_QUEUE ? "SPU" : "PPU", eq.key, eq.events.size(), eq.size, eq.sq.size()))); break; } case SYS_EVENT_PORT_OBJECT: { auto& ep = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Event Port 0x%08x: Name: %#llx, Bound: %s", id, ep.name, lv2_event_queue::check(ep.queue)))); + add_leaf(node, qstr(fmt::format("Event Port 0x%08x: Name: %#llx, Bound: %s", id, ep.name, lv2_event_queue::check(ep.queue)))); break; } case SYS_TRACE_OBJECT: { - l_addTreeChild(node, qstr(fmt::format("Trace 0x%08x", id))); + add_leaf(node, qstr(fmt::format("Trace 0x%08x", id))); break; } case SYS_SPUIMAGE_OBJECT: { auto& spi = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("SPU Image 0x%08x, Entry: 0x%x, Segs: *0x%x, Num Segs: %d", id, spi.e_entry, spi.segs, spi.nsegs))); + add_leaf(node, qstr(fmt::format("SPU Image 0x%08x, Entry: 0x%x, Segs: *0x%x, Num Segs: %d", id, spi.e_entry, spi.segs, spi.nsegs))); break; } case SYS_PRX_OBJECT: @@ -198,23 +346,23 @@ void kernel_explorer::Update() if (!addr0) { - l_addTreeChild(node, qstr(fmt::format("PRX 0x%08x: '%s' (HLE)", id, prx.name))); + add_leaf(node, qstr(fmt::format("PRX 0x%08x: '%s' (HLE)", id, prx.name))); break; } const u32 end0 = addr0 + prx.segs[0].size - 1; - l_addTreeChild(node, qstr(fmt::format("PRX 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, prx.name, addr0, end0))); + add_leaf(node, qstr(fmt::format("PRX 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, prx.name, addr0, end0))); break; } case SYS_SPUPORT_OBJECT: { - l_addTreeChild(node, qstr(fmt::format("SPU Port 0x%08x", id))); + add_leaf(node, qstr(fmt::format("SPU Port 0x%08x", id))); break; } case SYS_OVERLAY_OBJECT: { auto& ovl = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("OVL 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, ovl.name, ovl.segs[0].addr, ovl.segs[0].addr + ovl.segs[0].size - 1))); + add_leaf(node, qstr(fmt::format("OVL 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, ovl.name, ovl.segs[0].addr, ovl.segs[0].addr + ovl.segs[0].size - 1))); break; } case SYS_LWMUTEX_OBJECT: @@ -243,74 +391,63 @@ void kernel_explorer::Update() } else { - l_addTreeChild(node, qstr(fmt::format(u8"LWMutex 0x%08x: “%s”, Wq: %zu (Couldn't extract control data)", id, lv2_obj::name64(lwm.name), lwm.sq.size()))); + add_leaf(node, qstr(fmt::format(u8"LWMutex 0x%08x: “%s”, Wq: %zu (Couldn't extract control data)", id, lv2_obj::name64(lwm.name), lwm.sq.size()))); break; } - l_addTreeChild(node, qstr(fmt::format(u8"LWMutex 0x%08x: “%s”,%s Owner: %s, Locks: %u, Wq: %zu", id, lv2_obj::name64(lwm.name), + add_leaf(node, qstr(fmt::format(u8"LWMutex 0x%08x: “%s”,%s Owner: %s, Locks: %u, Wq: %zu", id, lv2_obj::name64(lwm.name), (lwm_data.attribute & SYS_SYNC_RECURSIVE) ? " Recursive," : "", owner_str, lwm_data.recursive_count, lwm.sq.size()))); break; } case SYS_TIMER_OBJECT: { // auto& timer = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Timer 0x%08x", id))); + add_leaf(node, qstr(fmt::format("Timer 0x%08x", id))); break; } case SYS_SEMAPHORE_OBJECT: { auto& sema = static_cast(obj); const auto val = +sema.val; - l_addTreeChild(node, qstr(fmt::format(u8"Sema 0x%08x: “%s”, Count: %d/%d, Wq: %zu", id, lv2_obj::name64(sema.name), + add_leaf(node, qstr(fmt::format(u8"Sema 0x%08x: “%s”, Count: %d/%d, Wq: %zu", id, lv2_obj::name64(sema.name), std::max(val, 0), sema.max, -std::min(val, 0)))); break; } case SYS_LWCOND_OBJECT: { auto& lwc = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"LWCond 0x%08x: “%s”, Wq: %zu", id, lv2_obj::name64(lwc.name), +lwc.waiters))); + add_leaf(node, qstr(fmt::format(u8"LWCond 0x%08x: “%s”, Wq: %zu", id, lv2_obj::name64(lwc.name), +lwc.waiters))); break; } case SYS_EVENT_FLAG_OBJECT: { auto& ef = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Event Flag 0x%08x: “%s”, Type: 0x%x, Key: %#llx, Pattern: 0x%llx, Wq: %zu", id, lv2_obj::name64(ef.name), + add_leaf(node, qstr(fmt::format(u8"Event Flag 0x%08x: “%s”, Type: 0x%x, Key: %#llx, Pattern: 0x%llx, Wq: %zu", id, lv2_obj::name64(ef.name), ef.type, ef.key, ef.pattern.load(), +ef.waiters))); break; } default: { - l_addTreeChild(node, qstr(fmt::format("Unknown object 0x%08x", id))); + add_leaf(node, qstr(fmt::format("Unknown object 0x%08x", id))); } } }); - lv2_types.emplace_back(l_addTreeChild(root, "Memory Containers")); - idm::select([&](u32 id, lv2_memory_container& container) { - lv2_types.back().count++; const u32 used = container.used; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format("Memory Container 0x%08x: Used: 0x%x/0x%x (%0.2f/%0.2f MB)", id, used, container.size, used * 1. / (1024 * 1024), container.size * 1. / (1024 * 1024)))); + add_leaf(find_node(m_tree, additional_nodes::memory_containers), qstr(fmt::format("Memory Container 0x%08x: Used: 0x%x/0x%x (%0.2f/%0.2f MB)", id, used, container.size, used * 1. / (1024 * 1024), container.size * 1. / (1024 * 1024)))); }); - lv2_types.emplace_back(l_addTreeChild(root, "PPU Threads")); - idm::select>([&](u32 id, ppu_thread& ppu) { - lv2_types.back().count++; - const auto func = ppu.last_function; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"PPU 0x%07x: “%s”, PRIO: %d, Joiner: %s, State: %s, %s func: “%s”", id, *ppu.ppu_tname.load(), +ppu.prio, ppu.joiner.load(), ppu.state.load() + add_leaf(find_node(m_tree, additional_nodes::ppu_threads), qstr(fmt::format(u8"PPU 0x%07x: “%s”, PRIO: %d, Joiner: %s, State: %s, %s func: “%s”", id, *ppu.ppu_tname.load(), +ppu.prio, ppu.joiner.load(), ppu.state.load() , ppu.current_function ? "In" : "Last", func ? func : ""))); }); - lv2_types.emplace_back(l_addTreeChild(root, "SPU Threads")); - idm::select>([&](u32 /*id*/, spu_thread& spu) { - lv2_types.back().count++; - std::string_view type = "threaded"; if (spu.is_isolated) @@ -322,18 +459,15 @@ void kernel_explorer::Update() type = "raw"; } - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"SPU 0x%07x: “%s”, State: %s, Type: %s", spu.lv2_id, *spu.spu_tname.load(), spu.state.load(), type))); + add_leaf(find_node(m_tree, additional_nodes::spu_threads), qstr(fmt::format(u8"SPU 0x%07x: “%s”, State: %s, Type: %s", spu.lv2_id, *spu.spu_tname.load(), spu.state.load(), type))); }); - lv2_types.emplace_back(l_addTreeChild(root, "SPU Thread Groups")); - idm::select([&](u32 id, lv2_spu_group& tg) { - lv2_types.back().count++; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"SPU Group 0x%07x: “%s”, Status = %s, Priority = %d, Type = 0x%x", id, tg.name, tg.run_state.load(), +tg.prio, tg.type))); + add_leaf(find_node(m_tree, additional_nodes::spu_thread_groups), qstr(fmt::format(u8"SPU Group 0x%07x: “%s”, Status = %s, Priority = %d, Type = 0x%x", id, tg.name, tg.run_state.load(), +tg.prio, tg.type))); }); - lv2_types.emplace_back(l_addTreeChild(root, "RSX Contexts")); + QTreeWidgetItem* rsx_context_node = find_node(m_tree, additional_nodes::rsx_contexts); do { @@ -353,12 +487,13 @@ void kernel_explorer::Update() break; } - lv2_types.back().count++; - const auto tree = l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"RSX Context 0x55555555, Local Size: %u MB, Base Addr: 0x%x, Device Addr: 0x%x", context_info->memory_size >> 20, base, context_info->device_addr))); + const QString branch_name = "RSX Context 0x55555555"; + QTreeWidgetItem* rsx_tree = add_volatile_node(m_tree, rsx_context_node, branch_name, + branch_name + qstr(fmt::format(u8", Local Size: %u MB, Base Addr: 0x%x, Device Addr: 0x%x", context_info->memory_size >> 20, base, context_info->device_addr))); - lv2_obj_rec io_tree = l_addTreeChild(tree, "IO-EA Table"); - lv2_obj_rec zc_tree = l_addTreeChild(tree, "Zcull Bindings"); - lv2_obj_rec db_tree = l_addTreeChild(tree, "Display Buffers"); + QTreeWidgetItem* io_tree = add_volatile_node(m_tree, rsx_tree, tr("IO-EA Table")); + QTreeWidgetItem* zc_tree = add_volatile_node(m_tree, rsx_tree, tr("Zcull Bindings")); + QTreeWidgetItem* db_tree = add_volatile_node(m_tree, rsx_tree, tr("Display Buffers")); decltype(rsx->iomap_table) table; decltype(rsx->display_buffers) dbs; @@ -379,8 +514,7 @@ void kernel_explorer::Update() if (size_block) { // Print block - l_addTreeChild(io_tree.node, qstr(fmt::format("IO: %08x, EA: %08x, Size: %uMB", first_io, first_ea, size_block))); - io_tree.count++; + add_leaf(io_tree, qstr(fmt::format("IO: %08x, EA: %08x, Size: %uMB", first_io, first_ea, size_block))); } if (i >= 512u) @@ -406,11 +540,10 @@ void kernel_explorer::Update() else { // Print last block before we continue to a new one - l_addTreeChild(io_tree.node, qstr(fmt::format("IO: %08x, EA: %08x, Size: %uMB", first_io, first_ea, old_block_size))); + add_leaf(io_tree, qstr(fmt::format("IO: %08x, EA: %08x, Size: %uMB", first_io, first_ea, old_block_size))); size_block = 1; first_ea = addr; first_io = (i - 1) << 20; - io_tree.count++; continue; } } @@ -425,9 +558,8 @@ void kernel_explorer::Update() { if (zc.bound) { - l_addTreeChild(zc_tree.node, qstr(fmt::format("O: %07x, W: %u, H: %u, Zformat: 0x%x, AAformat: 0x%x, Dir: 0x%x, sFunc: 0x%x, sRef: %02x, sMask: %02x" + add_leaf(zc_tree, qstr(fmt::format("O: %07x, W: %u, H: %u, Zformat: 0x%x, AAformat: 0x%x, Dir: 0x%x, sFunc: 0x%x, sRef: %02x, sMask: %02x" , zc.offset, zc.height, zc.width, zc.zFormat, zc.aaFormat, zc.zcullDir, zc.sFunc, zc.sRef, zc.sMask))); - zc_tree.count++; } } @@ -435,57 +567,74 @@ void kernel_explorer::Update() { if (db.valid()) { - l_addTreeChild(db_tree.node, qstr(fmt::format("Offset: %07x, Width: %u, Height: %u, Pitch: %u" + add_leaf(db_tree, qstr(fmt::format("Offset: %07x, Width: %u, Height: %u, Pitch: %u" , db.offset, db.height, db.width, db.pitch))); - db_tree.count++; - } - } - - for (auto&& entry : {&io_tree, &zc_tree, &db_tree}) - { - if (entry->node) - { - if (entry->count) - { - // Append object count - entry->node->setText(0, entry->node->text(0) + qstr(fmt::format(" (%zu)", entry->count))); - } - else - { - // Delete node otherwise - delete entry->node; - } } } } while (0); - lv2_types.emplace_back(l_addTreeChild(root, "File Descriptors")); - idm::select([&](u32 id, lv2_fs_object& fo) { - lv2_types.back().count++; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"FD %u: “%s”", id, fo.name.data()))); + add_leaf(find_node(m_tree, additional_nodes::file_descriptors), qstr(fmt::format(u8"FD %u: “%s”", id, fo.name.data()))); }); - for (auto&& entry : lv2_types) - { - if (entry.node) - { - if (entry.count) - { - // Append object count - entry.node->setText(0, entry.node->text(0) + qstr(fmt::format(" (%zu)", entry.count))); - } - else - { - // Delete node otherwise - delete entry.node; - } - } - } - // RawSPU Threads (TODO) + std::function final_touches; + final_touches = [&final_touches](QTreeWidgetItem* item) -> int + { + int visible_children = 0; + + for (int i = 0; i < item->childCount(); i++) + { + auto node = item->child(i); + + if (!node) + { + continue; + } + + switch (node->data(0, kernel_item_role::type_role).toInt()) + { + case kernel_item_type::leaf: + { + node->setHidden(false); + break; + } + case kernel_item_type::node: + case kernel_item_type::volatile_node: + { + const int count = final_touches(node); + + if (count > 0) + { + // Append count + node->setText(0, node->text(0) + qstr(fmt::format(" (%zu)", count))); + + // Expand if necessary + node->setExpanded(node->data(0, kernel_item_role::expanded_role).toBool()); + } + + // Hide node if it has no children + node->setHidden(count <= 0); + break; + } + case kernel_item_type::root: + default: + { + break; + } + } + + if (!node->isHidden()) + { + visible_children++; + } + } + + return visible_children; + }; + final_touches(root); root->setExpanded(true); } diff --git a/rpcs3/rpcs3qt/kernel_explorer.h b/rpcs3/rpcs3qt/kernel_explorer.h index 6523d2a9e4..a607409381 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.h +++ b/rpcs3/rpcs3qt/kernel_explorer.h @@ -3,14 +3,30 @@ #include #include +#include "Utilities/types.h" + class kernel_explorer : public QDialog { Q_OBJECT - QTreeWidget* m_tree; + static const size_t sys_size = 256; + + enum additional_nodes + { + memory_containers = sys_size + 0, + ppu_threads = sys_size + 1, + spu_threads = sys_size + 2, + spu_thread_groups = sys_size + 3, + rsx_contexts = sys_size + 4, + file_descriptors = sys_size + 5, + }; public: kernel_explorer(QWidget* parent); + +private: + QTreeWidget* m_tree; + private Q_SLOTS: void Update(); }; diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index a05c3d03dc..9b6203b023 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -313,5 +313,28 @@ namespace gui { open_dir(sstr(path)); } + + QTreeWidgetItem* add_child(QTreeWidgetItem *parent, const QString& text, int column) + { + if (parent) + { + QTreeWidgetItem *tree_item = new QTreeWidgetItem(); + tree_item->setText(0, text); + parent->addChild(tree_item); + return tree_item; + } + return nullptr; + }; + + void remove_children(QTreeWidgetItem* parent) + { + if (parent) + { + for (int i = 0; i < parent->childCount(); i++) + { + parent->removeChild(parent->child(i)); + } + } + } } // utils } // gui diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index e65543ed61..bd64a5aff9 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace gui { @@ -65,5 +66,11 @@ namespace gui // Open a path in the explorer and mark the file void open_dir(const QString& path); + + // Constructs and adds a child to a QTreeWidgetItem + QTreeWidgetItem* add_child(QTreeWidgetItem* parent, const QString& text, int column = 0); + + // Removes all children of a QTreeWidgetItem + void remove_children(QTreeWidgetItem* parent); } // utils } // gui