Files
virtualjaguar-libretro/src
Joe MattielloandClaude Fable 5.1 bb2ee2c0ea perf(gpu): direct-threaded dispatch inside GPUExec (#569) (#1140)
GPUExec dispatched every emulated instruction through executeOpcode(): a
call, a return and the register spills either side of them, per op.  The
function could never be inlined into the loop: it holds label addresses
(computed goto), and it is recursive -- jump/jr run their delay slot
through it.

GPUExec now dispatches through its own computed-goto table to labels
inside GPUExec, where the (GPU_OP_HOT) handlers are inlined; the loop's
post-op bookkeeping follows unchanged.  executeOpcode() stays exactly as
it was for the delay slot.  One X-macro, GPU_OPCODE_LIST, generates both
tables and both label blocks, so the two copies cannot drift (it also
replaces executeOpcode's hand-written 64-entry table).  Non-GNU compilers
keep the executeOpcode() call.

Emulated output unchanged: frame_hash_ab CSVs byte-identical to develop
over 300 Doom gameplay frames and 400 yarc frames; gpu_idle_probe_falsify
passes (the idle probe keys on index 53 after dispatch, as before).

Measured on top of #1137, Doom gameplay from one savestate:
- cachegrind (ir_ab.sh --ref, 300 frames): Ir -4.07%, D1 miss -4.13%,
  I1 miss +2.80% (GPUExec is larger), LL +0.16%.
- Mac Studio M4 Max wall, test_benchmark 900 frames, A/B/B/A x3:
  avg 3.60 -> 3.38 ms (-6.1%), p99 4.3 -> 4.0 ms, every B run faster
  than every A run; yarc 3.39 -> 3.34 ms (-1.6%).

Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-11-01 16:21:07 -05:00
..