diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 139f6b97ca..f3d810878e 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -740,11 +740,7 @@ public: std::shared_ptr spup[64]; // SPU Ports spu_channel exit_status{}; // Threaded SPU exit status (not a channel, but the interface fits) atomic_t last_exit_status; // Value to be written in exit_status after checking group termination - -private: - lv2_spu_group* const group; // SPU Thread Group (only safe to access in the spu thread itself) -public: - + lv2_spu_group* const group; // SPU Thread Group (access by the spu threads in the group only! From other threads obtain a shared pointer to group using group ID) const u32 index; // SPU index std::shared_ptr shm; // SPU memory const std::add_pointer_t ls; // SPU LS pointer diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 90fcc0a7f7..5b3721d345 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -319,7 +319,7 @@ void kernel_explorer::Update() if (mem.pshared) { - add_leaf(node, qstr(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Granularity: %s, Mappings: %u Key: %#llx", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", +mem.counter, mem.key))); + add_leaf(node, qstr(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Granularity: %s, Mappings: %u, Key: %#llx", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", +mem.counter, mem.key))); break; } @@ -541,7 +541,58 @@ void kernel_explorer::Update() idm::select>([&](u32 /*id*/, spu_thread& spu) { - 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(), spu.get_type()))); + QTreeWidgetItem* spu_thread_tree = add_solid_node(m_tree, 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(), spu.get_type()))); + + if (spu.get_type() == spu_type::threaded) + { + reader_lock lock(spu.group->mutex); + + bool has_connected_ports = false; + const auto first_spu = spu.group->threads[0].get(); + + // Always show information of the first thread in group + // Or if information differs from that thread + if (&spu == first_spu || std::any_of(std::begin(spu.spup), std::end(spu.spup), [&](const auto& port) + { + // Flag to avoid reporting information if no SPU ports are connected + has_connected_ports |= lv2_obj::check(port); + + // Check if ports do not match with the first thread + return port != first_spu->spup[&port - spu.spup]; + })) + { + for (const auto& port : spu.spup) + { + if (lv2_obj::check(port)) + { + add_leaf(spu_thread_tree, qstr(fmt::format("SPU Port %u: Queue ID: 0x%08x", &port - spu.spup, port->id))); + } + } + } + else if (has_connected_ports) + { + // Avoid duplication of information between threads which is common + add_leaf(spu_thread_tree, qstr(fmt::format("SPU Ports: As SPU 0x%07x", first_spu->lv2_id))); + } + + for (const auto& [key, queue] : spu.spuq) + { + if (lv2_obj::check(queue)) + { + add_leaf(spu_thread_tree, qstr(fmt::format("SPU Queue: Queue ID: 0x%08x, Key: 0x%x", queue->id, key))); + } + } + } + else + { + for (const auto& ctrl : spu.int_ctrl) + { + if (lv2_obj::check(ctrl.tag)) + { + add_leaf(spu_thread_tree, qstr(fmt::format("Interrupt Tag %u: ID: 0x%x, Mask: 0x%x, Status: 0x%x", &ctrl - spu.int_ctrl.data(), ctrl.tag->id, +ctrl.mask, +ctrl.stat))); + } + } + } }); idm::select([&](u32 id, lv2_spu_group& tg)