Commit Graph
17 Commits
Author SHA1 Message Date
Brian Degenhardt 540c5ae0c5 eerunner: --gsdump headless GS-dump capture
Records the GIF command stream from a --liverun into a .gs dump, so a GS
workload can be replayed in pcsx2-gsrunner with no emulator in front of it.

This is the only honest way to A/B the GS across two builds. Measuring the
GS inside a live run does not work: both the MTGS ring and the software
rasterizer's job queue spin while waiting for work, so a build with a faster
EE shifts the GS threads' instruction counts without changing a single pixel
of GS work. Measured on Xenosaga with byte-identical GS workloads (same PRIM,
DRW and Mpps), a live run reported the GS threads executing 23% more
instructions while burning 21% fewer cycles -- the signature of spin, not
rasterization. Replaying the same captured dump standalone put the same build
11% ahead on instructions.

Recording arms after --gsdump-at frames (default 30, so the scene has settled)
and stops on its own once the requested frame count has been recorded; the
frame budget is raised automatically if it would truncate the dump.
2026-07-24 21:04:35 -07:00
Brian Degenhardt 9fa68bdfce eerunner: --rec-fallback opcode-group interpreter bisect switch
EE JIT divergence hunts start at "this game miscomputes something under the
EE recompiler" with no idea which emitter is at fault, and the existing
funnel (stepdiff/contmem) localizes to a frame and a RAM region — which
chaotic float amplification makes almost unreadable by end of frame.

Add a harness-only switch that routes whole EE opcode groups through
recCall(interpret) instead of their native emitter, everything else still
JIT. The interpreters are the known-good baseline, so the group that makes
the symptom disappear contains the bug, and each probe is one run instead of
a rebuild per hypothesis. Groups: fpu, cop2, mmi, multdiv, shift, arith,
loadstore, move, cop0, branch, plus all/none. cop2 narrows into cop2move /
cop2vu / cop2ls and then into qmfc2 / cfc2 / qmtc2 / ctc2, and those four
accept a `:<reg>` destination filter (`ctc2:0`) since their fs field names
32 registers with wildly different semantics.

Gated on PCSX2_RECOMPILER_TESTS so it never reaches a shipping build; the
non-hooks path compiles to `constexpr bool forcedInterp = false`. A forced
fallback also feeds delaySlotNeedsBranchBracket, matching the conservatism
already applied to genuine !opcode.recompile fallbacks, so the switch cannot
itself perturb delay-slot exception semantics.

Pairs with `--mkstate --renderer sw`, whose savestate carries a rendered
screenshot, to give a headless visual oracle for graphical bugs. Together
these took the Xenosaga zoom bug from "somewhere in the EE JIT" to the exact
instruction (CTC2 to vi00) in about ten runs.
2026-07-24 17:35:31 -07:00
Brian Degenhardt b8e7ad46ad eerunner: fix stepdiff self-loop skip and add entry-state probe
The self-loop phase-skip only consulted BlockBackwardBranchTarget on
ar.pc (the fall-through), so a self-loop whose divergence is observed
at its exit was never skipped and stopped the walk as a false positive
(this is what pinned Carbon's 0x174c30 byte-fill loop). Also check
ar.prev_pc, the offending self-loop itself.

The zoom report now dumps both streams' GPRs and FPRs at the offending
block head, so identical-entry false positives are visible directly in
the report instead of needing a hand-built harness.
2026-07-24 15:30:32 -07:00
J1coding 80feae5f31 iOS: route RetroAchievements through native UI instead of ImGui FullscreenUI
The iOS app renders its own SwiftUI UI, so the shared core's ImGui
FullscreenUI overlay never appears on screen. Before this change every
RetroAchievements event still initialized FullscreenUI and posted the
notification through it, adding per-frame render work for an overlay that
is invisible on iOS, and the native toast layer never saw the events at all.

Add two Host callbacks so a platform can take over notification rendering:

  bool Host::HasNativeAchievementNotifications()
  void Host::OnAchievementNotification(key, duration, title, message, badge_path)

When HasNativeAchievementNotifications() is true the shared core hands each
RA event (unlocks, mastery, leaderboard start/submit/scoreboard,
login, connect/disconnect, summary) to OnAchievementNotification and skips
ImGuiManager::InitializeFullscreenUI() entirely — in BeginLoadingScreen,
ClientLoadGameCallback, DisplayHardcoreDeferredMessage, and
SetHardcoreMode — so the invisible overlay and its render loop stay down.
The existing ImGui path is unchanged for desktop/Android, which return
false from the new callback. Every frontend (eerunner, gsrunner, libretro,
sdl, qt, android, test stub, macOS stubs) gets a no-op implementation; iOS
provides the real one, posting the notification to its SwiftUI toast layer
through ARMSX2_PostRetroAchievementsNotification. The notification now also
carries the configured display duration.

Also flesh out Achievements::GetCurrentUserStats / GetCurrentGameStats /
GetCurrentAchievementList, which were previously unimplemented stubs
returning false. The iOS bridge already wired these up to the
RetroAchievements panel; they now return the logged-in user's score, the
active game's unlock progress, and a bucket-ordered achievement list so the
native panel has real data instead of an empty state.

Stray RetroAchievements debug fprintf spam in the iOS bridge and overlay
defaults is dropped.
2026-07-24 12:15:28 +02:00
Brian Degenhardt 615502cc33 comments: drop the yaps2 name from the transplanted sources
Rewording only, no behaviour change. Four of these are contrast sentences that
a blind search-and-replace would have inverted into nonsense -- e.g. microVU's
"Unlike stock x86/ARMSX2 ... yaps2 inlined it here", where the ARMSX2 being
contrasted against is the pre-transplant line. Those now say "this backend" or
"the pre-transplant line" explicitly.

REFACTOR_STATUS.md keeps its two mentions: it is a dated changelog, and
rewriting it would falsify history.

Copyright headers are untouched.

recompiler_tests 1407/1407.
2026-07-21 21:47:45 -07:00
Brian DegenhardtandClaude 37bc5e1646 ee/fpu: make add/sub guard-bit emulation unconditional
The fpuGuardedAddSub Recompiler option (and its GameDB
clampModes.guardedAddSub override) is gone; both JITs now always mask
the smaller-exponent operand's guard bits on the single-precision
ADD/SUB fast path, matching the Full-mode DOUBLE path's unconditional
guard. Games like True Crime NYC and Jak 3 misrender without the
masking, and per-game flagging proved impractical to maintain — the
failures take cross-build diffing to even attribute. The |exp diff|<=1
early-out keeps the common case at a plain op.

Test-side, EnableFpuGuarded() is removed (the masked behavior is now
the default the guard-bit suite pins directly) and the
DefaultOffEmitsPlainOpMatchingInterp test goes away with the option.
recompiler_tests: 1389/1389 green.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 00:22:24 -07:00
Brian DegenhardtandClaude ea62853294 eerunner: --mkstate savestate bootstrapper for twindiff
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 23:45:03 -07:00
Brian DegenhardtandClaude 2ec02fab87 eerunner: twindiff cross-build twin-trace modes + --dump-config/--set
--twindump/--twincompare compare per-frame memhash/regfp/cycle
trajectories across two DIFFERENT builds of this runner (full-interp x2
control + full-JIT passes; interp cells force EE+VU0+VU1+IOP interp so
they are pure shared-C++). Built to triage the ARMSX2 jit-transplant
regressions against their pre-transplant master JIT, but generic: works
for any working-vs-broken pair sharing a savestate format (e.g. x86 vs
arm64 yaps2). --dump-config serializes the effective per-game EmuConfig
(GameDB applied) for cross-build config diffing; --set Section/Key=Value
gives base-layer overrides for A/B (e.g. fpuGuardedAddSub). Guarded with
EERUNNER_LEGACY_CORE so the same file compiles against pre-yaps2 cores.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 23:31:50 -07:00
Brian DegenhardtandClaude 5458d12ca1 eerunner: VU0-aware fingerprints, divergence census, frame-local RAM diff
Three stepdiff triage upgrades built during the Jak 3 artifact hunt:

- EERUNNER_VU0FP=1 mixes VU0 macro-visible state (VF00-31, ACC, the
  STATUS/MAC/CLIP flag VIs) into the divtrace fingerprint and captures it
  in FullSnap/DiffFullSnaps, so the zoom breaks at the EE block whose COP2
  macro emission first produced divergent VU0 state instead of only when
  it later lands in a GPR or memory (the default fingerprint surfaced the
  Jak 3 fMax vertex flood at an unrelated memcpy). VF is memory-resident
  at every block boundary per the COP2 VF-cache seam policy, so the JIT
  block-prologue sample site reads consistent state. Opt-in: the benign
  filters were tuned without VU0 fields, and mVU-vs-interp has by-design
  NaN/clamp corners.

- EERUNNER_CENSUS=1 stops the zoom hard-stopping at the first non-benign
  divergence: each data/control site is reported compactly, ResyncAfter
  walks past TRANSIENT ones (DMA-phase staging buffers, poll-phase
  control-flow splits that reconverge), and the walk stops at the first
  PERSISTENT site. Persistent control-flow sites also dump the JIT-side
  guest regs at the split entry (busy-poll operands), the polled word when
  it resolves to RAM, and both sides' frame-end SPR channel registers.

- The stepdiff candidate-real branch now re-runs the frame once per mode
  from the same checkpoint and byte-diffs EE RAM+scratch (ReportMemDiff,
  max_show now a parameter): with a clean interp control every differing
  byte is a real JIT store difference, at frame-local granularity.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 21:02:39 -07:00
Brian Degenhardt bc33ef1d09 arm64: attribute yaps2 authorship in SPDX copyright headers
Add "yaps2 Dev Team" copyright to the files we authored. Net-new files
(all 42 pcsx2/arm64/ codegen/ProgCache/persist sources, the recompiler
test suite + harness, and the vurunner/eerunner tools) never existed
upstream, so they carry yaps2 sole credit. RecStubs.cpp predates the
fork and was heavily extended, so it keeps PCSX2 credit and adds yaps2.

The five pre-existing arm64 files we only lightly touched (AsmHelpers,
Vif_Dynarec, Vif_UnpackNEON) stay PCSX2-only. GPL-3.0+ license lines are
unchanged throughout; this is authorship attribution only.
2026-07-19 07:37:15 -07:00
Brian DegenhardtandClaude Fable 5 71fdc2f03b SL-09: offline bisection tooling for EE superblock triage
The instrumentation that localized SL-07, kept for the next hunt:

eerunner stepdiff timer-taint classifier:
- Bound the taint walk at the observation pc: the offending block can end
  mid-disasm (e.g. at an ei), and walking on into the NEXT block's jr delay
  slot let an untainted overwrite erase taint that was live at the
  observation point (a v0 timer read classified as a real divergence).
- Seed the const-tracker with the interpreter's register file snapped at
  the offending block's entry (one extra frame re-run), so load addresses
  materialized in earlier blocks resolve - the software-virtual-clock
  accumulator shape (lui 0x1000 in one block, ori 0x800 in the next) was
  under-tainting. Every unmodeled GPR-writing op now clears known[] so a
  seeded value can never go stale into an over-taint.
- Widen the benign class from the four EE timer COUNTs to cycle-phase MMIO
  (0x1000xxxx hardware regs + GS privileged bank): DMAC CHCR tag fields
  etc. legitimately read back a different transfer phase under JIT-vs-
  interp cycle granularity. The ResyncAfter reconvergence gate remains the
  safety net against masking real bugs.

EE rec testing-only env knobs (no production effect when unset):
- YAPS2_EESB: bitmask of continuation-site families (bit0 BEQ/BNE, bit1
  BLEZ/BGTZ, bit2 REGIMM BLTZ/BGEZ; 0 = pre-superblock block formation).
- YAPS2_EESB_LO/HI: guest-pc window for sites - the binary search over
  this window is what pinned SL-07 to one branch at 0x3f84c8.
- YAPS2_EESB_DUMP=<hex pc>: disassemble any block covering that pc after
  finalize (needs INCLUDE_DISASSEMBLER; no-op otherwise).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 11:49:48 -07:00
Brian DegenhardtandClaude Fable 5 76dd819f70 eerunner: honor explicit --renderer null; add @THREADPERF@ per-thread counters
Two liverun harness fixes surfaced by the mq65 rig bring-up:

1. --liverun silently promoted --renderer null to VK unless --perf-jitdump
   was also set, so every codegen_ab "renderer=null" A/B to date actually
   ran Vulkan. Track whether --renderer was passed explicitly and honor an
   explicit null (pairs with the new deviceless GSDeviceNone). A defaulted
   Null still promotes to VK for the hang-repro use case.

2. @THREADPERF@: per-tid perf_event_open(instructions,cycles) attached
   after savestate load and reported at shutdown next to @THREADCPU@.
   Gives codegen A/Bs an EE-thread-SCOPED deterministic hardware metric:
   whole-process perf stat dilutes an EE-only delta by the GS/MTVU/worker
   threads' share (~50% on SotC), and the time-based @THREADCPU@ line is
   clock-sensitive. Falls back to user-only counting when
   perf_event_paranoid blocks kernel-inclusive counts (unprivileged dev
   boxes); devices run as root and count both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:55:15 -07:00
Brian DegenhardtandClaude Fable 5 b9d708f59c eerunner: gate --vu0diff COP2 hooks on PCSX2_RECOMPILER_TESTS
The g_cop2ReadHook/g_cop2StateHook externs only exist when
ENABLE_RECOMPILER_TEST_HOOKS=ON, but Main.cpp referenced them
unconditionally — production configs (build-rocknix, hooks OFF) failed
to link eerunner since the vu0diff feature landed. Gate the references
and fail --vu0diff loudly on hooks-OFF builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:55:14 -07:00
Brian DegenhardtandClaude Fable 5 8790266046 eerunner: print per-thread CPU time at liverun end (@THREADCPU@)
Wallclock A/B on the SD865 has a ±0.3s noise floor over a 600-frame run —
useless for sub-ms/frame codegen deltas. utime+stime per VM thread (parsed
from /proc/self/task/*/stat while the threads are still alive) isolates e.g.
GS-thread cost from GPU/sync/scheduler jitter; it resolved a 1% GS-thread
delta that wallclock could not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-01 23:32:13 -07:00
Brian DegenhardtandClaude Fable 5 62bcd8c4ec eerunner: GS diagnostics — HWDownloadMode/spin-readback env gates, per-frame GS stats
Three harness-only additions used to root-cause the OutRun 2006 readback
serialization on SD865 (follow the existing EERUNNER_* env-gate pattern):

- EERUNNER_HWDL=<0..4> forces EmuCore/GS HWDownloadMode. Unsynchronized (3)
  keeps CPU-side copy work but skips the fence wait, isolating the stall
  cost of synchronous GPU readbacks. 1-4 are not render-accurate; A/B only.
- EERUNNER_SPINCPU=1 sets HWSpinCPUForReadbacks (spin instead of sleeping
  on readback fences, cutting scheduler wake latency).
- --liverun now prints an @GSSTAT@ line at completion (per-frame draws,
  render passes, readbacks, texture copies/uploads from g_perfmon) so
  scripted device runs can attribute GS-side behavior without a profiler.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-01 22:51:11 -07:00
Brian DegenhardtandClaude Opus 4.8 a187fdc0b6 tools/perf: M2-first CPU profiling rig + runner --perf-jitdump
Step 0 of the `neither` cherry-pick funnel: a repeatable, attributable,
wallclock-anchored CPU bottleneck baseline for our own ARM64 port, since the
RK3562-era numbers are stale and the target has shifted to Snapdragon 865.
Built and validated on M2 Max / Asahi first; the same scripts run on SD865 with
a new devices/<label>.env and no code change.

Test-harness change (no production code path, no shipped env gate):

- pcsx2-eerunner --perf-jitdump: emit a Linux perf jitdump under EmuFolders::Cache
  so `perf inject --jit` resolves EE_/VU0_/VU1_/IOP_/VIF_ block symbols. The enable
  is driven through the EmuCore/Profiler EnablePerfDump config bool (ApplySettings ->
  LoadSettings re-applies Perf::SetJitDumpEnabled every apply, so a manual enable
  would be reset). SetJitDumpDir is set before the first block compiles. Also honors
  an explicit `--renderer null` under --perf-jitdump for the CPU-only diagnostic.
- pcsx2-vurunner --perf-jitdump: same flag for VU-only captures (manual enable; the
  runner doesn't go through ApplySettings).

Tooling (pure stdlib + bash):

- tools/perf/bucket_perf.py: parse a `perf report --stdio` dump into a PCSX2
  subsystem ranking. Buckets tuned against real M2 R&C UYA / Katamari captures:
  JIT-by-prefix (EE/VU0/VU1/IOP/VIF) + VU-glue (mVUlookupProg / dispatch envelope) +
  native VIF + GS (incl. GIF decode + XXH3) + a GPU-driver bucket that quarantines
  the host Asahi/Vulkan/DRM stack (host-specific, NOT an SD865 proxy) + JIT-other
  (unsymbolized continuation blocks) + startup/io + a visible `unattributed`.
- tools/perf/profile_run.sh: one-command wrapper (precondition gate -> perf record
  -> inject --jit -> single report dump -> bucket -> median wallclock + median
  per-bucket share -> summary.md). Device/scene parameterized.
- tools/perf/devices/m2max-asahi.env: the working M2 incantation (P-core PMU
  apple_avalanche_pmu/cycles/, -F 999).
- tools/perf/scenes/*.env: R&C UYA + Katamari cinematic/gameplay scenes (assets are
  copyrighted, not checked in; paths reflect this dev box).

Validated on M2: go/no-go #1 (perf+jitdump resolves 277 JIT symbols) and go/no-go #2
(per-bucket shares stable across 3 runs) both green. First finding: on M2 the `vk`
profile is dominated by the Asahi GPU stack (GS thread ~57% of samples) — the CPU
shape comes from `--renderer null`, where VU (bodies+glue) is the largest emulation
cost.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 12:40:31 -07:00
ff1abc36b8 arm64: SDL/kmsdrm frontend, headless runners, and handheld defaults (fork-only)
pcsx2-sdl (SDL3/kmsdrm handheld frontend), pcsx2-eerunner / pcsx2-vurunner headless
JIT regression+divergence tools, the gsrunner libmali CLI flags + Wayland scanner
wiring, and the heterogeneous-CPU thread-pinning default. Fork-only tooling/frontends.

Co-Authored-By: Ryan Walklin <ryan@testtoast.com>
Co-Authored-By: Brian Degenhardt <bmd@bmdhacks.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:27:56 -07:00