135 Commits
Author SHA1 Message Date
Brian Degenhardt 7f93a80dd7 PerformanceMetrics: count the GS back thread
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.
2026-07-30 21:55:58 -07:00
Brian Degenhardt 639b317dbc gsrunner: stop the Wayland message pump blocking past the shutdown flag
The pump polls the display fd with a 16 ms cap so it can re-test the shutdown
flag between polls, but on POLLIN it called wl_display_dispatch(), which reads
the queued events and then waits for more. A window nobody is drawing to gets
no further events, so the flag was never re-tested and the process never exited
-- gsrunner would print its whole stats block and then hang forever, leaving
every automated run to be killed by a timeout.

Switch to the non-blocking read sequence: prepare_read, flush, poll, then
read_events or cancel_read, then dispatch_pending. Nothing in the loop can
block now, so the cap does what its comment claims.
2026-07-30 21:55:58 -07:00
Brian Degenhardt 5da1d59568 gsrunner: capture GS dump frames with RenderDoc
Add -renderdoc <path> and -renderdoc-frame N[,C], writing one .rdc per selected
dump frame.

RenderDoc's own triggers cannot reach gsrunner on a Wayland session. It polls
only X11/xcb for the capture key, so PlatformHasKeyInput() is false and F12 is
never seen. Target control cannot drive it either: RenderDoc hooks only the core
EGL entry points, while GLContextEGL prefers eglGetPlatformDisplayEXT and
eglCreatePlatformWindowSurfaceEXT, so no native window is ever registered for the
surface and there is nothing to attach a capture to. The in-application API
sidesteps both -- StartFrameCapture(nullptr, nullptr) captures the active device
whatever the windowing system, and needs no keypress, so a headless dump replay
can capture unattended.

Captures open and close at present boundaries in Host::BeginPresentFrame(), which
runs on the GS thread with the frame's work submitted but not yet presented.

RenderDoc must already be in the process, since it installs its hooks from its
library constructor. A late dlopen hands back a working API whose hooks were
never installed and then captures nothing, so that case is refused with the
LD_PRELOAD command to use instead.

Verified on aarch64/Asahi against a God of War II dump. Vulkan surfaceless
produces a valid capture: 351 actions, 218 draws, 99 textures, render target
carrying real image data. Windowed Vulkan cannot work under RenderDoc's layer at
all, which does not advertise VK_KHR_wayland_surface. GL captures record correct
events and texture contents but replay with black render targets on Honeykrisp.

3rdparty/include/renderdoc_app.h is RenderDoc's MIT-licensed in-application API
header, vendored verbatim.
2026-07-25 09:43:57 -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 9a4bd0f56f GS tooling: report GPU device and driver identity in stats output
Both `gsctl stats` and `gsrunner -stats-json` reported counters with no
indication of which GPU or driver produced them, so a stats blob from a
tester was not attributable — and driver identity is the axis nearly every
mobile GPU behaviour turns on.

This matters beyond provenance. Several GS features are force-overridden
per-driver (framebuffer fetch and texture barriers are forced on for Adreno
regardless of INI), so without knowing the driver you cannot tell whether a
settings A/B was applied at all or silently compared a config against itself.

gsrunner captures the strings on the GS thread at first present rather than
at shutdown, where the device may already be gone. Both emitters escape the
values, since GetDriverInfo() is multi-line on Vulkan.
2026-07-22 21:16:45 -07:00
Brian Degenhardt fca9018dce GS: per-draw ledger (GSDrawLog)
We could see that a frame issued 900 draws and 40 barriers, but not which draws
were expensive or what PS2 state made them so. The existing per-draw facility,
GSHWDrawConfig::DumpConfig driven by SaveHWConfig, writes one text file per
draw: right for inspecting a single suspicious draw, wrong for profiling a
scene, since a heavy frame produces hundreds of files.

Records one append-only table instead, so a whole scene can be sorted by cost,
grouped by pixel format, or scanned for draws that forced a barrier, in one pass
with shell tools.

A row is assembled from two points in the draw, because the field sets do not
overlap. The PS2 view is live at the top of GSRendererHW::Draw -- primitive type
and count, FRAME/ZBUF/TEX0 addresses, formats and buffer widths, FBMSK, blend
equation, alpha test and DATE. The backend view only exists at submit, and
reuses the fields DumpConfig already knows: topology, barrier requirement,
tex_hazard, destination_alpha, colormask and drawarea. Draws that return before
submit still get a row, marked unsubmitted, since "which draws were skipped" is
itself a signal.

I/O discipline is the design. At ~900 draws/frame and 60 fps this is ~54k
rows/sec; formatting a row costs microseconds and writing it costs bandwidth,
and both would land on the GS thread -- the thread under investigation. So
capture stores a packed POD into a preallocated arena, with no formatting and no
I/O, and serialisation happens once afterwards. The arena is bounded to ~64
heavy frames, so a long session yields a contiguous prefix rather than an
unusable file; truncation is reported rather than silent.

Activity tracks GSConfig.DumpDrawLog directly rather than a config edge, so
recording works when the setting is already true at GS open. GSUpdateConfig
writes the CSV on the true->false edge, making a live capture "turn it on, play
the slow bit, turn it off" -- both edges drivable over PINE. gsrunner gets
-drawlog <path.csv>.

The stringifiers GetTopologyName/GetTexHazardName/GetDestinationAlphaModeName
were TU-local statics in GSDevice.cpp; exposed as GSGet* so the ledger names
enum fields instead of duplicating the tables.

Verified on a dump replay: row count matches @HWSTAT@ Draws exactly (277/277),
and with the frame limiter off, median-of-5 p50 frame time is unchanged (+0.00%,
distributions overlapping). Note the test scene is ~70 draws/frame rather than
the ~900 the arena is sized for.

This is an attribution tool, never a comparison tool: an A/B with the ledger
enabled on one arm is invalid.
2026-07-22 21:16:45 -07:00
Brian Degenhardt e0f1d4962a gsrunner: implement Host::RunOnCPUThread
It was a hard pxFailRel("Not implemented"), so any path that marshals work to
the CPU thread aborted the whole run. That is reachable from outside: with PINE
enabled, a settings apply, savestate or frame advance would kill gsrunner
outright rather than fail the command.

Queues the function and drains it in PumpMessagesOnCPUThread, which the replay
loop already calls. Tasks run outside the lock so a task that posts more work
cannot deadlock, and the blocking form waits for the queue to drain.

This also makes the PINE control surface usable against gsrunner, so a settings
sweep can be driven against deterministic dump replay rather than a live game.
2026-07-22 21:16:45 -07:00
Brian Degenhardt 1edbeb1fbb gsrunner: per-frame stats JSON, percentiles, and a generic -set override
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.
2026-07-22 21:16:45 -07:00
Brian Degenhardt 616e5900f3 Merge yaps2/main: GV7 GS front/back thread split (17 commits)
Brings the complete GV7 campaign: GSBackQueue SPSC record ring, draw/
transfer/PCRTC/vsync records with inline executors, the GSFrontState
two-object front split, lockstep + pipelined back-thread modes (default
Off), mid-frame MTGS-thread drain seams, back-thread affinity fix, and
the Qt/Big Picture GSBackThreadMode settings UI.

Conflict resolutions:
- pcsx2/GS/GS.cpp: adjacent additions unioned (Android Tekken 5 Mali
  override + GV7 g_gs_front/GSParseTarget). GSAllocateWrappedMemory
  unioned semantically: keeps the iOS-safe HostSys::CreateSharedMemory
  routing AND drops the static fd so two wrapped allocations coexist
  (the GV7 two-object split runs two GSStates, each with a wrapped vm);
  CreateSharedMemory already unlinks/memfds the name and the iOS
  file-backed fallback O_EXCL-retries per-attempt paths, so coexisting
  allocations cannot collide.
- GraphicsAdvancedSettingsTab.ui: take gsBackThreadMode tabstop; drop
  yaps2's stale "rov" tabstop (no such widget in either tree).
- FullscreenUI_Settings.cpp: GS Back Thread setting inserted outside the
  ARMSX2 !__APPLE__ guard around exclusive fullscreen.

Audit: ARMSX2 one-liners in GV7-rewritten files survived
(GSClut CreateFeedbackTarget, GSState SaveTransferImages); ARMSX2's
Merge()-path additions (RetroArch shader chain, FastMAD fallback) run
post-drain on the vsync path, so no unaudited device seams.

Gates on merged tree: recompiler_tests 1359/1359, gs_vertex_tests 21/21
(incl. new gs_backqueue suite), full build incl. armsx2-qt.
2026-07-19 16:19:06 -07:00
Brian DegenhardtandClaude 370fe6e29a GV7-1b: GSBackThreadMode config + record-off fast path
Add the GV-7 mode ladder as a restart-required GS option
(EmuCore/GS GSBackThreadMode: 0=off, 1=inline-records, 2=lockstep,
3=pipelined; default 0). Restart-required means the mode can never
change under a live GSState, so it's sampled once at construction.

Off now skips the DRAW record round-trip entirely: every field the
record carries is captured from live state and installed back over the
same live state, an identity — FlushPrim calls the executor tail
directly instead. ExecDrawRecord splits into the install block +
DrawRecordTail(draw_serial), which is the shared tail for both paths
(and closes the ~2 env memcpys/draw the GV7-0d inline path was paying;
GV7-3's bool-off profile verifies against the GV-6b baseline).

Mode >= 1 keeps the GV7-0 build+execute-inline shape (modes 2/3 fall
back to it until the thread lands). gsrunner grows -backthread <mode>
so the gate matrix can pin both rungs.

Gates: gs_vertex_tests 21/21; gsrunner PNG hashes bit-identical to
GV-0 baselines on all 10 dumps, vk + sw, in BOTH mode 0 and mode 1
(record path confirmed active via the new startup log line).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 12:45:24 -07:00
Brian Degenhardt 3e077eff9b Merge yaps2: arm64 JIT transplant + test/perf/libretro infrastructure
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.
2026-07-19 10:24:29 -07:00
Brian DegenhardtandClaude Opus 4.8 4c9ada053e gsrunner: add -accblend <0-5> to force accurate blending unit
Adds a test-only CLI flag to pcsx2-gsrunner that overrides the
EmuCore/GS accurate_blending_unit setting, so the SW-blend / fb-fetch
(ROV) feedback path can be exercised headlessly during GS-dump replay.
The game/global default is often Basic, which rarely activates the ROV
path, so a replayed dump reports zero @HWSTAT@ Draws-Calls-(ROV) even
for titles that show fb-fetch artifacts in real gameplay. -accblend
lets a dump be sieved at High/Maximum to find the buggy path.

Test tooling only; no effect on the emulator core.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 15:55:14 -07:00
TJnotJTandTellowKrinkle d1cd5a62ed GS/HW: ROV revisions.
Use GSTexture::FeedbackTarget RTs by default (not GSTexture::ShaderWriteTarget).
Do ROV type conversion for both color and depth (if needed).
Make sure the formats for ROV actually support UAV/storage image.
Change 'PSSetUnorderedAccess' to 'PSSetROVs' for clarity.
DX12: Use enum names instead of raw constants in PSSetShaderResource().
Some string format cleanup to GL_*() macros.
VK: Do not enable ROV if FB fetch is available.
Other misc cosmetic changes such as whitespace, etc.

Co-authored-by: TellowKrinkle
2026-06-28 12:13:38 -04: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
SternXD b5ed65ddd6 Qt/ImGui: Add clipboard support and fix modfier keys
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 15:02:57 -04:00
SternXD d2cb0aaf63 GSRunner: Remove noops that aren't needed
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 14:53:56 -04:00
TJnotJT bbcd5ec25e GS/HW: ROV support. 2026-06-03 03:26:24 +02:00
TJnotJT 3d71e310c0 GSRunner: Add -debugdevice command line parameter. 2026-04-30 02:21:35 +02:00
TellowKrinkle 3450e3c238 Revert "Core: Reserve memory map as early as possible"
This reverts commit 337daf7ed9.

It's no longer needed
2026-03-28 17:49:17 -04:00
TJnotJT 8a46f9e6ca GSRunner: Disable framerate limit in runner and dump frame timing information. 2026-03-12 18:46:47 +01:00
SternXD fe919d18b2 GSRunner: Fix gsrunner ImGui fonts and host stubs 2026-03-11 12:44:05 +01:00
TellowKrinkle 337daf7ed9 Core: Reserve memory map as early as possible
Protects against all the other stuff we load eating up all the address space that we want
2026-03-07 09:12:00 -05:00
Alexei Doell b7d2b1a26c Qt: Add hotkey to toggle mouse lock 2026-02-24 11:55:09 -05:00
TJnotJT 24090772cd GS/HW: Add option for dumping HW config data.
Dumps HW config per draw.

Helps debugging/optimizing HW pipeline changes.
2026-02-18 22:25:54 +01:00
lightningterror 84dc2959c5 GSDumpRunner: Use utf-8 encoding for opening files.
Allows the dump compare to work normally with unique characters.
2026-01-29 14:17:02 +01:00