Batocera tuning
Agent skills that teach coding agents to operate a Batocera arcade cabinet: ops, ROMs, display, tuning, maintenance. Claude Code plugin.
npx -y skills add t3chnaztea/batocera-skills --skill batocera-tuningAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when a Batocera cabinet feels slow, laggy, or stuttery, or when tuning performance: EmulationStation menu/carousel lag, a slow-since-boot frontend, CPU governor stuck in powersave, overclock or CPU-speed core options that seem to do nothing, run-ahead / input latency, framerate that drops the longer the machine runs, or a 3D-heavy game (PSX/PS2/Dreamcast/N64 class) that crashes or stutters — Vulkan errors in emulator logs, picking a rendering resolution or graphics backend the hardware can afford. Covers the diagnostic order (check the throttle first), the v43 governor-at-boot failure, the emulator-name vs rom-dir-name overclock scoping trap, ES event-hook frame cost, and hardware-tier detection (batocera-info / batocera-vulkan). Not for shader visual quality (batocera-display).
SKILL.md
9.2 KB, as published. Nobody here has run it
Batocera Tuning
Performance: why the frontend or a game runs slow, and how to fix it. Assumes
the connection pattern and batocera.conf model from batocera-ops. The theme
throughout: diagnose the machine state before blaming config, themes, or
gamelists. Most "it got slow" reports are a throttle or a leak, not the thing
the user suspects.
Diagnostic order for "the frontend is slow"
Work down this list; each step is one command and rules out a whole class:
- CPU governor. A frontend that is laggy from cold boot and stays laggy
through a reboot is almost always the CPU stuck in
powersave:
If it saysSSHB "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"powersavedespitesystem.cpu.governor=performance, see the governor fix below. This is step 1 because it's the most common cause and the cheapest check. - ES event hooks. Lag that gets progressively worse as you navigate, and
is cured only by an ES restart, points at a slow/leaky ES event hook. ES runs
every executable in its scripts dirs on menu events — a
system-selectedhook fires on every carousel tile change and can add permanent per-frame cost. Audit/userdata/system/scripts/and/userdata/system/configs/emulationstation/scripts/. Because hooks are global (not theme-scoped), this masquerades as a "theme-independent ES bug" — the tell is that it accumulates per tile visited. See below. - Texture cache. Lag that's fine at first and worsens with a large media set,
with revisits staying fast, can be ES texture-cache eviction thrash. Raising
MaxVRAMines_settings.cfg(e.g. to 1024) can help, but measure — on some builds the GPU floor and MaxVRAM make no difference and the real cause is #1 or #2. - GPU clock floor. Only chase this after 1–3. On a vsync-paced frontend the GPU often won't upclock on its own, but pinning a higher floor frequently makes no measurable difference — A/B it before persisting anything.
Measure, don't assume: a framerate overlay (DrawFramerate=true in
es_settings.cfg) plus the batocera-ops screenshot loop lets you read fps on
a specific tile remotely. Turn it back off when done.
Do not drive these tests with synthetic input (see batocera-ops doctrine).
Ask the human to navigate, and read results from logs / screenshots / /proc.
Also note: a governor re-assert (below) may fire ~60–90s after boot, so the first
minute can feel slow even when fixed — don't let anyone judge inside that window.
The v43 governor-at-boot failure
On some v43 builds, system.cpu.governor=performance fails to apply at every
boot: the init script (/etc/init.d/S18governor) runs but its sysfs write
silently fails (redirected to /dev/null), leaving intel_pstate's default
powersave (~1.5 GHz vs the chip's ~3.4 GHz). A reboot does not fix it — every
boot lands in powersave.
Fix: re-assert the governor from a boot service after userspace is up, and
periodically thereafter. In /userdata/system/services/custom_service (the v43
replacement for custom.sh; see batocera-maintenance), add a loop that runs
/etc/init.d/S18governor start ~60s after boot and then hourly. Verify across a
reboot:
SSHB "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" # after ~90s uptime
Bonus hazard covered by the same hourly re-assert: batocera-shutdown's
fake-suspend can save the governor state such that a repeated fake-suspend strands
the machine in powersave permanently. The periodic re-assert covers that too.
The overclock / CPU-speed scoping trap
Overclock and CPU-speed core options are keyed on the emulator/core context,
which is not always the rom directory you think you're affecting. Example: a
fbneo.fbneo-cpu-speed-adjust=200% key applies to launches in the fbneo core
context — it does not reach games launched out of a different rom directory
(e.g. neogeo) even though they run on the same underlying core. If Neo Geo games
still run at 100% after you "set the fbneo overclock," that's why: they need a
neogeo.<key> of their own.
To find the right scope, launch the game and read
/userdata/system/logs/es_launch_stdout.log for the system/emulator/core it
actually used, then key the override on that name. And remember the .opt
exception (batocera-ops → batocera-conf): a per-game .opt replaces global
core options, so a game with an .opt takes its CPU value from inside the .opt,
not from batocera.conf — set it there for that game.
ES event-hook frame cost
Expanding on step 2: an installed ES event hook (say, a home-automation
integration that pings a server on system-selected) executes on every carousel
move. Even a fast-looking script can leave a couple of milliseconds of permanent
per-frame cost that accumulates per tile visited until ES is restarted, dragging
a 60 fps menu down to single digits after a while. Diagnosing it:
- Symptom shape: per-visit accumulation (revisiting two tiles 50 times degrades as much as visiting 50 different tiles), cured only by ES restart.
- Confirm with ES debug logging (
es.customsargs=--debuginbatocera.conf, picked up on a plainkill -9restart of ES, no reboot needed): you'll see the hookfireEvent → executing:line on every tile change. - Fix: remove the hook. If you want the integration, poll from the other side instead of installing an ES event hook. Trap: some agents/services rewrite their hooks on every boot/start — remove the service/agent that installs them, not just the hook files, or they reappear next boot.
Tuning 3D-heavy games (PSX, PS2, Dreamcast, N64 class)
Graphics tuning only pays off on 3D systems whose emulators expose rendering levers. 8/16-bit libretro systems render native-res for pennies — they have no resolution or backend knobs worth touching, and "tuning" them means frameskip or the overclock keys above. Don't burn a session tuning a 2D system.
Know the hardware before proposing values. One command each:
SSHB "batocera-info" # CPU model/cores, RAM, display resolution
SSHB "batocera-vulkan hasVulkan; batocera-vulkan hasDiscrete; batocera-vulkan listDevices"
An iGPU-only box (hasDiscrete false) and a discrete-GPU box want different
defaults; find out which you're on before copying settings from a forum post.
The two levers, in order:
- Internal resolution scale — the dominant cost knob on 3D emulators
(
duckstation_resolution_scale,pcsx2_resolution,flycast_render_resolution, …). Each step multiplies fill-rate cost; on an iGPU, 1x-2x native is realistic and forum-copied 4x+ is not. - Graphics backend — Vulkan vs OpenGL. On Intel iGPUs Vulkan is a common stability problem, not just a performance choice (see the table below).
Discover the exact key names and accepted values per emulator in
es_features.cfg (batocera-ops → batocera-conf reference) rather than
guessing — values can be opaque enums (PCSX2 OpenGL is
ps2.pcsx2_gfxbackend=12).
Crash and slowdown patterns in the emulator logs under
/userdata/system/logs/ (read es_launch_stdout.log first to see which
emulator/core actually ran):
| Log pattern | Likely cause | First fix |
|---|---|---|
VK_ERROR_*, vkQueueSubmit failure, device lost | Vulkan instability, esp. on Intel iGPUs | Switch that emulator's backend to OpenGL |
std::bad_alloc, OOM-killer hit in dmesg | Resolution scale / texture cost beyond memory | Drop resolution scale one step |
| Slowdown or audio crackle, no crash | GPU fill-rate saturated | Drop resolution scale before reaching for frameskip |
Scope fixes per game where one heavy title is the problem
(psx["Game (USA).chd"].duckstation_gfxbackend=…) rather than degrading the
whole system for it.
Run-ahead and input latency
Run-ahead hides emulation input latency by simulating N frames ahead each frame —
great feel, but it multiplies CPU cost (each run-ahead frame is a full re-emulate)
and needs a core that supports save-states. Enable modestly
(<system>.retroarch.run_ahead_frames=1) on systems where the CPU has headroom;
back off if it introduces audio crackle or drops frames. On a weak cabinet iGPU,
prefer 1 frame and reserve it for the systems where latency matters most (fighters,
shmups) rather than globally.