From 7a015840db06d31a72b401aecc38a7fafea8a3c8 Mon Sep 17 00:00:00 2001 From: Shokunin <30949000+tetrisgm@users.noreply.github.com> Date: Wed, 21 Oct 2026 08:45:11 -0700 Subject: [PATCH] Limit supersampling on high-resolution displays by opt-in policy --- docs/config_schema.md | 5 +++++ docs/internal/FAITHFUL_TIMING_PLAN.md | 2 ++ recompiler/src/config_loader.cpp | 7 +++++++ recompiler/src/config_loader.h | 2 ++ .../tests/video_enhancement_settings_test.cpp | 6 +++++- runtime/include/internal_resolution.h | 7 +++++++ runtime/src/main.cpp | 15 +++++++++++---- runtime/tests/test_internal_resolution.cpp | 5 +++++ 8 files changed, 44 insertions(+), 5 deletions(-) diff --git a/docs/config_schema.md b/docs/config_schema.md index cdce0fcc..a157b522 100644 --- a/docs/config_schema.md +++ b/docs/config_schema.md @@ -702,6 +702,7 @@ player's `settings.toml`, and a game may ship a default the same way: [video] internal_resolution = "4k" # native | 720p | 1080p | 1440p | 4k | 5k | 8k | display, or a number of lines resolution_reference_lines = 240 # game.toml only: the title's usual display height (120..1024) +supersample_disable_at_lines = 0 # game.toml only: supersample 1.0 at/above this display height (0 = off, 240..8640) match_display_max_lines = 0 # game.toml only: cap on the display height Match display uses (0 = none, 240..8640) ``` @@ -712,6 +713,10 @@ past the full-VRAM surface. The high-resolution window then engages, and dynamic resolution cannot step there. A title whose GPU budget is set for 4K lines sets 2160. +`supersample_disable_at_lines` uses the physical monitor height before the cap. +At or above its threshold the effective factor is 1.0; the saved factor is +retained for smaller monitors. Zero preserves the default behaviour. + A preset is a target height. The runtime renders at the integer scale `S = ceil(target / resolution_reference_lines)`: at the default 240 lines, 720p is 3x, 1080p is 5x (1200 lines, area-resolved to 1080), 1440p 6x, 4K 9x, diff --git a/docs/internal/FAITHFUL_TIMING_PLAN.md b/docs/internal/FAITHFUL_TIMING_PLAN.md index c57b6c65..60bfcb20 100644 --- a/docs/internal/FAITHFUL_TIMING_PLAN.md +++ b/docs/internal/FAITHFUL_TIMING_PLAN.md @@ -213,6 +213,8 @@ on a fixed region -> next. ## 5. Status / Log (update every session) +- **2026-10-21 (BRIEF-4 Stage 1):** Reused #722 display-height cap and added opt-in `supersample_disable_at_lines`; unchanged defaults for other titles. Resolution, replay, config-parser and R4 default checks pass on Windows and macOS. R4 fullscreen 8K route cap restores 60 Hz but completion gaps remain (6 quiet / 7 loaded); no stage acceptance claimed. See R4 `analysis/STATUS.md`. + - **2026-10-21 (BRIEF-4 Stage 1):** Port recorded-route measurement support from `a9da7fce`; validation pending. No guest timing changes. - **2026-10-07 — DuckStation texture authoring fidelity (beads-qi66).** diff --git a/recompiler/src/config_loader.cpp b/recompiler/src/config_loader.cpp index 46161cf4..768350b7 100644 --- a/recompiler/src/config_loader.cpp +++ b/recompiler/src/config_loader.cpp @@ -780,6 +780,13 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path& } rt.video_match_display_max_lines = static_cast(n); } + if (video.contains("supersample_disable_at_lines")) { + const auto n = toml::find(video, "supersample_disable_at_lines"); + if (n != 0 && (n < 240 || n > 8640)) + throw std::runtime_error(fmt::format( + "[video] supersample_disable_at_lines must be 0 or 240..8640: {}", n)); + rt.video_supersample_disable_at_lines = static_cast(n); + } if (video.contains("window_width")) { const auto n = toml::find(video, "window_width"); if (n < 640 || n > 7680) { diff --git a/recompiler/src/config_loader.h b/recompiler/src/config_loader.h index 7fb4375b..1cca1185 100644 --- a/recompiler/src/config_loader.h +++ b/recompiler/src/config_loader.h @@ -472,6 +472,8 @@ struct RuntimeConfig { /* [video] match_display_max_lines: cap on the display height Match * display (and its dynamic-resolution floor) resolves against; 0 = none. */ int video_match_display_max_lines = 0; + /* Disable target supersampling on displays at least this tall (0 = off). */ + int video_supersample_disable_at_lines = 0; // Optional initial window width declared by the title profile. Zero keeps // the historical fit-to-display behavior; player settings may override it. diff --git a/recompiler/tests/video_enhancement_settings_test.cpp b/recompiler/tests/video_enhancement_settings_test.cpp index 002bbd8f..cdc5b83a 100644 --- a/recompiler/tests/video_enhancement_settings_test.cpp +++ b/recompiler/tests/video_enhancement_settings_test.cpp @@ -226,14 +226,17 @@ static void test_internal_resolution_game_toml() { "resolution_reference_lines defaults to 240"); check(gc.runtime.video_match_display_max_lines == 0, "match_display_max_lines defaults to 0 (no cap)"); + check(gc.runtime.video_supersample_disable_at_lines == 0, "display SS policy defaults off"); fs::remove(p); p = write_game_toml("psxrecomp_ir_match_cap.toml", "[video]\n" "internal_resolution = \"display\"\n" - "match_display_max_lines = 2160\n"); + "match_display_max_lines = 2160\n" + "supersample_disable_at_lines = 2160\n"); gc = PSXRecompV4::load_game_config(p); check(gc.runtime.video_match_display_max_lines == 2160, "match_display_max_lines parses"); + check(gc.runtime.video_supersample_disable_at_lines == 2160, "display SS policy parses"); fs::remove(p); p = write_game_toml("psxrecomp_ir_4k.toml", @@ -257,6 +260,7 @@ static void test_internal_resolution_game_toml() { "internal_resolution = 1\n", "resolution_reference_lines = 50\n", "match_display_max_lines = 100\n", + "supersample_disable_at_lines = 100\n", "supersampling = 33\n" }) { p = write_game_toml("psxrecomp_ir_bad.toml", std::string("[video]\n") + bad); bool rejected = false; diff --git a/runtime/include/internal_resolution.h b/runtime/include/internal_resolution.h index 50e61016..ebc435e9 100644 --- a/runtime/include/internal_resolution.h +++ b/runtime/include/internal_resolution.h @@ -143,6 +143,13 @@ static inline int psx_ss_parse_milli(const char *s) { return (int)m; } +/* Opt-in display policy; keep the saved factor intact for smaller displays. */ +static inline int psx_ir_display_supersample(int ss_milli, int display_px_h, + int disable_at_lines) { + return disable_at_lines > 0 && display_px_h >= disable_at_lines + ? PSX_SS_MILLI_MIN : ss_milli; +} + /* psx_resolve_internal_scale with the supersample factor on the target. */ static inline int psx_resolve_internal_scale_ss(int value, int ref_lines, int display_px_h, int s_max, diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 55dbacd5..dcc7153b 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -1279,6 +1279,11 @@ static int g_video_ss_milli = 1000; /* [video] supersample / PSX_SUPER * full-VRAM surface, where the high-resolution window engages and dynamic * resolution cannot step; a title can cap it at, say, 4K lines. */ static int g_video_match_max_lines = 0; +static int g_video_ss_disable_at_lines = 0; +static int display_supersample(SDL_Window *window) { + return psx_ir_display_supersample(g_video_ss_milli, + psx_sdl_display_pixel_height(window), g_video_ss_disable_at_lines); +} static int match_display_px_h(SDL_Window *window) { const int h = psx_sdl_display_pixel_height(window); return (g_video_match_max_lines > 0 && h > g_video_match_max_lines) @@ -1463,7 +1468,7 @@ static void apply_internal_resolution(int display_px_h) { if (preset == PSX_IR_UNSET) return; g_video_scale = psx_resolve_internal_scale_ss(preset, g_video_ref_lines, display_px_h, video_scale_ceiling(), - g_video_ss_milli); + display_supersample(nullptr)); } /* The value the launcher row starts on: the preset, or the legacy factor shown @@ -17972,6 +17977,7 @@ int main(int argc, char** argv) { g_video_internal_res = gc.runtime.video_internal_resolution; g_video_ref_lines = gc.runtime.video_resolution_reference_lines; g_video_match_max_lines = gc.runtime.video_match_display_max_lines; + g_video_ss_disable_at_lines = gc.runtime.video_supersample_disable_at_lines; g_video_dynres = gc.runtime.video_dynamic_resolution ? 1 : 0; g_video_dynres_min = gc.runtime.video_dynamic_resolution_min; if (gc.runtime.video_window_width > 0) { @@ -20462,9 +20468,10 @@ session_reboot: else if (!std::strcmp(e, "fxaa_hq")) g_video_post_aa = GL_POST_AA_FXAA_HQ; else std::fprintf(stdout, "psxrecomp: PSX_AA_MODE=%s not understood (off, fxaa, fxaa_hq)\n", e); } - if (g_video_ss_milli != 1000) + const int effective_ss = display_supersample(nullptr); + if (effective_ss != 1000 || effective_ss != g_video_ss_milli) std::fprintf(stdout, "psxrecomp: supersample %.3gx on the internal-resolution target\n", - g_video_ss_milli / 1000.0); + effective_ss / 1000.0); apply_internal_resolution(match_display_px_h(nullptr)); if (g_video_scale < 1) g_video_scale = 1; if (const char* e = std::getenv("PSX_DYNRES")) @@ -21112,7 +21119,7 @@ session_reboot: const int dh = match_display_px_h(sdl_window); const int s = psx_resolve_internal_scale_ss(PSX_IR_DISPLAY, g_video_ref_lines, dh, GL_MAX_INTERNAL_SCALE, - g_video_ss_milli); + display_supersample(sdl_window)); gr_set_scale(s); g_video_requested_scale = s; std::fprintf(stdout, "psxrecomp: internal resolution Match display: " diff --git a/runtime/tests/test_internal_resolution.cpp b/runtime/tests/test_internal_resolution.cpp index 25b04cdd..2fbbace7 100644 --- a/runtime/tests/test_internal_resolution.cpp +++ b/runtime/tests/test_internal_resolution.cpp @@ -222,6 +222,11 @@ int main() { expect("ss clamps to the ceiling", psx_resolve_internal_scale_ss(4320, 240, 0, 32, 4000), 32); expect("ss unknown display stays 1", psx_resolve_internal_scale_ss(PSX_IR_DISPLAY, 240, 0, 32, 2000), 1); + expect("ss policy opt-out", psx_ir_display_supersample(1500, 4320, 0), 1500); + expect("ss below threshold", psx_ir_display_supersample(1500, 2159, 2160), 1500); + expect("ss at threshold", psx_ir_display_supersample(1500, 2160, 2160), 1000); + expect("ss on 8K", psx_ir_display_supersample(1500, 4320, 2160), 1000); + expect("ss unknown display", psx_ir_display_supersample(1500, 0, 2160), 1500); launcher_round_trips(); if (g_failures) return 1;