diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index c7fad44dbc..bdc08ce176 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4790,6 +4790,12 @@ bool spu_thread::stop_and_signal(u32 code) return false; } + if (Emu.IsStarting()) + { + // Deregister lv2_obj::g_to_sleep entry (savestates related) + lv2_obj::sleep(*this); + } + if (group->run_state >= SPU_THREAD_GROUP_STATUS_WAITING && group->run_state <= SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED) { // Try again diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 59e1d4b29f..11d5316c22 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1233,6 +1233,30 @@ void lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout) { const u64 start_time = get_guest_system_time(); + auto on_to_sleep_update = [&]() + { + std::string out = fmt::format("Threads (%d):", g_to_sleep.size()); + for (auto thread : g_to_sleep) + { + fmt::append(out, " 0x%x,", thread->id); + } + + ppu_log.warning("%s", out); + + if (g_to_sleep.empty()) + { + // All threads are ready, wake threads + Emu.CallFromMainThread([] + { + if (Emu.IsStarting()) + { + // It uses lv2_obj::g_mutex, run it on main thread + Emu.FinalizeRunRequest(); + } + }); + } + }; + if (auto ppu = thread.try_get()) { ppu_log.trace("sleep() - waiting (%zu)", g_pending.size()); @@ -1260,27 +1284,7 @@ void lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout) if (unqueue(g_to_sleep, ppu)) { ppu->start_time = start_time; - - std::string out = fmt::format("Threads (%d):", g_to_sleep.size()); - for (auto thread : g_to_sleep) - { - fmt::append(out, " 0x%x,", thread->id); - } - - ppu_log.warning("%s", out); - - if (g_to_sleep.empty()) - { - // All threads are ready, wake threads - Emu.CallFromMainThread([] - { - if (Emu.IsStarting()) - { - // It uses lv2_obj::g_mutex, run it on main thread - Emu.FinalizeRunRequest(); - } - }); - } + on_to_sleep_update(); } // Already sleeping @@ -1293,6 +1297,13 @@ void lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout) ppu->raddr = 0; // Clear reservation ppu->start_time = start_time; } + else if (auto spu = thread.try_get()) + { + if (unqueue(g_to_sleep, spu)) + { + on_to_sleep_update(); + } + } if (timeout) { @@ -1551,9 +1562,9 @@ ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_l return PPU_THREAD_STATUS_ONPROC; } -void lv2_obj::set_future_sleep(ppu_thread* ppu) +void lv2_obj::set_future_sleep(cpu_thread* cpu) { - g_to_sleep.emplace_back(ppu); + g_to_sleep.emplace_back(cpu); } bool lv2_obj::is_scheduler_ready() diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index ff1c8cd9d9..ac512d4b68 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -228,7 +228,7 @@ lv2_spu_group::lv2_spu_group(utils::serial& ar) noexcept *ep = idm::get_unlocked(ar.operator u32()); } - u32 waiter_spu_index = -1; + waiter_spu_index = -1; switch (run_state) { @@ -256,15 +256,20 @@ lv2_spu_group::lv2_spu_group(utils::serial& ar) noexcept // Suspend all SPU threads except a thread that waits on sys_spu_thread_receive_event for (const auto& thread : threads) { - if (thread && thread->index != waiter_spu_index) + if (thread) { + if (thread->index == waiter_spu_index) + { + lv2_obj::set_future_sleep(thread.get()); + continue; + } + thread->state += cpu_flag::suspend; } } break; } - //case SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED: //case SPU_THREAD_GROUP_STATUS_RUNNING: //case SPU_THREAD_GROUP_STATUS_STOPPED: //case SPU_THREAD_GROUP_STATUS_UNKNOWN: diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index b62072fe33..aadb9bfbe3 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -200,7 +200,7 @@ public: } // Serialization related - static void set_future_sleep(ppu_thread* ppu); + static void set_future_sleep(cpu_thread* cpu); static bool is_scheduler_ready(); static void cleanup(); @@ -450,7 +450,7 @@ private: static std::deque> g_waiting; // Threads which must call lv2_obj::sleep before the scheduler starts - static std::deque g_to_sleep; + static std::deque g_to_sleep; static void schedule_all(); }; diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 62951208cf..3cef40ed2b 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -20,6 +20,7 @@ #include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Cell/lv2/sys_prx.h" #include "Emu/Cell/lv2/sys_overlay.h" +#include "Emu/Cell/lv2/sys_spu.h" #include "Emu/Cell/Modules/cellGame.h" #include "Emu/title.h" @@ -1955,11 +1956,30 @@ void Emulator::RunPPU() // Run main thread idm::select>([](u32, named_thread& cpu) { - if (cpu.stop_flag_removal_protection) return; + if (std::exchange(cpu.stop_flag_removal_protection, false)) + { + return; + } + ensure(cpu.state.test_and_reset(cpu_flag::stop)); cpu.state.notify_one(cpu_flag::stop); }); + // Run SPUs waiting on a syscall (savestates related) + idm::select>([](u32, named_thread& spu) + { + if (spu.group && spu.index == spu.group->waiter_spu_index) + { + if (std::exchange(spu.stop_flag_removal_protection, false)) + { + return; + } + + ensure(spu.state.test_and_reset(cpu_flag::stop)); + spu.state.notify_one(cpu_flag::stop); + } + }); + if (auto thr = g_fxo->try_get>()) { thr->state -= cpu_flag::stop; @@ -1992,11 +2012,20 @@ void Emulator::FixGuestTime() } void Emulator::FinalizeRunRequest() { - auto on_select = [] (u32, spu_thread& cpu) + auto on_select = [](u32, spu_thread& spu) { - if (cpu.stop_flag_removal_protection) return; - ensure(cpu.state.test_and_reset(cpu_flag::stop)); - cpu.state.notify_one(cpu_flag::stop); + if (spu.group && spu.index == spu.group->waiter_spu_index) + { + return; + } + + if (std::exchange(spu.stop_flag_removal_protection, false)) + { + return; + } + + ensure(spu.state.test_and_reset(cpu_flag::stop)); + spu.state.notify_one(cpu_flag::stop); }; idm::select>(on_select);