diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 434187ba9a..fed1a8fafd 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1142,6 +1142,8 @@ static T ppu_load_acquire_reservation(ppu_thread& ppu, u32 addr) ppu.raddr = addr; + u32 addr_mask = -1; + if (const s32 max = g_cfg.core.ppu_128_reservations_loop_max_length) { // If we use it in HLE it means we want the accurate version @@ -1152,7 +1154,7 @@ static T ppu_load_acquire_reservation(ppu_thread& ppu, u32 addr) if ((cia & 0xffff) >= 0x10000u - max * 4) { // Do not cross 64k boundary - return true; + return false; } const auto inst = vm::_ptr>(cia); @@ -1188,12 +1190,44 @@ static T ppu_load_acquire_reservation(ppu_thread& ppu, u32 addr) return true; }(); + + if (ppu.use_full_rdata) + { + addr_mask = -128; + } } else { ppu.use_full_rdata = false; } + if ((addr & addr_mask) == (ppu.last_faddr & addr_mask)) + { + ppu_log.trace(u8"LARX after fail: addr=0x%x, faddr=0x%x, time=%u c", addr, ppu.last_faddr, (perf0.get() - ppu.last_ftsc)); + } + + if ((addr & addr_mask) == (ppu.last_faddr & addr_mask) && (perf0.get() - ppu.last_ftsc) < 600 && (vm::reservation_acquire(addr, sizeof(T)) & -128) == ppu.last_ftime) + { + be_t rdata; + std::memcpy(&rdata, &ppu.rdata[addr & 0x78], 8); + + if (rdata == data.load()) + { + ppu.rtime = ppu.last_ftime; + ppu.raddr = ppu.last_faddr; + return static_cast(rdata << data_off >> size_off); + } + + ppu.last_fail++; + ppu.last_faddr = 0; + } + else + { + // Silent failure + ppu.last_faddr = 0; + } + + // Skip loop if already loaded for (u64 count = 0; count != umax; [&]() { if (ppu.state) @@ -1322,6 +1356,7 @@ const auto ppu_stcx_accurate_tx = build_function_asm(new_data))) { - case 0: - { - // Reservation lost - return false; - } case UINT32_MAX: { - break; + const bool ok = cpu_thread::suspend_all<+1>(&ppu, [&] + { + auto& all_data = *vm::get_super_ptr(addr & -128); + + if ((res & -128) == rtime && cmp_rdata(ppu.rdata, all_data)) + { + data.release(new_data); + res += 127; + return true; + } + + mov_rdata(ppu.rdata, all_data); + res -= 1; + return false; + }); + + if (ok) + { + break; + } + + [[fallthrough]]; + } + case 0: + { + if (ppu.last_faddr == addr) + { + ppu.last_fail++; + } + + ppu.last_faddr = addr; + ppu.last_ftime = res.load() & -128; + ppu.last_ftsc = __rdtsc(); + return false; } default: { @@ -1682,22 +1766,17 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) perf_log.warning("STCX: took too long: %u", count); } - return true; + break; } } - return cpu_thread::suspend_all<+1>(&ppu, [&] + if (ppu.last_faddr == addr) { - if ((res & -128) == rtime && cmp_rdata(ppu.rdata, vm::_ref(addr & -128))) - { - data.release(new_data); - res += 127; - return true; - } + ppu.last_succ++; + } - res -= 1; - return false; - }); + ppu.last_faddr = 0; + return true; } auto [_oldd, _ok] = res.fetch_op([&](u64& r) @@ -1749,6 +1828,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) if (new_data == old_data) { + ppu.last_faddr = 0; return res.compare_and_swap_test(rtime, rtime + 128); } @@ -1770,6 +1850,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) // Give up if reservation has been locked or updated if (!_ok) { + ppu.last_faddr = 0; return false; } @@ -1780,11 +1861,34 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) return true; } - res -= 1; + const u64 old_rtime = res.fetch_sub(1); + + // TODO: disabled with this setting on, since it's dangerous to mix + if (!g_cfg.core.ppu_128_reservations_loop_max_length) + { + // Store old_data on failure + if (ppu.last_faddr == addr) + { + ppu.last_fail++; + } + + ppu.last_faddr = addr; + ppu.last_ftime = old_rtime & -128; + ppu.last_ftsc = __rdtsc(); + std::memcpy(&ppu.rdata[addr & 0x78], &old_data, 8); + } + return false; }()) { res.notify_all(); + + if (addr == ppu.last_faddr) + { + ppu.last_succ++; + } + + ppu.last_faddr = 0; return true; }