diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index d858735edf3f..d0638982cba4 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -867,9 +867,9 @@ bool cpu_thread::check_state() noexcept store = true; } - if (flags & cpu_flag::notify) + if (flags & (cpu_flag::notify + cpu_flag::measure)) { - flags -= cpu_flag::notify; + flags -= cpu_flag::notify + cpu_flag::measure; store = true; } diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 5e3484f7f51c..aae9895fec66 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -28,6 +28,7 @@ enum class cpu_flag : u32 notify, // Flag meant solely to allow atomic notification on state without changing other flags yield, // Thread is being requested to yield its execution time if it's running preempt, // Thread is being requested to preempt the execution of all CPU threads + measure, // Thread must remove the flag as soon as possible, no side effects dbg_global_pause, // Emulation paused dbg_pause, // Thread paused diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 6fd923ad5174..096f9330f44f 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -1139,8 +1139,10 @@ error_code cellCameraGetBufferInfo(s32 dev_num, vm::ptr info) return CELL_OK; } -error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info) +error_code cellCameraGetBufferInfoEx(ppu_thread& ppu, s32 dev_num, vm::ptr info) { + ppu.state += cpu_flag::wait; + cellCamera.notice("cellCameraGetBufferInfoEx(dev_num=%d, info=0x%x)", dev_num, info); // calls cellCameraGetBufferInfo @@ -1151,10 +1153,16 @@ error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info } auto& g_camera = g_fxo->get(); - std::lock_guard lock(g_camera.mutex); - *info = g_camera.info; + CellCameraInfoEx info_out; + + { + std::lock_guard lock(g_camera.mutex); + + info_out = g_camera.info; + } + *info = info_out; return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index c50c93fc4412..7f02a0eabefe 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -2284,6 +2284,8 @@ error_code cellGemClearStatusFlags(u32 gem_num, u64 mask) error_code cellGemConvertVideoFinish(ppu_thread& ppu) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemConvertVideoFinish()"); auto& gem = g_fxo->get(); @@ -2306,8 +2308,10 @@ error_code cellGemConvertVideoFinish(ppu_thread& ppu) return CELL_OK; } -error_code cellGemConvertVideoStart(vm::cptr video_frame) +error_code cellGemConvertVideoStart(ppu_thread& ppu, vm::cptr video_frame) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemConvertVideoStart(video_frame=*0x%x)", video_frame); auto& gem = g_fxo->get(); @@ -2459,6 +2463,8 @@ error_code cellGemEnableMagnetometer2(u32 gem_num, u32 enable) error_code cellGemEnd(ppu_thread& ppu) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemEnd()"); auto& gem = g_fxo->get(); @@ -3263,7 +3269,7 @@ error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality) extern error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm::ptr arg2); extern error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2); - extern error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info); + extern error_code cellCameraGetBufferInfoEx(ppu_thread&, s32 dev_num, vm::ptr info); vm::var info = vm::make_var({}); vm::var arg1 = vm::make_var({}); @@ -3271,7 +3277,7 @@ error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality) cellCameraGetAttribute(0, 0x3e6, arg1, arg2); cellCameraSetAttribute(0, 0x3e6, 0x3e, *arg2 | 0x80); - cellCameraGetBufferInfoEx(0, info); + cellCameraGetBufferInfoEx(*cpu_thread::get_current(), 0, info); if (info->width == 640) { @@ -3603,6 +3609,8 @@ error_code cellGemTrackHues(vm::cptr req_hues, vm::ptr res_hues) error_code cellGemUpdateFinish(ppu_thread& ppu) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemUpdateFinish()"); auto& gem = g_fxo->get(); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index d5ad515a1abe..c4f1cfadf40a 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -425,6 +425,28 @@ extern void mov_rdata_nt(spu_rdata_t& _dst, const spu_rdata_t& _src) #endif } +static inline u32 cmp16_rdata(const decltype(spu_thread::rdata)& rdata, const decltype(spu_thread::rdata)& to_write) +{ + u32 diffcnt = 0; + u32 pos = 0; + + for (u32 i = 0; i < 8; i++) + { + if (std::memcmp(rdata + (i * 16), to_write + (i * 16), 16)) + { + diffcnt++; + pos = i; + } + }; + + if (diffcnt == 1) + { + return pos; + } + + return -1; +} + #if defined(_MSC_VER) #define mwaitx_func #define waitpkg_func @@ -3969,17 +3991,52 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) auto& super_data = *vm::get_super_ptr(addr); const bool success = [&]() { - // Full lock (heavyweight) - // TODO: vm::check_addr - vm::writer_lock lock(addr, range_lock); - - if (cmp_rdata(rdata, super_data)) + if (int pos = cmp16_rdata(rdata, to_write); addr != spurs_addr && pos != -1) { - mov_rdata(super_data, to_write); - return true; + auto& bits = *utils::bless>(vm::g_reservations + ((addr & 0xff80) / 2 + 32)); + const auto bits_val = +bits; + + // Full lock (heavyweight) + // TODO: vm::check_addr + const bool halt_ppus = bits_val == umax || bits_val < 0x100000; + + if (vm::writer_lock lock(addr, range_lock, halt_ppus); cmp_rdata(rdata, super_data)) + { + auto cast_as = [](void* ptr, usz pos){ return reinterpret_cast(ptr) + pos; }; + auto cast_as_const = [](const void* ptr, usz pos){ return reinterpret_cast(ptr) + pos; }; + + if (halt_ppus) + { + *cast_as(super_data, pos) = *cast_as_const(to_write, pos); + bits.try_inc(u64{umax}); + return true; + } + else if (atomic_storage::compare_exchange(*cast_as(super_data, pos), *cast_as(rdata, pos), *cast_as_const(to_write, pos))) + { + bits.try_inc(u64{umax}); + *cast_as(rdata, pos) = *cast_as_const(to_write, pos); + ensure(cmp_rdata(rdata, super_data)); + return true; + } + } + + bits = u64{umax}; + return false; } + else + { + // Full lock (heavyweight) + // TODO: vm::check_addr + vm::writer_lock lock(addr, range_lock); - return false; + if (cmp_rdata(rdata, super_data)) + { + mov_rdata(super_data, to_write); + return true; + } + + return false; + } }(); res += success ? 64 : 0 - 64; @@ -4773,7 +4830,7 @@ bool spu_thread::process_mfc_cmd() if (raddr != addr) { // Last check for event before we replace the reservation with a new one - if (reservation_check(raddr, rdata)) + if (~ch_events.load().events & SPU_EVENT_LR && reservation_check(raddr, rdata, addr)) { set_events(SPU_EVENT_LR); } @@ -5483,7 +5540,7 @@ bool spu_thread::process_mfc_cmd() ch_mfc_cmd.cmd, ch_mfc_cmd.lsa, ch_mfc_cmd.eal, ch_mfc_cmd.tag, ch_mfc_cmd.size); } -bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const +bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data, u32 current_eal) const { if (!addr) { @@ -5502,9 +5559,24 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const return !cmp_rdata(data, *vm::get_super_ptr(addr)); } + if ((addr >> 20) == (current_eal >> 20)) + { + if (vm::check_addr(addr, vm::page_1m_size)) + { + // Same random-access-memory page as the current MFC command, assume allocated + return !cmp_rdata(data, vm::_ref(addr)); + } + + if ((addr >> 16) == (current_eal >> 16) && vm::check_addr(addr, vm::page_64k_size)) + { + // Same random-access-memory page as the current MFC command, assume allocated + return !cmp_rdata(data, vm::_ref(addr)); + } + } + // Ensure data is allocated (HACK: would raise LR event if not) // Set range_lock first optimistically - range_lock->store(u64{128} << 32 | addr); + range_lock->store(u64{128} << 32 | addr | vm::range_readable); u64 lock_val = *std::prev(std::end(vm::g_range_lock_set)); u64 old_lock = 0; @@ -5580,12 +5652,12 @@ bool spu_thread::reservation_check(u32 addr, u32 hash, atomic_t* range_ if ((addr >> 28) < 2 || (addr >> 28) == 0xd) { // Always-allocated memory does not need strict checking (vm::main or vm::stack) - return compute_rdata_hash32(*vm::get_super_ptr(addr)) == hash; + return compute_rdata_hash32(*vm::get_super_ptr(addr)) != hash; } // Ensure data is allocated (HACK: would raise LR event if not) // Set range_lock first optimistically - range_lock->store(u64{128} << 32 | addr); + range_lock->store(u64{128} << 32 | addr | vm::range_readable); u64 lock_val = *std::prev(std::end(vm::g_range_lock_set)); u64 old_lock = 0; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 36d24ff55e9a..9adb15a47cfd 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -901,7 +901,8 @@ class spu_thread : public cpu_thread // Returns true if reservation existed but was just discovered to be lost // It is safe to use on any address, even if not directly accessed by SPU (so it's slower) - bool reservation_check(u32 addr, const decltype(rdata)& data) const; + // Optionally pass a known allocated address for internal optimization (the current Effective-Address of the MFC command) + bool reservation_check(u32 addr, const decltype(rdata)& data, u32 current_eal = 0) const; static bool reservation_check(u32 addr, u32 hash, atomic_t* range_lock); usz register_cache_line_waiter(u32 addr); void deregister_cache_line_waiter(usz index); diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index 9f726e994d7e..d48df8f7d549 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -5,6 +5,7 @@ #include "Emu/CPU/CPUThread.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/SPUThread.h" +#include "Emu/Cell/PPUThread.h" #include "Emu/IdManager.h" #include "util/asm.hpp" @@ -213,6 +214,8 @@ error_code sys_memory_allocate_from_container(cpu_thread& cpu, u64 size, u32 cid if (alloc_addr) { + sys_memory.notice("sys_memory_allocate_from_container(): Allocated 0x%x address (size=0x%x)", addr, size); + vm::lock_sudo(addr, static_cast(size)); cpu.check_state(); *alloc_addr = addr; @@ -247,17 +250,37 @@ error_code sys_memory_free(cpu_thread& cpu, u32 addr) return CELL_OK; } -error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptr attr) +error_code sys_memory_get_page_attribute(ppu_thread& ppu, u32 addr, vm::ptr attr) { - cpu.state += cpu_flag::wait; + ppu.state += cpu_flag::wait; sys_memory.trace("sys_memory_get_page_attribute(addr=0x%x, attr=*0x%x)", addr, attr); - vm::writer_lock rlock; + if ((addr >> 28) == (ppu.stack_addr >> 28)) + { + // Stack address: fast path + if (!(addr >= ppu.stack_addr && addr < ppu.stack_addr + ppu.stack_size) && !vm::check_addr(addr)) + { + return { CELL_EINVAL, addr }; + } + + if (!vm::check_addr(attr.addr(), vm::page_readable, attr.size())) + { + return CELL_EFAULT; + } + + attr->attribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITEs + attr->access_right = SYS_MEMORY_ACCESS_RIGHT_PPU_THR; + attr->page_size = 4096; + attr->pad = 0; // Always write 0 + return CELL_OK; + } + + const auto [ok, vm_flags] = vm::get_addr_flags(addr); - if (!vm::check_addr(addr) || addr >= SPU_FAKE_BASE_ADDR) + if (!ok || addr >= SPU_FAKE_BASE_ADDR) { - return CELL_EINVAL; + return { CELL_EINVAL, addr }; } if (!vm::check_addr(attr.addr(), vm::page_readable, attr.size())) @@ -266,19 +289,20 @@ error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptrattribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITE (TODO) - attr->access_right = addr >> 28 == 0xdu ? SYS_MEMORY_ACCESS_RIGHT_PPU_THR : SYS_MEMORY_ACCESS_RIGHT_ANY;// (TODO) + attr->access_right = SYS_MEMORY_ACCESS_RIGHT_ANY; // TODO: Report accurately - if (vm::check_addr(addr, vm::page_1m_size)) + if (vm_flags & vm::page_1m_size) { attr->page_size = 0x100000; } - else if (vm::check_addr(addr, vm::page_64k_size)) + else if (vm_flags & vm::page_64k_size) { attr->page_size = 0x10000; } else { - attr->page_size = 4096; + //attr->page_size = 4096; + fmt::throw_exception("Unreachable"); } attr->pad = 0; // Always write 0 diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.h b/rpcs3/Emu/Cell/lv2/sys_memory.h index c2ca046bccf0..2702c45ac290 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.h +++ b/rpcs3/Emu/Cell/lv2/sys_memory.h @@ -4,6 +4,7 @@ #include "Emu/Cell/ErrorCodes.h" class cpu_thread; +class ppu_thread; enum lv2_mem_container_id : u32 { @@ -131,7 +132,7 @@ struct sys_memory_user_memory_stat_t error_code sys_memory_allocate(cpu_thread& cpu, u64 size, u64 flags, vm::ptr alloc_addr); error_code sys_memory_allocate_from_container(cpu_thread& cpu, u64 size, u32 cid, u64 flags, vm::ptr alloc_addr); error_code sys_memory_free(cpu_thread& cpu, u32 start_addr); -error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptr attr); +error_code sys_memory_get_page_attribute(ppu_thread& cpu, u32 addr, vm::ptr attr); error_code sys_memory_get_user_memory_size(cpu_thread& cpu, vm::ptr mem_info); error_code sys_memory_get_user_memory_stat(cpu_thread& cpu, vm::ptr mem_stat); error_code sys_memory_container_create(cpu_thread& cpu, vm::ptr cid, u64 size); diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 4ef4e04d85f0..0a980e712bc2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -414,12 +414,56 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea sys_rsx.warning("sys_rsx_context_iomap(): RSX is not idle while mapping io"); } + constexpr u32 _1m = 1u << 20; + + std::unique_lock fast_lock(render->sys_rsx_mtx, std::defer_lock); + + // Fast path for when mapping is the same + for (u32 attempts = 0;; attempts++) + { + if (attempts == 2) + { + // Nothing to do + return CELL_OK; + } + + if (attempts == 1 && size >= _1m * 2) + { + // Confirm mapped the same by rechecking with mutex + fast_lock.lock(); + } + + for (u32 i = 0; i < size; i += _1m) + { + const auto& table = render->iomap_table; + + const u32 prev_ea = table.ea[(io + i) / _1m]; + const u32 prev_io = table.io[(ea + i) / _1m]; + + if (prev_ea != ea + i || prev_io != io + i) + { + attempts = umax; + break; + } + } + + if (attempts == umax) + { + break; + } + } + + if (fast_lock.owns_lock()) + { + fast_lock.unlock(); + } + // Wait until we have no active RSX locks and reserve iomap for use. Must do so before acquiring vm lock to avoid deadlocks rsx::reservation_lock rsx_lock(ea, size); vm::writer_lock rlock; - for (u32 addr = ea, end = ea + size; addr < end; addr += 0x100000) + for (u32 addr = ea, end = ea + size; addr < end; addr += _1m) { if (!vm::check_addr(addr, vm::page_readable | (addr < 0x20000000 ? 0 : vm::page_1m_size))) { @@ -433,20 +477,23 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea } } - io >>= 20, ea >>= 20, size >>= 20; - rsx::eng_lock fifo_lock(render); std::scoped_lock lock(render->sys_rsx_mtx); - for (u32 i = 0; i < size; i++) + for (u32 i = 0; i < size; i += _1m) { auto& table = render->iomap_table; - // TODO: Investigate relaxed memory ordering - const u32 prev_ea = table.ea[io + i]; - table.ea[io + i].release((ea + i) << 20); - if (prev_ea + 1) table.io[prev_ea >> 20].release(-1); // Clear previous mapping if exists - table.io[ea + i].release((io + i) << 20); + const u32 prev_ea = table.ea[(io + i) / _1m]; + + table.ea[(io + i) / _1m].release(ea + i); + + if (prev_ea != umax) + { + table.io[prev_ea / _1m].release(-1); // Clear previous mapping if exists + } + + table.io[(ea + i) / _1m].release(io + i); } return CELL_OK; diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index b118ac80274c..71dc2ff3b22e 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -8,6 +8,7 @@ #include "Utilities/address_range.h" #include "Emu/CPU/CPUThread.h" #include "Emu/RSX/RSXThread.h" +#include "Emu/Cell/PPUThread.h" #include "Emu/Cell/SPURecompiler.h" #include "Emu/perf_meter.hpp" #include @@ -402,7 +403,7 @@ namespace vm return; } - if (i < 100) + if (1 || i < 100) busy_wait(200); else std::this_thread::yield(); @@ -460,9 +461,13 @@ namespace vm { } - writer_lock::writer_lock(u32 const addr, atomic_t* range_lock, u32 const size, u64 const flags) noexcept + writer_lock::writer_lock(u32 const addr, atomic_t* range_lock, bool halt_ppus) noexcept : range_lock(range_lock) { + // Constant-ized arguments + constexpr u32 size = 128; + constexpr u64 flags = vm::range_locked; + cpu_thread* cpu{}; if (g_tls_locked) @@ -480,24 +485,23 @@ namespace vm } } - for (u64 i = 0;; i++) + for (usz i = 0, diff = range_lock ? range_lock - g_range_lock_set : 0;; i++) { auto& bits = get_range_lock_bits(true); + const u64 bits_val = bits.load(); if (!range_lock) { - if (!bits && bits.compare_and_swap_test(0, u64{umax})) + if (!bits_val && bits.compare_and_swap_test(0, u64{umax})) { break; } } - else + else if (~bits_val & (1ull << diff)) { range_lock->release(addr | u64{size} << 32 | flags); - const auto diff = range_lock - g_range_lock_set; - - if (bits != umax && !bits.bit_test_set(static_cast(diff))) + if (!bits.bit_test_set(static_cast(diff))) { break; } @@ -519,14 +523,6 @@ namespace vm { perf_meter<"SUSPEND"_u64> perf0; - for (auto lock = g_locks.cbegin(), end = lock + g_cfg.core.ppu_threads; lock != end; lock++) - { - if (auto ptr = +*lock; ptr && ptr->state.none_of(cpu_flag::wait + cpu_flag::memory)) - { - ptr->state.test_and_set(cpu_flag::memory); - } - } - u64 addr1 = addr; if (u64 is_shared = g_shmem[addr >> 16]) [[unlikely]] @@ -547,6 +543,13 @@ namespace vm { to_clear = for_all_range_locks(to_clear & ~get_range_lock_bits(true), [&](u64 addr2, u32 size2) { + constexpr u32 range_size_loc = vm::range_pos - 32; + + if ((size2 >> range_size_loc) == (vm::range_readable >> vm::range_pos)) + { + return 0; + } + // Split and check every 64K page separately for (u64 hi = addr2 >> 16, max = (addr2 + size2 - 1) >> 16; hi <= max; hi++) { @@ -575,12 +578,22 @@ namespace vm break; } + to_clear &= get_range_lock_bits(false); + utils::pause(); } - for (auto lock = g_locks.cbegin(), end = lock + g_cfg.core.ppu_threads; lock != end; lock++) + for (auto lock = g_locks.cbegin(), end = lock + g_cfg.core.ppu_threads; halt_ppus && lock != end; lock++) + { + if (auto ptr = +*lock; ptr && ptr->state.none_of(cpu_flag::wait + cpu_flag::memory)) + { + ptr->state.test_and_set(cpu_flag::memory); + } + } + + for (auto lock = g_locks.cbegin(), end = lock + g_cfg.core.ppu_threads; halt_ppus && lock != end; lock++) { - if (auto ptr = +*lock) + if (auto ptr = static_cast*>(lock->load())) { while (!(ptr->state & cpu_flag::wait)) { diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 3c39d0aa4a01..09d2d1542177 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -81,7 +81,7 @@ namespace vm bool check_addr(u32 addr, u8 flags, u32 size); template - bool check_addr(u32 addr, u8 flags = page_readable) + inline bool check_addr(u32 addr, u8 flags = page_readable) { extern std::array g_pages; @@ -94,6 +94,16 @@ namespace vm return !(~g_pages[addr / 4096] & (flags | page_allocated)); } + // Like check_addr but should only be used in lock-free context with care + inline std::pair get_addr_flags(u32 addr) noexcept + { + extern std::array g_pages; + + const u8 flags = g_pages[addr / 4096].load(); + + return std::make_pair(!!(flags & page_allocated), flags); + } + // Read string in a safe manner (page aware) (bool true = if null-termination) bool read_string(u32 addr, u32 max_size, std::string& out_string, bool check_pages = true) noexcept; diff --git a/rpcs3/Emu/Memory/vm_locking.h b/rpcs3/Emu/Memory/vm_locking.h index c4d805554f20..199983b270ea 100644 --- a/rpcs3/Emu/Memory/vm_locking.h +++ b/rpcs3/Emu/Memory/vm_locking.h @@ -96,7 +96,7 @@ namespace vm writer_lock(const writer_lock&) = delete; writer_lock& operator=(const writer_lock&) = delete; writer_lock() noexcept; - writer_lock(u32 addr, atomic_t* range_lock = nullptr, u32 size = 128, u64 flags = range_locked) noexcept; + writer_lock(u32 addr, atomic_t* range_lock = nullptr, bool halt_ppus = true) noexcept; ~writer_lock() noexcept; }; } // namespace vm diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 360c35210017..68c6f45e2b5e 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -651,15 +651,29 @@ void kernel_explorer::update() return fmt::format(" (%.1fs)", wait_time); }; + std::vector> ppu_threads; + idm::select>([&](u32 id, ppu_thread& ppu) { const auto func = ppu.last_function; const ppu_thread_status status = lv2_obj::ppu_state(&ppu, false, false).first; - add_leaf(find_node(root, additional_nodes::ppu_threads), QString::fromStdString(fmt::format(u8"PPU 0x%07x: “%s”, PRIO: %d, Joiner: %s, Status: %s, State: %s, %s func: “%s”%s", id, *ppu.ppu_tname.load(), ppu.prio.load().prio, ppu.joiner.load(), status, ppu.state.load() - , ppu.ack_suspend ? "After" : (ppu.current_function ? "In" : "Last"), func ? func : "", get_wait_time_str(ppu.start_time)))); + const s32 prio = ppu.prio.load().prio; + std::string prio_text = fmt::format("%4d", prio); + prio_text = fmt::replace_all(prio_text, " ", " "); + + ppu_threads.emplace_back(prio, fmt::format(u8"PPU 0x%07x: PRIO: %s, “%s”Joiner: %s, Status: %s, State: %s, %s func: “%s”%s", id, prio_text, *ppu.ppu_tname.load(), ppu.joiner.load(), status, ppu.state.load() + , ppu.ack_suspend ? "After" : (ppu.current_function ? "In" : "Last"), func ? func : "", get_wait_time_str(ppu.start_time))); }, idm::unlocked); + // Sort by priority + std::stable_sort(ppu_threads.begin(), ppu_threads.end(), FN(x.first < y.first)); + + for (const auto& [prio, text] : ppu_threads) + { + add_leaf(find_node(root, additional_nodes::ppu_threads), QString::fromStdString(text)); + } + lock_idm_lv2.reset(); idm::select>([&](u32 /*id*/, spu_thread& spu)