quality presets: an unrecognised GPU gets Medium, not Ultra; honest docs

Nothing is measured about an unknown GPU, so the fallback no longer assumes
Ultra's extra work (supersampling above the display, title Ultra extras);
Low would drop Smooth motion and the PGXP extras on what is usually capable
new hardware. Medium keeps the display's resolution with dynamic
resolution under it. Covers unnamed renderers, Linux with only an AMD PCI
id, and unnamed nouveau chips. docs/QUALITY_PRESETS.md states plainly that
autodetect is a static heuristic over name, threads and memory with no
benchmark, and that the dynamic systems keep the frame rate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Shokunin
2026-10-13 00:04:12 -07:00
parent b92b96ea6b
commit 09ee810fbd
4 changed files with 40 additions and 23 deletions
+25 -16
View File
@@ -58,29 +58,38 @@ name is the fallback when the probe fails, and the probe is skipped under 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`. **Autodetect is a static
heuristic that picks only Low or Ultra**: it classifies the hardware it reads
and measures nothing. A GPU it does not recognise gets Ultra. Medium and High
are manual choices. Low-end machines start on Low.
Every other machine starts on Ultra, and the dynamic systems are its safety
net: dynamic resolution (a title can floor it at native x1), Smooth motion's
in-between frames (made from surplus only) and the adaptive aspect.
`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 |
| Everything else: discrete GPUs, Apple M3 and later, any Pro/Max/Ultra chip, Strix Halo (8050S/8060S), unknown GPUs | Ultra |
| 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. No mid
class is detected as Medium: on every in-between part measured, either Low's
savings or Ultra's dynamic floor decided it. There is no timed benchmark
yet; the probe is the GPU class, thread count and memory.
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.
The classifier reads the `GL_RENDERER` string as Windows drivers, Mesa and
macOS report it; the unit test holds a table of real strings from all three.
On Linux, when the GL probe fails, the sysfs PCI id is used instead (Steam
Deck parts, Intel integrated vs Arc, NVIDIA); an AMD id alone cannot tell an
APU from a card, so it falls through to Ultra.
Deck parts, Intel integrated vs Arc, NVIDIA).
## When detection runs, and what is saved
+3 -2
View File
@@ -148,7 +148,7 @@ static void test_classify() {
PSX_QUALITY_ULTRA, "RTX 4080: Ultra");
check(tier(host("x", "llvmpipe (LLVM 17.0.6, 256 bits)", 16, 32)) == PSX_QUALITY_LOW,
"software GL: Low");
check(tier(host("x", "Some Future GPU", 16, 32)) == PSX_QUALITY_ULTRA, "unknown GPU: Ultra");
check(tier(host("x", "Some Future GPU", 16, 32)) == PSX_QUALITY_MEDIUM, "unknown GPU: Medium (conservative)");
check(tier(host("AMD Ryzen 7 9800X3D 8-Core Processor", "AMD Radeon(TM) Graphics", 16, 48)) ==
PSX_QUALITY_LOW, "2-CU Radeon iGPU: Low");
check(tier(host("x", "NVIDIA GeForce RTX 3060", 4, 16)) == PSX_QUALITY_LOW,
@@ -208,7 +208,8 @@ static void test_renderer_table() {
{"Mesa Intel(R) Arc(tm) Graphics (MTL)", PSX_QUALITY_LOW},
{"Mesa Intel(R) Arc(tm) A750 Graphics (DG2)", PSX_QUALITY_ULTRA},
{"NVIDIA GeForce RTX 3060/PCIe/SSE2", PSX_QUALITY_ULTRA},
{"NV167 (nouveau)", PSX_QUALITY_ULTRA},
{"NV167 (nouveau)", PSX_QUALITY_MEDIUM}, /* unnamed chip: unknown */
{"Virtio-GPU Venus (Some Future GPU)", PSX_QUALITY_MEDIUM},
{"llvmpipe (LLVM 17.0.6, 256 bits)", PSX_QUALITY_LOW},
{"zink Vulkan 1.3(AMD Radeon Graphics (RADV RENOIR) (MESA_RADV))", PSX_QUALITY_LOW},
/* macOS */
+1 -1
View File
@@ -38,7 +38,7 @@ int psx_quality_from_name(const char *name);
/* The preset this host should start from, and one line saying why
* ("AMD integrated GPU: Low"). Ultra unless the class cannot hold 60 Hz even
* at the dynamic floor; unknown hardware gets Ultra. */
* at the dynamic floor; unknown hardware gets Medium. */
int psx_quality_classify(const PsxHostInfo *h, char *reason, size_t reason_cap);
/* Nearest preset the title offers (bit i of offered_mask = tier i): the
+11 -4
View File
@@ -51,9 +51,10 @@ static int apple_gen(const char *s, int *variant) {
/* Autodetect picks between Low and Ultra only (owner decision 2026-10-08):
* low-end machines -- software GL, the Steam Deck's APU, mobile GPUs, every
* integrated GPU up to the 890M class, Apple M1/M2 base chips, GeForce MX/GT
* -- start on Low; everything else, unknown GPUs included, starts on Ultra
* -- start on Low; recognised capable GPUs start on Ultra
* with dynamic resolution, Smooth motion from surplus and the adaptive aspect
* as the safety net. Medium and High are manual choices.
* as the safety net. A GPU the rules do not recognise gets Medium (see
* psx_quality_classify). High is a manual choice.
*
* The input is a GL_RENDERER string as Windows drivers, Mesa and macOS
* report it ("AMD Radeon(TM) Graphics", "AMD Radeon RX Vega 11 Graphics",
@@ -154,7 +155,7 @@ static int pci_tier(const char *id, const char **why) {
return PSX_QUALITY_LOW;
}
if (vendor == 0x10de) { *why = "NVIDIA GPU (PCI id)"; return PSX_QUALITY_ULTRA; }
return -1; /* AMD: the id alone does not tell an APU from a card */
return -1; /* AMD: the id alone does not tell an APU from a card (=> Medium) */
}
int psx_quality_classify(const PsxHostInfo *h, char *reason, size_t reason_cap) {
@@ -171,7 +172,13 @@ int psx_quality_classify(const PsxHostInfo *h, char *reason, size_t reason_cap)
if (tier < 0 && g[0]) tier = gpu_tier(g, &why);
if (tier < 0 && !g[0] && h && h->gpu_id[0]) tier = pci_tier(h->gpu_id, &why);
if (tier < 0) tier = gpu_tier(c, &why); /* Apple: the CPU names the GPU */
if (tier < 0) { tier = PSX_QUALITY_ULTRA; why = "unknown GPU"; }
/* Unrecognised GPU: Medium, not Ultra. Nothing was measured, so the
* expensive Ultra-only work (1.5x supersampling, mipmaps + 16x aniso,
* bloom) is not assumed; Medium keeps the display's own resolution,
* Smooth motion from surplus and the PGXP extras, which Low would drop
* on what is often new, capable hardware. A title without Medium falls
* to the nearest lower preset (psx_quality_pick_offered). */
if (tier < 0) { tier = PSX_QUALITY_MEDIUM; why = "unknown GPU"; }
/* Only what the dynamic systems cannot absorb: too few CPU threads for
* the emulation + render threads, or too little memory. */