diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 05f53a8e9..e687fffd0 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4330,6 +4330,7 @@ bool spu_thread::process_mfc_cmd() // Seemingly not getllar_busy_waiting_switch = umax; getllar_spin_count = 0; + getllar_outbuf_hits = 0; return true; } @@ -4337,6 +4338,7 @@ bool spu_thread::process_mfc_cmd() { getllar_busy_waiting_switch = umax; getllar_spin_count = 0; + getllar_outbuf_hits = 0; return true; } @@ -4375,8 +4377,29 @@ bool spu_thread::process_mfc_cmd() getllar_cs_first = cs.empty() ? umax : cs[0].second; } - if (getllar_cs_first != umax && last_getllar_lsa > getllar_cs_first) + // Stop believing the verdict once it is contradicted by repetition. + // + // Saying "not a loop" here is not free. It resets the spin count and + // leaves the switch at umax, and the caller then skips both busy_wait + // and the sleep path and returns immediately, so the SPU re-executes + // GETLLAR at full rate with no backoff at all. The spin count never + // reaches 4, so the optimisation is never evaluated, and the 400ms + // "don't be stubborn" fallback that would force a sleep is never + // reached either. One SPU in that state holds a core flat out. + // + // Web of Shadows sits in exactly that case: its GETLLAR sites use an + // LSA in the top 64K of local store that looks like a caller's OUT + // buffer, and process_mfc_cmd measured 55% of all CPU across the + // process while the game ran at 10-15fps. + // + // Re-entering the same site with the same stack this many times is + // itself the evidence that it is a loop, whatever the LSA looks like, + // so hand it back to the normal spin detection and let that decide + // between busy-waiting and sleeping. + if (getllar_cs_first != umax && last_getllar_lsa > getllar_cs_first && + getllar_outbuf_hits < 32) { + getllar_outbuf_hits++; getllar_busy_waiting_switch = umax; getllar_spin_count = 0; return true; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 25714514d..eaa89792d 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -822,6 +822,11 @@ public: u32 getllar_cs_sp = umax; u32 getllar_cs_lr = umax; u32 getllar_cs_first = umax; + + // Consecutive "not a loop" verdicts from that check at the same site. The verdict suppresses + // both the busy-wait and the sleep, so believing it forever leaves the SPU re-entering + // GETLLAR at full rate; repetition is the evidence that it is wrong. + u32 getllar_outbuf_hits = 0; u32 getllar_busy_waiting_switch = umax; // umax means the test needs evaluation, otherwise it's a boolean u64 getllar_evaluate_time = 0;