diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 3aa3cca8ed..ad5545ba62 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2326,6 +2326,7 @@ void ppu_thread::cpu_wait(bs_t old) if (u32 addr = res_notify) { res_notify = 0; + res_notify_postpone_streak = 0; if (res_notify_time + 128 == (vm::reservation_acquire(addr) & -128)) { @@ -3613,34 +3614,17 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) { extern atomic_t liblv2_begin, liblv2_end; - const u32 notify = ppu.res_notify; - - if (notify) - { - if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true)) - { - ppu.state += cpu_flag::wait; - waiter->notify_all(); - } - - ppu.res_notify = 0; - } + u32 notify = ppu.res_notify; // Avoid notifications from lwmutex or sys_spinlock const bool is_liblv2_or_null = (ppu.cia >= liblv2_begin && ppu.cia < liblv2_end); - if (!is_liblv2_or_null && (addr ^ notify) & -128) + if (!is_liblv2_or_null) { // 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 - const u32 count = vm::reservation_notifier_count(addr & -128, rtime); + const u32 count = vm::reservation_notifier_count(addr, rtime); - if (cpu_flag::wait - ppu.state) - { - ppu.state += cpu_flag::wait; - } - - vm::reservation_notifier_notify(addr & -128, rtime); switch (count) { case 0: @@ -3650,11 +3634,18 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) } case 1: { - if (!notify) + // Postpone notifications if there is no pending one OR if there is likely a complex operation on reservation going on + // Which consists of multiple used addresses + if (ppu.res_notify_postpone_streak <= 4) { - ppu.res_notify = addr & -128; - ppu.res_notify_time = rtime; - break; + if (!notify || ((notify & -128) == (addr & -128) && new_data != old_data)) + { + ppu.res_notify = addr; + ppu.res_notify_time = rtime; + ppu.res_notify_postpone_streak++; + notify = 0; + break; + } } // Notify both @@ -3667,12 +3658,24 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) ppu.state += cpu_flag::wait; } - vm::reservation_notifier_notify(addr & -128, rtime); + vm::reservation_notifier_notify(addr, rtime); break; } } } + if (notify) + { + if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true)) + { + ppu.state += cpu_flag::wait; + waiter->notify_all(); + } + + ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; + } + static_cast(ppu.test_stopped()); if (addr == ppu.last_faddr) @@ -3700,6 +3703,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) } ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; } ppu.raddr = 0; diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 322cc13ebe..97c705aed5 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -267,6 +267,7 @@ public: u32 res_cached{0}; // Reservation "cached" addresss u32 res_notify{0}; u64 res_notify_time{0}; + u32 res_notify_postpone_streak{0}; union ppu_prio_t { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 8873089cf4..877f07e6c1 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4200,9 +4200,10 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args) do_cell_atomic_128_store(addr, _ptr(args.lsa & 0x3ff80)); - // TODO: Implement properly (notifying previous two times) - vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 128); - vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 256); + // Cover all waiters (TODO: Get reservation time atomically) + const u64 rtime = vm::reservation_acquire(addr); + vm::reservation_notifier_notify(addr, rtime - 128); + vm::reservation_notifier_notify(addr, rtime - 256); } bool spu_thread::do_mfc(bool can_escape, bool must_finish) @@ -6132,6 +6133,8 @@ s64 spu_thread::get_ch_value(u32 ch) eventstat_spin_count = 0; } } + + lv2_obj::notify_all(); } else { diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 284768d8bb..c405b98a2c 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1336,13 +1336,16 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) if (cpu.get_class() == thread_class::ppu) { - if (u32 addr = static_cast(cpu).res_notify) + ppu_thread& ppu = static_cast(cpu); + + if (u32 addr = ppu.res_notify) { - static_cast(cpu).res_notify = 0; + ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) { - if ((*it++ = vm::reservation_notifier_notify(addr, static_cast(cpu).res_notify_time, true))) + if ((*it++ = vm::reservation_notifier_notify(addr, ppu.res_notify_time, true))) { if (it < std::end(g_to_notify)) { @@ -1353,7 +1356,7 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) } else { - vm::reservation_notifier_notify(addr, static_cast(cpu).res_notify_time); + vm::reservation_notifier_notify(addr, ppu.res_notify_time); } } } @@ -1389,6 +1392,7 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio) if (u32 addr = ppu->res_notify) { ppu->res_notify = 0; + ppu->res_notify_postpone_streak = 0; if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) { @@ -2258,12 +2262,9 @@ void lv2_obj::notify_all() noexcept atomic_t* range_lock = nullptr; - u32 current_raddr = 0; - if (cpu->get_class() == thread_class::spu) { range_lock = static_cast(cpu)->range_lock; - current_raddr = static_cast(cpu)->raddr; } for (usz i = 0, checked = 0; checked < 4 && i < total_waiters; i++) @@ -2272,7 +2273,7 @@ void lv2_obj::notify_all() noexcept const u64 value = waiter.load(); u32 raddr = static_cast(value) & -128; - if (vm::check_addr(raddr) && (raddr != current_raddr || (value % 128) > 1)) + if (vm::check_addr(raddr)) { if (((raddr >> 28) < 2 || (raddr >> 28) == 0xd)) { @@ -2337,8 +2338,9 @@ void lv2_obj::notify_all() noexcept { if (notifies[i]) { - vm::reservation_update(notifies[i]); + // Cover all waiters for an address vm::reservation_notifier_notify(notifies[i], notifies_time[i]); + vm::reservation_notifier_notify(notifies[i], notifies_time[i] - 128); } } } diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 7dea0fed63..0aff5e1e7a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -485,7 +485,6 @@ public: static void enqueue_on_top(const void* waiter) { - return; g_to_notify[0] = waiter; g_to_notify[1] = nullptr; } diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 669661ee9a..de0bda933a 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -114,7 +114,10 @@ namespace vm if (ok) { + // Notify all waiters for an address + // This is because reservation_update is often brought after the actual reservation update and not by a fused operation reservation_notifier_notify(addr, rtime); + reservation_notifier_notify(addr, rtime - 128); if (cpu && !had_wait && cpu->test_stopped()) { diff --git a/rpcs3/Emu/Memory/vm_reservation.h b/rpcs3/Emu/Memory/vm_reservation.h index fe190aab65..6ff9aa2376 100644 --- a/rpcs3/Emu/Memory/vm_reservation.h +++ b/rpcs3/Emu/Memory/vm_reservation.h @@ -45,15 +45,14 @@ namespace vm static inline atomic_t* reservation_notifier(u32 raddr, u64 rtime) { - rtime = 0; constexpr u32 wait_vars_for_each = 64; - constexpr u32 unique_address_bit_mask = 0b111; - constexpr u32 unique_rtime_bit_mask = 0b11; + constexpr u32 unique_address_bit_mask = 0b1111; + constexpr u32 unique_rtime_bit_mask = 0b1; extern std::array, wait_vars_for_each * (unique_address_bit_mask + 1) * (unique_rtime_bit_mask + 1)> g_resrv_waiters_count; // Storage efficient method to distinguish different nearby addresses (which are likely) - const usz index = std::popcount(raddr & -1024) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 3) + ((raddr / 128) & unique_address_bit_mask); + const usz index = std::popcount(raddr & -2048) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 4) + ((raddr / 128) & unique_address_bit_mask); return &g_resrv_waiters_count[index]; }