Under GSBackThreadMode >= Lockstep roughly half the GS work moves to a second
thread, and every surface that reports GS cost -- OSD, PerfLog, the Qt status
bar, PINE stats, gsrunner's @HWSTAT@ block -- measured the MTGS thread alone.
So the split read as a large GS saving. It is not: on a Rogue Galaxy savestate
here, mode 0 costs 15.8% / 2.63 ms and mode 3 costs 17.0% / 2.84 ms plus
14.2% / 2.37 ms on the back thread -- about twice the total GS CPU time, bought
to halve the critical path. That is a real trade, but nobody could see it.
The back thread registers its own handle at entry, as the SW rasterizer workers
do; StopBackThread clears it after the join. Unlike every other handle here it
is written by a thread other than the one sampling it, so the handle and its
running total sit behind a mutex taken twice a second. Installing a handle
rebases the total off it, so the first window after a GSreopen respawn measures
the new thread rather than its difference against the retired one's.
The figure is omitted, not reported as zero, wherever a back thread does not
exist -- otherwise a mode 0 vs mode 3 comparison reads a permanent 0% as
meaningful. gsrunner latches the presence flag during the run because DumpStats
executes after VMManager::Shutdown, by which point the thread has joined.
Rendering
- Auto renderer now resolves to Vulkan HW on Adreno (OpenGL elsewhere).
- Mobile hardware ROV (Phase 0): tile-native depth feedback behind the ROV toggle.
Performance & input
- Low Latency frame pacing is the default on capable devices, with a one-time
migration for existing installs; low-end devices keep the queued pacing.
- Reduce Android input latency and improve input handling (PR #403, Splaser).
- Experimental CPU clock hint (ADPF) toggle in Performance settings (default off).
Audio & UI
- Pop-up open/close sound cues (info, hardcore confirm, patches & cheats).
- Alternating controller navigation / slider tick sounds.
RetroAchievements
- Inject the RA client version from a build-time secret kept out of public source,
with a stock-PCSX2 fallback (no hardcore) for secret-less builds. Applies to the
iOS client token too. Prevents third parties from copying our User-Agent.
Game compatibility
- Delta Force: Black Hawk Down (SLUS-21124 / SLES-53299) GameDB fixes
(PR #401, XDarkFallenX).
gsrunner's textual output was run-aggregate only: totals for the whole run plus
min/avg/max frame time. A single spiky frame was invisible, and nothing was
machine-readable. The -perf block is also useless on short runs, because
PerformanceMetrics only updates about once a second -- an 8-frame replay reports
nan/inf for every field.
-stats-json <path> writes a per-frame series plus a run summary. Counters are
exact per-frame deltas (update_stat now returns the delta it accumulates, so the
series and the totals stay derived from one source), and frame_ms is measured
directly rather than taken from PerformanceMetrics' window averages. Adds
PerformanceMetrics::GetLastGPUTime for the same reason: OnGPUPresent already
receives a per-frame GPU time but only accumulated it, and an averaged value
hides the spike we are looking for.
Percentiles (p50/p95/p99) and the worst-frame index are reported over drawn
frames only; idle frames are present-only and would drag the distribution down.
-set <Section/Key>=<value> overrides any setting generically. Every experiment
previously needed a bespoke flag -- -accblend, -no-fb-fetch, -no-tex-barriers,
-no-vs-expand, -backthread -- which is a flag per question and makes a sweep
driver impossible. Verified equivalent: -accblend 3 and
-set EmuCore/GS/accurate_blending_unit=3 produce identical counters, and the
override demonstrably bites (accblend 0 -> 4 barriers, accblend 5 -> 365).
Also prints the previously-accumulated-but-never-printed PS2-level draw count,
adds prim accounting, and surfaces the new texture-cache hit/miss counters.
Merges yaps2/main (github.com/yaps2/yaps2, c16b88cb7) into ARMSX2,
replacing the arm64 recompiler family with the yaps2 JITs and importing
the yaps2 testing, perf, and libretro infrastructure. Common ancestor is
upstream PCSX2 342db5152 (2026-06-19); git auto-merged all but 38 files.
Replaced (deleted in this merge, recoverable from history):
- arm64/aR5900*, aR3000A*, aVU* -> arm64/iR5900*/iR3000A*/microVU*-arm64:
EE static-pin register file with lazy dirty tracking, dual-residence
allocator, IOP block linking, native COP2 macro ops, inline unaligned
fastmem, persisted VU program cache, call-ret shadow ring, VU0 spin
fast-forward.
- MVU_DIFF shadow-run hooks in shared VU interpreter TUs (superseded by
the offline vurunner JIT-vs-interp oracle).
Imported from yaps2:
- tests/ctest/core/recompilers: ~80 gtest suites (EE/IOP/VU differential
harnesses, fuzzers, ABI digest tripwire, capture format pins) plus the
gs_vertex_tests kernel oracle.
- pcsx2-vurunner / pcsx2-eerunner headless capture-replay runners.
- tools/perf counter-based A/B rigs, perf jitdump productionization,
PmuCounters, clang-perf/clang-handheld presets.
- pcsx2-libretro core (ENABLE_LIBRETRO, default OFF; rename pending).
- GS vertex-kick fast path (GV series): TBL-based packed parse,
register-resident kick, scalar-outcode cull, fused draw-rect/FindMinMax.
- Null renderer, VK_KHR_display direct WSI, swapchain PresentStats.
- SPU2 NEON mixer vectorization, EE timer read clamp (NFL 2K5 hang),
IOP ioman signed-compare fix, assorted UB fixes.
Kept from ARMSX2 in the both-touched files:
- iOS dual-map W^X and fastmem-unavailable resilience (Memory, HostSys,
vtlb). The split data/code area model is retained; both areas now take
fixed VA hints so cached VU JIT code stays deterministic on Linux.
- Android thread-affinity model, VMState shutdown early-outs, all
platform frontends, branding, CI, RetroAchievements identity/policy.
- GSDeviceVK: ARMSX2's push-descriptor decision logic (Mali crash gate,
proprietary-vs-turnip Adreno split) merged with yaps2's descriptor-pool
exhaustion recovery (flush + render-pass restart instead of dropped
binds). Vendor feature policy is the union: Mali fbfetch policy with
MediaTek/G57/Xclipse gates from ARMSX2; Adreno stencil/ROV/
test-and-sample-depth hang avoidance and no_ps2_z_quantization from
yaps2.
Build-system notes:
- The Qt debugger is now gated behind ENABLE_QT_DEBUGGER (default off on
arm64) so handheld builds drop the KDDockWidgets dependency.
- GSDeviceNone and remaining yaps2 GS code were ported to the newer
upstream GSTexture Usage-flags API.
The replaced backend's interpreter-fallback glue (intExecuteOneInst,
AndroidEEOpHist) and the EEDiffVerify runtime differ are retained for
now; dead pieces will be removed in a follow-up commit.
Every -logfile / stdout-captured run now leaves a durable framerate
record: a "PerfLog:" line every ~30s (rolling fps + EE/GS/VU/GPU thread
utilization + frame number, so warmup separates from steady state) and a
"PerfLog session:" whole-session average at VM shutdown. Coarse
regression signal for A/B and capture runs on device; the rigorous gate
remains perf stat instructions+cycles with pinned clocks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>