diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index bc8c55910a..4c6b54f007 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3541,49 +3541,62 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) // Avoid notifications from lwmutex or sys_spinlock if (new_data != old_data && (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end)) { - const u32 notify = ppu.res_notify; + u32 notify = ppu.res_notify; if (notify) { - bool notified = false; - - if (ppu.res_notify_time == (vm::reservation_acquire(notify) & -128)) + if (ppu.res_notify_time == vm::reservation_notifier_count_index(notify).second) { ppu.state += cpu_flag::wait; vm::reservation_notifier_notify(notify); - notified = true; - } - - if (vm::reservation_notifier_count(addr)) - { - if (!notified) - { - ppu.res_notify = addr; - ppu.res_notify_time = rtime + 128; - } - else if ((addr ^ notify) & -128) - { - vm::reservation_notifier_notify(addr); - ppu.res_notify = 0; - } } else { - ppu.res_notify = 0; + notify = 0; } - static_cast(ppu.test_stopped()); + ppu.res_notify = 0; } - else + + if ((addr ^ notify) & -128) { // Try to postpone notification to when PPU is asleep or join notifications on the same address // This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock - if (vm::reservation_notifier_count(addr)) + const auto [count, index] = vm::reservation_notifier_count_index(addr); + + switch (count) { - ppu.res_notify = addr; - ppu.res_notify_time = rtime + 128; + case 0: + { + // Nothing to do + break; + } + case 1: + { + if (!notify) + { + ppu.res_notify = addr; + ppu.res_notify_time = index; + break; + } + + // Notify both + [[fallthrough]]; + } + default: + { + if (!notify) + { + ppu.state += cpu_flag::wait; + } + + vm::reservation_notifier_notify(addr); + break; + } } } + + static_cast(ppu.test_stopped()); } if (addr == ppu.last_faddr) @@ -3601,7 +3614,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) // And on failure it has some time to do something else if (notify && ((addr ^ notify) & -128)) { - if (ppu.res_notify_time == (vm::reservation_acquire(notify) & -128)) + if (ppu.res_notify_time == vm::reservation_notifier_count_index(notify).second) { ppu.state += cpu_flag::wait; vm::reservation_notifier_notify(notify); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index ad02909767..21a739664e 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -446,7 +446,7 @@ waitpkg_func static void __tpause(u32 cycles, u32 cstate) namespace vm { - std::array, 512> g_resrv_waiters_count{}; + std::array, 1024> g_resrv_waiters_count{}; } void do_cell_atomic_128_store(u32 addr, const void* to_write); diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 1570558278..2c29505a54 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1335,7 +1335,7 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) { static_cast(cpu).res_notify = 0; - if (static_cast(cpu).res_notify_time != (vm::reservation_acquire(addr) & -128)) + if (static_cast(cpu).res_notify_time != vm::reservation_notifier_count_index(addr).second) { // Ignore outdated notification request } @@ -1388,7 +1388,7 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio) { ppu->res_notify = 0; - if (ppu->res_notify_time != (vm::reservation_acquire(addr) & -128)) + if (ppu->res_notify_time != vm::reservation_notifier_count_index(addr).second) { // Ignore outdated notification request } diff --git a/rpcs3/Emu/Memory/vm_reservation.h b/rpcs3/Emu/Memory/vm_reservation.h index 6231e61a06..c9d2e0c50a 100644 --- a/rpcs3/Emu/Memory/vm_reservation.h +++ b/rpcs3/Emu/Memory/vm_reservation.h @@ -45,14 +45,24 @@ namespace vm static inline std::pair*, atomic_t*> reservation_notifier(u32 raddr) { - extern std::array, 512> g_resrv_waiters_count; + extern std::array, 1024> g_resrv_waiters_count; // Storage efficient method to distinguish different nearby addresses (which are likely) - const usz index = std::popcount(raddr & -512) + ((raddr / 128) % 4) * 32; - auto& waiter = g_resrv_waiters_count[index * 4]; - return { &g_resrv_waiters_count[index * 4 + waiter.load().waiters_index % 4], &waiter }; + constexpr u32 wait_vars_for_each = 8; + constexpr u32 unique_address_bit_mask = 0b11; + const usz index = std::popcount(raddr & -1024) + ((raddr / 128) & unique_address_bit_mask) * 32; + auto& waiter = g_resrv_waiters_count[index * wait_vars_for_each]; + return { &g_resrv_waiters_count[index * wait_vars_for_each + waiter.load().waiters_index % wait_vars_for_each], &waiter }; } + // Returns waiter count and index + static inline std::pair reservation_notifier_count_index(u32 raddr) + { + const auto notifiers = reservation_notifier(raddr); + return { notifiers.first->load().waiters_count, static_cast(notifiers.first - notifiers.second) }; + } + + // Returns waiter count static inline u32 reservation_notifier_count(u32 raddr) { return reservation_notifier(raddr).first->load().waiters_count; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index d066b83908..8c3566dca5 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -947,6 +947,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 3183f8dd83..584787892a 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1461,6 +1461,9 @@ Emu\Memory + + Emu\Memory + Emu\Memory