SwanStation (libretro)¶
PlayStation. Measured on the Retroid Pocket Nova; #495 holds the discussion.
What runs¶
- Package
swanstation-lr, libretro/swanstation atb6c30a7, under RetroArch. - Patches:
001: an NTSC line is 3412.5 ticks.002: frontend run-ahead states keep VRAM on the GPU (see Pacing).- Renderer Vulkan, resolution scale 4x, from
retroarch-core-options.cfg.
Presentation¶
The 119.652 Hz mode, two swapchain images, swap interval 2: one present a
frame, 16.715 ms apart, timed by the driver (RetroArch patch 0014). Integer
scaling for this core only (SwanStation.cfg): with overscan cropping the
active area is 320x240, so 4x fills the 1280x960 panel exactly.
Pacing¶
Without a preemptive frame the core holds every frame on its vblank (E3 in the test plan).
Preemptive frames (PRMPT in Settings > Consoles, psx.preempt=1, off by
default). RetroArch saves a state every frame and, when the input changed,
loads it back, runs the core twice and saves again. SwanStation's
retro_serialize took the full savestate path every time. Its VRAM readback
downsamples the 4x VRAM to native and then waits for the GPU to finish
everything queued, the frame's 4x rendering included. On a load, the native
copy is uploaded again. So the emulation thread sat idle at 13-30% of a
core while frames missed their vblank, and a faster GPU only shortened the
wait.
Patch 002 uses what DuckStation does for its own run-ahead. For states
taken under RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE, VRAM is copied
GPU to GPU, into a texture per frontend buffer. The CPU never waits, and the
4x content survives the rollback. Normal savestates are unchanged. The fast
path is off while "software renderer for readbacks" is on, because that
renderer keeps a second VRAM in CPU memory.
With the patch the core runs twice on a press, so its thread gets busier
(25-36%). Under schedutil its cluster often sat at 500-1000 MHz, so
runemu.sh sets performance on the CPUs and the GPU while PRMPT is on
for the PlayStation. Without the preemptive frame the defaults hold every
frame, so nothing changes there.
CPU and core mapping¶
One hot thread, RetroArch's main thread running the core. It runs in
game.slice on cores 3-7 like every emulator (CPU_ISOLATION.md), with the
A510s left to the system. At 13-36% of a core it does not need its own core,
so there is no pinning beyond that.
Settings and why¶
| Setting | Where | Why |
|---|---|---|
| Vulkan, 4x | retroarch-core-options.cfg |
the upscale; costs nothing measurable here |
video_scale_integer = "true" |
SwanStation.cfg |
320x240 at 4x is the panel exactly |
run_ahead_enabled = "false" |
SwanStation.cfg |
second-instance run-ahead would need a second Vulkan context; preemptive frames are the other flag |
audio_out_rate = "44100" |
SwanStation.cfg |
the SPU's rate; no resampler in the path |
psx.preempt=0 |
system.cfg |
off by default; optional PRMPT raises CPU/GPU clocks while enabled |
performance CPU and GPU with PRMPT on |
runemu.sh |
see Pacing |
swanstation_CDROM_PreCacheCHD = "true" |
core options | the CHD stays in RAM, compressed: no SD card read mid-game; a slow card showed as audio and frame dips that this removed (user report, 2026-10-18) |
Measurements¶
Tekken 3, fights with buttons mashed, MangoHud frametimes. Missed means a frame at or above 25.07 ms, the target plus one refresh.
| Date | Build | Change | fps | Missed | p99 |
|---|---|---|---|---|---|
| 2026-09-28 | fd20d52 |
no preemptive frame (E3 reference) | 59.8 | 0 | 19.70 ms |
| 2026-10-03 | a694ce5 |
PRMPT, unpatched, schedutil, GPU 401 | 53.9 | 8.3/s | 30.7 ms |
| 2026-10-03 | a694ce5 |
PRMPT, unpatched, performance, GPU 401 | 53.3 | 10.1/s | 29.3 ms |
| 2026-10-03 | a694ce5 |
PRMPT, unpatched, performance, GPU 680 | 59.0 | 8.7/s | 27.8 ms |
| 2026-10-03 | a694ce5 + patch 002 |
PRMPT, schedutil, GPU 401 | 59.6 | 3.8/s | 26.7 ms |
| 2026-10-03 | a694ce5 + patch 002 |
PRMPT, performance, GPU 401 | 59.8 | 2.2/s | 25.7 ms |
| 2026-10-03 | a694ce5 + patch 002 |
PRMPT, performance, GPU 680 | 59.8 | 0.5/s | 24.96 ms |
The patched rows used a core built in a Debian container (gcc 14, no LTO), copied over the shipped one. No visual glitches were seen on a rollback. Each run's phases were consecutive in one session, so how hard the buttons were mashed differs a little between rows.
Open questions¶
- The last 0.5 missed vblanks a second, against none without PRMPT.
- The GPU at maximum without
performanceon the CPUs was not measured with the patch, so the CPU half may not be needed.