From 68349e48cdfb070164dfe7fdd54f3b183c25ead6 Mon Sep 17 00:00:00 2001 From: Eladash <18193363+elad335@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:16:23 +0200 Subject: [PATCH] Savestates: Save PPU running order --- rpcs3/Emu/Cell/PPUThread.cpp | 65 ++++++++++++++++++++++++++++++- rpcs3/Emu/Cell/PPUThread.h | 3 +- rpcs3/Emu/Cell/lv2/lv2.cpp | 16 ++++---- rpcs3/Emu/Cell/lv2/sys_sync.h | 12 +++++- rpcs3/Emu/savestate_utils.cpp | 2 +- rpcs3/rpcs3qt/kernel_explorer.cpp | 2 +- 6 files changed, 86 insertions(+), 14 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index dd52bd9dba..fb2f1dc666 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2390,6 +2390,12 @@ void ppu_thread::serialize_common(utils::serial& ar) } } +struct save_lv2_tag +{ + atomic_t saved = false; + atomic_t loaded = false; +}; + ppu_thread::ppu_thread(utils::serial& ar) : cpu_thread(idm::last_id()) // last_id() is showed to constructor on serialization , stack_size(ar) @@ -2398,6 +2404,8 @@ ppu_thread::ppu_thread(utils::serial& ar) , entry_func(std::bit_cast(ar)) , is_interrupt_thread(ar) { + [[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(ppu); + struct init_pushed { bool pushed = false; @@ -2408,6 +2416,11 @@ ppu_thread::ppu_thread(utils::serial& ar) syscall_history.data.resize(g_cfg.core.ppu_call_history ? syscall_history_max_size : 1); syscall_history.count_debug_arguments = static_cast(g_cfg.core.ppu_call_history ? std::size(syscall_history.data[0].args) : 0); + if (version >= 2 && !g_fxo->get().loaded.exchange(true)) + { + ar(lv2_obj::g_priority_order_tag); + } + serialize_common(ar); // Restore jm_mask @@ -2437,7 +2450,41 @@ ppu_thread::ppu_thread(utils::serial& ar) case PPU_THREAD_STATUS_RUNNABLE: case PPU_THREAD_STATUS_ONPROC: { - lv2_obj::awake(this); + if (version >= 2) + { + const u32 order = ar.pop(); + + struct awake_pushed + { + bool pushed = false; + shared_mutex dummy; + std::map awake_ppus; + }; + + g_fxo->get().awake_ppus[order] = this; + + if (!std::exchange(g_fxo->get().pushed, true)) + { + Emu.PostponeInitCode([this]() + { + u32 prev = umax; + + for (auto ppu : g_fxo->get().awake_ppus) + { + ensure(prev + 1 == ppu.first); + prev = ppu.first; + lv2_obj::awake(ppu.second); + } + + g_fxo->get().awake_ppus.clear(); + }); + } + } + else + { + lv2_obj::awake(this); + } + [[fallthrough]]; } case PPU_THREAD_STATUS_SLEEP: @@ -2485,6 +2532,7 @@ ppu_thread::ppu_thread(utils::serial& ar) const u32 op = vm::read32(ppu.cia); const auto& table = g_fxo->get(); ppu.loaded_from_savestate = true; + ppu.prio.raw().preserve_bit = 1; table.decode(op)(ppu, {op}, vm::_ptr(ppu.cia), &ppu_ret); ppu.optional_savestate_state->clear(); // Reset to writing state @@ -2539,9 +2587,17 @@ void ppu_thread::save(utils::serial& ar) } ar(stack_size, stack_addr, _joiner, entry, is_interrupt_thread); + + const bool is_null = ar.m_file_handler && ar.m_file_handler->is_null(); + + if (!is_null && !g_fxo->get().saved.exchange(true)) + { + ar(lv2_obj::g_priority_order_tag); + } + serialize_common(ar); - ppu_thread_status status = lv2_obj::ppu_state(this, false); + auto [status, order] = lv2_obj::ppu_state(this, false); if (status == PPU_THREAD_STATUS_SLEEP && cpu_flag::again - state) { @@ -2551,6 +2607,11 @@ void ppu_thread::save(utils::serial& ar) ar(status); + if (status == PPU_THREAD_STATUS_RUNNABLE || status == PPU_THREAD_STATUS_ONPROC) + { + ar(order); + } + ar(*ppu_tname.load()); } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 8c46475a68..00961f7ddc 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -268,7 +268,8 @@ public: { u64 all; bf_t prio; // Thread priority (0..3071) (firs 12-bits) - bf_t order; // Thread enqueue order (last 52-bits) + bf_t order; // Thread enqueue order (last 52-bits) + bf_t preserve_bit; // Preserve value for savestates }; atomic_t prio{}; diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 2676ee1d3c..a407ad22de 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1946,7 +1946,7 @@ void lv2_obj::make_scheduler_ready() lv2_obj::awake_all(); } -ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_lv2) +std::pair lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_lv2) { std::optional opt_lock[2]; @@ -1957,13 +1957,13 @@ ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_l if (!Emu.IsReady() ? ppu->state.all_of(cpu_flag::stop) : ppu->stop_flag_removal_protection) { - return PPU_THREAD_STATUS_IDLE; + return { PPU_THREAD_STATUS_IDLE, 0}; } switch (ppu->joiner) { - case ppu_join_status::zombie: return PPU_THREAD_STATUS_ZOMBIE; - case ppu_join_status::exited: return PPU_THREAD_STATUS_DELETED; + case ppu_join_status::zombie: return { PPU_THREAD_STATUS_ZOMBIE, 0}; + case ppu_join_status::exited: return { PPU_THREAD_STATUS_DELETED, 0}; default: break; } @@ -1988,18 +1988,18 @@ ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_l { if (!ppu->interrupt_thread_executing) { - return PPU_THREAD_STATUS_STOP; + return { PPU_THREAD_STATUS_STOP, 0}; } - return PPU_THREAD_STATUS_SLEEP; + return { PPU_THREAD_STATUS_SLEEP, 0 }; } if (pos >= g_cfg.core.ppu_threads + 0u) { - return PPU_THREAD_STATUS_RUNNABLE; + return { PPU_THREAD_STATUS_RUNNABLE, pos }; } - return PPU_THREAD_STATUS_ONPROC; + return { PPU_THREAD_STATUS_ONPROC, pos}; } void lv2_obj::set_future_sleep(cpu_thread* cpu) diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index ad3235c81b..ba3b074e66 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -239,6 +239,16 @@ public: object->prio.atomic_op([order = ++g_priority_order_tag](std::common_type_t()->prio.load())>& prio) { + if constexpr (requires { +std::declval().preserve_bit; } ) + { + if (prio.preserve_bit) + { + // Restoring state on load + prio.preserve_bit = 0; + return; + } + } + prio.order = order; }); } @@ -274,7 +284,7 @@ public: static void make_scheduler_ready(); - static ppu_thread_status ppu_state(ppu_thread* ppu, bool lock_idm = true, bool lock_lv2 = true); + static std::pair ppu_state(ppu_thread* ppu, bool lock_idm = true, bool lock_lv2 = true); static inline void append(cpu_thread* const thread) { diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 3006ce1703..7bf6787f16 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -42,7 +42,7 @@ static std::array s_serial_versions; } SERIALIZATION_VER(global_version, 0, 16) // For stuff not listed here -SERIALIZATION_VER(ppu, 1, 1) +SERIALIZATION_VER(ppu, 1, 1, 2/*PPU sleep order*/) SERIALIZATION_VER(spu, 2, 1) SERIALIZATION_VER(lv2_sync, 3, 1) SERIALIZATION_VER(lv2_vm, 4, 1) diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index d0ad360f36..a751ca93c8 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -655,7 +655,7 @@ void kernel_explorer::update() 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); + const ppu_thread_status status = lv2_obj::ppu_state(&ppu, false, false).first; add_leaf(find_node(root, additional_nodes::ppu_threads), qstr(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))));