diff --git a/docs/QUALITY_PRESETS.md b/docs/QUALITY_PRESETS.md index 4626bf3b..35b21fbf 100644 --- a/docs/QUALITY_PRESETS.md +++ b/docs/QUALITY_PRESETS.md @@ -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 diff --git a/recompiler/tests/quality_presets_test.cpp b/recompiler/tests/quality_presets_test.cpp index 40f9c50c..5382e246 100644 --- a/recompiler/tests/quality_presets_test.cpp +++ b/recompiler/tests/quality_presets_test.cpp @@ -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 */ diff --git a/runtime/include/quality_tier.h b/runtime/include/quality_tier.h index f6eed26f..849a24a5 100644 --- a/runtime/include/quality_tier.h +++ b/runtime/include/quality_tier.h @@ -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 diff --git a/runtime/src/quality_tier.c b/runtime/src/quality_tier.c index 77aa323f..cb8907c5 100644 --- a/runtime/src/quality_tier.c +++ b/runtime/src/quality_tier.c @@ -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. */