Dynamic resolution floor "display": the output's own lines

dynamic_resolution_min = "display" (game.toml, settings.toml,
PSX_DYNRES_MIN) floors the controller at the selected internal resolution
without the supersample factor, so with supersample > 1 it only trades
supersampling and never renders below the output. psx_dynres_floor_scale
in internal_resolution.h, tested in test_internal_resolution. OpenBIOS
emitter stamp regenerated (config_loader changed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Shokunin
2026-10-07 21:39:20 -07:00
parent c061ea23ea
commit 7d06942eb0
6 changed files with 52 additions and 13 deletions
+15
View File
@@ -622,6 +622,21 @@ ordered-grid SSAA of everything, 2D included. Native and the legacy
its ceiling and steps under it as before. `PSX_SUPERSAMPLE=<factor>` overrides
it for one run.
To make supersampling the elastic part only, set the dynamic resolution floor
to the output: `dynamic_resolution_min = "display"` (also `PSX_DYNRES_MIN=display`
and settings.toml) means the selected internal resolution *without* the
supersample factor, i.e. the monitor's own lines under Match display or the
preset's lines otherwise. Dynamic resolution then steps between the
supersampled ceiling and the output and never renders below it:
```toml
[video]
internal_resolution = "display"
supersample = 2.0
dynamic_resolution = true
dynamic_resolution_min = "display"
```
`antialiasing_mode` filters the composed game image on OpenGL: after the game
quad (4:3, native-wide, Smooth motion and generated frames alike) and before
hold-last capture, the OSD, screenshots and the swap. It reads only output
+1 -1
View File
@@ -1 +1 @@
be426241e46d80c1d2ca130c73f12b453e31df1c3f20d252196f3c2cecc9f4b6
0a499298b6a38126d2945801a6e24786e7bb1b83a89248de70d8b28bfdc287c8
+4 -7
View File
@@ -715,8 +715,7 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path&
int value = 0;
bool ok = false;
if (ir.is_string()) {
ok = psx_ir_parse(ir.as_string().str.c_str(), &value) != 0 &&
value != PSX_IR_DISPLAY;
ok = psx_ir_parse(ir.as_string().str.c_str(), &value) != 0;
} else if (ir.is_integer()) {
const auto n = ir.as_integer();
ok = n >= PSX_IR_MIN_LINES && n <= PSX_IR_MAX_LINES;
@@ -725,7 +724,7 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path&
if (!ok) {
throw std::runtime_error(
"[video] dynamic_resolution_min must be native, 720p, 1080p, "
"1440p, 4k, 5k, 8k, or a number of lines");
"1440p, 4k, 5k, 8k, display, or a number of lines");
}
rt.video_dynamic_resolution_min = value;
}
@@ -2801,8 +2800,7 @@ UserSettings load_user_settings(const fs::path& path) {
const toml::value& ir = toml::find(v, "dynamic_resolution_min");
int value = 0;
if (ir.is_string()) {
if (psx_ir_parse(ir.as_string().str.c_str(), &value) &&
value != PSX_IR_DISPLAY) {
if (psx_ir_parse(ir.as_string().str.c_str(), &value)) {
s.dynamic_resolution_min = value; s.has_dynamic_resolution_min = true;
}
} else if (ir.is_integer()) {
@@ -3211,8 +3209,7 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
if (s.has_dynamic_resolution)
f << "dynamic_resolution = " << (s.dynamic_resolution ? "true" : "false") << "\n";
if (s.has_dynamic_resolution_min &&
psx_ir_value_valid(s.dynamic_resolution_min) &&
s.dynamic_resolution_min != PSX_IR_DISPLAY) {
psx_ir_value_valid(s.dynamic_resolution_min)) {
const char* id = psx_ir_id_for(s.dynamic_resolution_min);
if (id)
f << "dynamic_resolution_min = \"" << id << "\"\n";
+14
View File
@@ -241,3 +241,17 @@ static inline PsxIrAdopted psx_ir_adopt_launcher(int has_row, int preset,
}
return a;
}
/* ---- Dynamic resolution floor ([video] dynamic_resolution_min) -------------
* A preset or line count resolves as usual. "display" (PSX_IR_DISPLAY) is the
* output's own lines: the selected internal resolution WITHOUT the supersample
* factor (Match display = the monitor's pixel height). With supersample > 1
* the controller then trades only supersampling and never drops below the
* output. Clamped to [1, ceiling]. */
static inline int psx_dynres_floor_scale(int min_value, int selected_ir, int ref_lines,
int display_px_h, int ceiling) {
const int v = (min_value == PSX_IR_DISPLAY && selected_ir != PSX_IR_UNSET)
? selected_ir : min_value;
int s = psx_resolve_internal_scale(v, ref_lines, display_px_h, ceiling);
return s > ceiling ? ceiling : s;
}
+11 -5
View File
@@ -9334,9 +9334,15 @@ static void dynres_setup(void) {
"dual raster)\n");
return;
}
int floor_s = psx_resolve_internal_scale(dynres_min_value(), g_video_ref_lines,
psx_sdl_display_pixel_height(nullptr),
ceiling);
/* Floor "display": the output's own lines, i.e. the selected internal
* resolution without the supersample factor (Match display = the
* monitor's pixel height). Dynamic resolution then only gives back
* supersampling and never renders below the output. */
int floor_s = psx_dynres_floor_scale(dynres_min_value(),
effective_internal_resolution(),
g_video_ref_lines,
psx_sdl_display_pixel_height(nullptr),
ceiling);
if (floor_s > ceiling) floor_s = ceiling;
DynresParams params;
dynres_default_params(&params);
@@ -18089,9 +18095,9 @@ session_reboot:
g_video_dynres_env = (*e && *e != '0') ? 1 : 0;
if (const char* e = std::getenv("PSX_DYNRES_MIN")) {
int v = 0;
if (psx_ir_parse(e, &v) && v != PSX_IR_DISPLAY) g_video_dynres_min_env = v;
if (psx_ir_parse(e, &v)) g_video_dynres_min_env = v;
else std::fprintf(stdout, "psxrecomp: PSX_DYNRES_MIN=%s not understood "
"(native, 720p, 1080p, 1440p, 4k, 5k, 8k, or lines)\n", e);
"(native, 720p, 1080p, 1440p, 4k, 5k, 8k, display, or lines)\n", e);
}
{
/* Per-backend ceiling. OpenGL allocates its hr surface at context init
@@ -206,6 +206,13 @@ int main() {
expect("ss parse 4", psx_ss_parse_milli("4"), 4000);
expect("ss reject 0.5", psx_ss_parse_milli("0.5"), 0);
expect("ss reject 5", psx_ss_parse_milli("5"), 0);
// Dynamic resolution floor "display": the output's lines, no supersample.
expect("floor display 1080 MD", psx_dynres_floor_scale(PSX_IR_DISPLAY, PSX_IR_DISPLAY, 240, 1080, 9), 5);
expect("floor display 1440 MD", psx_dynres_floor_scale(PSX_IR_DISPLAY, PSX_IR_DISPLAY, 240, 1440, 12), 6);
expect("floor display preset", psx_dynres_floor_scale(PSX_IR_DISPLAY, 1080, 240, 2160, 9), 5);
expect("floor display unset", psx_dynres_floor_scale(PSX_IR_DISPLAY, PSX_IR_UNSET, 240, 1080, 9), 5);
expect("floor display clamp", psx_dynres_floor_scale(PSX_IR_DISPLAY, PSX_IR_DISPLAY, 240, 2160, 4), 4);
expect("floor 720p", psx_dynres_floor_scale(720, PSX_IR_DISPLAY, 240, 1080, 9), 3);
expect("ss reject junk", psx_ss_parse_milli("2y"), 0);
expect("ss 1.0 is the plain resolve",
psx_resolve_internal_scale_ss(PSX_IR_DISPLAY, 240, 1080, 32, 1000), 5);