From ad42a2b89ad9b88ab65fd8a58d0ae457461b9a6f Mon Sep 17 00:00:00 2001 From: Elad Ashkenazi <18193363+elad335@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:19:45 +0300 Subject: [PATCH] SPU: Task-based SPURS limiter --- rpcs3/Emu/Cell/SPUThread.cpp | 77 +++++++++++++++++++++++++++++++++- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_spu.h | 1 + 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 9e3f188a12..07cef479e5 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1837,6 +1837,11 @@ void spu_thread::cpu_task() } } + if (spurs_addr && group->max_run != 6) + { + group->spurs_running++; + } + if (jit) { while (true) @@ -1884,6 +1889,14 @@ void spu_thread::cpu_task() allow_interrupts_in_cpu_work = false; } + + if (spurs_addr && group->max_run != 6) + { + if (group->spurs_running.exchange(0)) + { + group->spurs_running.notify_all(); + } + } } void spu_thread::cpu_work() @@ -5469,12 +5482,71 @@ s64 spu_thread::get_ch_value(u32 ch) case SPU_RdEventStat: { + const bool is_spurs_task_wait = pc == 0x11a8 && group->max_run != 6 && spurs_addr; + + if (is_spurs_task_wait) + { + const u32 prev_running = group->spurs_running.fetch_op([](u32& x) + { + if (x) + { + x--; + return true; + } + + return false; + }).first; + + if (prev_running == group->max_run) + { + group->spurs_running.notify_all(); + } + } + const u32 mask1 = ch_events.load().mask; auto events = get_events(mask1, false, true); + const auto wait_spurs_task = [&] + { + if (is_spurs_task_wait) + { + // Wait for other threads to complete their tasks (temporarily) + if (!is_stopped()) + { + const u32 prev_running = group->spurs_running.fetch_op([max = group->max_run](u32& x) + { + if (x < max) + { + x++; + return true; + } + + return false; + }).first; + + if (prev_running >= group->max_run) + { + const u64 before = get_system_time(); + thread_ctrl::wait_on(group->spurs_running, prev_running, 10000); + + // Check for missed waiting (due to thread-state notification or value change) + const u32 new_running = group->spurs_running; + + if (!is_stopped() && new_running >= group->max_run && get_system_time() - before < 1500) + { + thread_ctrl::wait_on(group->spurs_running, new_running, 10000); + } + + group->spurs_running++; + } + } + } + }; + if (events.count) { + wait_spurs_task(); return events.events & mask1; } @@ -5490,7 +5562,7 @@ s64 spu_thread::get_ch_value(u32 ch) const u32 old_raddr = raddr; // Does not need to safe-access reservation if LR is the only event masked - // Because it's either an access violation or a livelock if an invalid memory is passed + // Because it's either an access violation or a live-lock if an invalid memory is passed if (raddr && mask1 > SPU_EVENT_LR) { auto area = vm::get(vm::any, raddr); @@ -5504,7 +5576,7 @@ s64 spu_thread::get_ch_value(u32 ch) } else if (area) { - // Ensure possesion over reservation memory so it won't be deallocated + // Ensure possession over reservation memory so it won't be de-allocated auto [base_addr, shm_] = area->peek(raddr); if (shm_) @@ -5690,6 +5762,7 @@ s64 spu_thread::get_ch_value(u32 ch) thread_ctrl::wait_on(state, old, 100); } + wait_spurs_task(); wakeup_delay(); if (is_paused(state - cpu_flag::suspend)) diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 8985ba33e6..150d755536 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -1057,7 +1057,7 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id) default: return CELL_ESTAT; } - const u32 max_threads = group->max_run; + const u32 max_threads = group->max_num; group->join_state = 0; group->exit_status = 0; diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.h b/rpcs3/Emu/Cell/lv2/sys_spu.h index 690a273f4c..8b0b890b39 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.h +++ b/rpcs3/Emu/Cell/lv2/sys_spu.h @@ -289,6 +289,7 @@ struct lv2_spu_group atomic_t exit_status; // SPU Thread Group Exit Status atomic_t join_state; // flags used to detect exit cause and signal atomic_t running = 0; // Number of running threads + atomic_t spurs_running = 0; atomic_t stop_count = 0; atomic_t wait_term_count = 0; u32 waiter_spu_index = -1; // Index of SPU executing a waiting syscall