- [quality.*] accepts only the supported keys (quality_key_supported): twelve player-editable ones (masked, copied, classified, applied) and nine preset-only ones (no settings.toml form; applied on select). Any other key, e.g. vsync or renderer, is a load error. - Picking a preset now also applies bloom, texture LOD, anisotropy, FMV chroma smoothing and dithering (quality_apply_video_globals). - The frontends flag Custom on any tracked row; the host counts only rows of keys the title governs (psxq::launcher_custom_edit), so Antialiasing no longer turns Ultra into Custom when no preset sets it. Tests cover the rejection, every player key's masking/copy, and the classification cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 KiB
Graphics presets and hardware detection
A title can offer named graphics presets, Low, Medium, High and Ultra, the way PC games do. On first launch psxrecomp detects the machine and picks one, saves the pick, and shows it in Settings → Display → Graphics preset together with what it detected and a Re-detect button. Change any setting a preset sets and the preset becomes Custom. psxrecomp never overrides a Custom setup on its own. Dynamic resolution stays on in every preset as the safety net under the preset.
A title with no [quality.*] tables gets none of this, and nothing changes.
Declaring presets (game.toml)
[video] # the shipped look; usually Ultra
supersample = 1.5
frame_generation = true
dynamic_resolution_min = "display"
[quality.ultra] # empty: exactly [video]
[quality.high]
supersample = 1.25
[quality.medium]
supersample = 1.0
dynamic_resolution_min = "720p"
[quality.low]
supersample = 1.0
dynamic_resolution_min = "native"
frame_generation = false
pgxp_depth_buffer = false # PGXP itself stays on (a mod switch)
pgxp_color_correction = false
pgxp_seam = "off"
Each [quality.<name>] table holds [video] keys from the supported set
below. The preset is the whole game.toml re-parsed with those keys laid over
[video], so each key gets its usual [video] validation. The names are
low, medium, high and ultra. Any subset may be offered. Any other name,
a table value, an invalid value, or a key outside the supported set is a load
error. The keys a title's presets set, taken together, are the keys presets
govern.
Supported keys
Every key in the set is fully owned by the preset system. A preset in force masks the player's saved value. A one-run override, or an edit that touches no governed key, keeps the value on disk. A launcher row or live in-game change to a governed key is a governed edit and makes the state Custom. Picking a preset applies the key at once on the boot launcher, and at the next launch from the soft-return launcher.
| Kind | Keys |
|---|---|
| Player-editable (settings.toml form, launcher row) | texture_filtering, fmv_filter, antialiasing, dynamic_resolution, dynamic_resolution_min, frame_generation, render_thread, present_thread, geometry_correction, perspective_texturing, frame_interpolation, frame_interpolation_fps |
| Preset-only (no settings.toml form, no row) | supersample, texture_lod, anisotropic_filtering, fmv_chroma_smoothing, bloom, dithering, pgxp_depth_buffer, pgxp_color_correction, pgxp_seam |
Other [video] keys (vsync, renderer, scanlines, internal_resolution,
supersampling, ...) are rejected in a preset: they stay the player's.
PSXRecompV4::quality_key_supported is the list; extending it means adding
masking, copying, edit classification and apply-on-select for the key.
Detection
quality_probe.c reads OS facts only on every launch: the CPU brand, logical
thread count and RAM, the GPU name on Windows (primary display device) and
macOS (Apple silicon names the GPU after the chip), the GPU's PCI id on Linux,
and Steam Deck DMI (Valve / Jupiter or Galileo) or SteamDeck=1. These
facts hash to the hardware fingerprint. When a detection is due, a hidden
1×1 OpenGL window reads GL_RENDERER: the GPU OpenGL actually renders on,
which is not always the adapter the OS names (a desktop whose display hangs off
the CPU's iGPU while a discrete card sits idle, or a hybrid laptop). The OS
name is the fallback when the probe fails, and the probe is skipped under the
dummy/offscreen SDL video drivers. The context-creation log line names the
renderer too.
quality_tier.c maps the facts to a tier. It is a pure function, unit-tested in
recompiler/tests/quality_presets_test.cpp against a table of real
GL_RENDERER strings from Windows drivers, Mesa and macOS.
What it is, and what it is not. Autodetect is a static heuristic over the GPU name, the CPU thread count and the memory size. It measures nothing: there is no benchmark, and a name says nothing about clocks, thermals or the driver. It is a first-launch starting point, and the dynamic systems (dynamic resolution down to the title's floor, Smooth motion's in-between frames made from surplus only, the adaptive aspect) are what actually keep the frame rate.
| Class | Detected |
|---|---|
| Software GL, Steam Deck (Van Gogh), mobile GPUs, every integrated GPU up to the Radeon 890M / Iris Xe / Arc iGPU class, Apple M1 and M2 base chips, GeForce MX/GT | Low |
| A GPU the rules do not recognise (including Linux with only an AMD PCI id, and unnamed nouveau chips) | Medium |
| Recognised capable GPUs: discrete RX/RTX/GTX/Arc A/B cards, Apple M3 and later, any Pro/Max/Ultra chip, Strix Halo (8050S/8060S) | Ultra |
Fewer than 5 CPU threads, or under 6 GB of memory, also gives Low. High is a manual choice. If the detected tier is not offered, the highest offered tier below it is used, or failing that the lowest one above it.
Why unknown is Medium. Nothing about an unrecognised GPU is known, so the fallback must not assume Ultra's extra work (supersampling above the display, and whatever a title adds there, such as R4's mipmaps, 16x aniso and bloom). Low would also be wrong for what is usually new hardware: it gives up Smooth motion and the PGXP extras on machines that can almost always run them. Medium keeps the display's own resolution with dynamic resolution under it, which is the safe middle. A player can pick any preset at any time.
On Linux, when the GL probe fails, the sysfs PCI id is used instead (Steam Deck parts, Intel integrated vs Arc, NVIDIA).
When detection runs, and what is saved
settings.toml [video] keeps the following:
| Key | Meaning |
|---|---|
quality_preset |
low..ultra, or custom |
quality_base |
the preset a custom started from (its values for keys with no launcher row, such as supersample) |
quality_hardware |
the fingerprint the last detection ran on |
quality_detected |
what that detection picked |
Detection runs on first launch (no quality_preset), whenever the fingerprint
changes while a preset (not Custom) is in force, and on Re-detect. It never
runs under --headless or --hidden-window (CI and scripted runs): a saved
preset applies there, or else the title's [video] block, and nothing is
written. A detection is saved only when the GL probe succeeded; a result from
the OS name alone is used for that run and the next launch probes again. A new
machine under Custom is only reported:
psxrecomp: new hardware since your graphics settings were made; your Custom settings are kept
While a preset is in force it owns its keys. The player's saved settings.toml
values for those keys are ignored, so a launcher save of the preset's own values
cannot pin them. Under Custom the player's saved values apply on top of
quality_base. Keys a preset does not govern always stay the player's.
PSX_QUALITY=low|medium|high|ultra picks a preset for one run and is never
saved. PSX_QUALITY_REDETECT=1 forces a detection.
Who owns a saved key: one transaction
Every settings.toml save that can carry governed keys goes through
psxq::settle_save (quality_presets.cpp): the boot launcher (Play and Quit),
the soft-return launcher, and the in-game menu's live video settings. The
frontend only reports what the player did (below); the host decides what is
written, against settings.toml as it is on disk at that moment:
| This run | Player did | Saved |
|---|---|---|
PSX_QUALITY override |
anything | the governed keys and quality_* keys exactly as on disk: the previous preset, or Custom and its values, survive. A governed edit applies to this run only. |
| a preset in force | picked another preset | the picked preset; its keys stay as on disk (it owns them) |
| a preset in force | changed a governed value (launcher row, or a live in-game change) | Custom, from the preset in force, with the edited values; the next launch keeps them |
| Custom | anything | Custom, with the edited values |
| a preset in force | changed only keys no preset governs | the preset, unchanged; those keys are saved |
This holds when a launcher builds its save from an empty seed (the boot
launcher does): governed values the player did not choose are never written.
quality_presets_test covers each row through a real settings.toml round
trip, including the next launch's masking.
Launcher
RecompLauncherCGameInfo.quality_* gives the launcher the offered presets, the
detected preset, a hardware summary (for example "Apple M1 · 8 threads · 8 GB"),
and two callbacks:
quality_applyfills the Settings rows a preset sets. The host runs the preset's values through the same conversions it uses to seed the launcher.quality_redetectprobes again, GL included, and returns the preset.
Settings.quality_preset is 1–4 for a preset and 5 for Custom. The launcher
snapshots the rows after a preset is applied. As soon as a tracked row differs
from the snapshot, it sets Custom and keeps the preset it came from in
quality_base. recomp-ui and recomp-launcher report the same way. The
frontends track every quality row, so the host classifies the report itself
(psxq::launcher_custom_edit): it fills a copy of the rows from the reported
base preset and counts only rows of keys this title governs. A Custom flag
raised by an ungoverned row (for example Antialiasing when no preset sets it)
keeps the preset, or records the pick it followed. The host then saves the
state through the transaction above. (The launcher may still label such an
edit Custom until it is reopened.) A newly picked
preset's keys that have no row, such as supersample and the PGXP extras,
apply at once on the boot launcher and at the next launch from the soft-return
launcher.
The in-game menu's live video settings (psx_video_apply_live) have no preset
row. The host compares the governed values the menu changes (texture filter,
FMV filter, smooth scaling, dynamic resolution, Smooth motion, render and
present threads, internal resolution) with the ones in force; a change to any
of them is a governed edit and makes the state Custom.