RSX: fall back to event-stream wait when the semaphore spin does not park

The one-shot cacheline wait (ldaxr-armed WFE) used in semaphore_acquire
does not park on every core. Measured on Snapdragon 8 Elite class (Oryon,
Odin 3): WFE returns immediately while the exclusive monitor is armed
(~28.8M wakes/s in a standalone microbenchmark, vs ~20-30k/s for bare WFE
and sevl+wfe), so the acquire loop ran at ~57M iterations/s through waits
averaging 33 ms - about 99% of the RSX thread's wall time at a menu, with
each iteration also paying the driver-recovery get_system_time() check.

Keep the armed one-shot for the first 500 iterations of a wait - on cores
where it parks it keeps its instant wake-on-write, and where it does not
it acts as a short spin that still catches quick signals - then fall back
to wait_for_event(), which parks on both classes and bounds wake latency
at the architected event-stream period (~50 us measured).

Measured on device (Mirror's Edge, MT RSX on, state-verified windows):
menu instructions -55% (402.7G -> 179.5G per 30 s), played-gameplay
instructions -29% (391.6G -> 279.7G), loop iterations down ~1,400x, wait
counts/durations unchanged, 30 fps frame pacing unchanged (max frametime
34.2 ms). Note: cpu-cycles PMU counts at full clock during WFE park on
this SoC, so cycle-based profiles cannot see this change; measure with
instructions retired.
This commit is contained in:
Zulux91
2026-08-14 03:47:48 -05:00
parent 39ca5cdab6
commit 002a9b274a
+20 -2
View File
@@ -50,6 +50,7 @@ namespace rsx
u64 start = get_system_time();
u64 last_check_val = start;
u64 spin_budget = 0;
while (sema != arg)
{
@@ -92,8 +93,25 @@ namespace rsx
RSX_PROF_SCOPE(idle);
// Wait until the value changes or until 100us pass.
utils::spin_on_cacheline_once(atomic_sema, sema, 100);
#if defined(ARCH_ARM64)
// The armed one-shot wait below does not park on every core: on Oryon
// (Snapdragon 8 Elite class) WFE returns immediately while the exclusive
// monitor is armed, so this loop ran at tens of millions of iterations per
// second instead of waiting. Give the armed form a short window first - on
// cores where it parks it keeps its instant wake-on-write, and where it
// does not it acts as a brief spin that still catches short waits - then
// fall back to the event-stream wait, which parks on both classes and
// bounds wake latency at the event-stream period.
if (++spin_budget > 500)
{
utils::wait_for_event();
}
else
#endif
{
// Wait until the value changes or until 100us pass.
utils::spin_on_cacheline_once(atomic_sema, sema, 100);
}
}
RSX(ctx)->fifo_wake_delay();