mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Live resolution: truthful restart contract per backend
Keep the requested internal resolution / supersample (shown and saved) apart from what the backend last applied. When the live change is unavailable (no GL, windowed high-resolution mode, CPU-authoritative dual rendering, no context) or refused, the session scale is left as applied and PSX_VIDEO_RESTART_RESOLUTION stays set, in both psx_video_apply_live and the menu's restart bits (psx_host_video_restart_bits_backend, unit-tested for the capable and refused cases). video_set rejects invalid presets and factors without applying or saving, and reports applied values, capability and pending restart. TCP command index regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10632,21 +10632,42 @@ static void handle_dynres_check(int id, const char *json)
|
||||
|
||||
/* {"cmd":"video_set","internal_resolution":N,"supersample":M} - change the
|
||||
* Internal resolution preset (lines, 1 native, -1 match display) and/or the
|
||||
* supersample (thousandths) through the in-game menu's live path. Returns the
|
||||
* restart bits the change still needs (0 = fully live). */
|
||||
* supersample (thousandths, 1000..4000) through the in-game menu's live path.
|
||||
* Invalid values are rejected without applying or saving anything. Returns
|
||||
* the restart bits the change still needs (0 = applied live), the requested
|
||||
* values, and the values the backend actually applied. */
|
||||
static void handle_video_set(int id, const char *json)
|
||||
{
|
||||
extern void psx_video_current(PsxHostVideoSettings *v);
|
||||
extern unsigned psx_video_apply_live(const PsxHostVideoSettings *next);
|
||||
extern void psx_video_resolution_applied(int *ir, int *ss_milli, int *live_capable,
|
||||
int *restart_pending);
|
||||
const int k_absent = -1000000;
|
||||
const int ir = json_get_int(json, "internal_resolution", k_absent);
|
||||
const int ss = json_get_int(json, "supersample", k_absent);
|
||||
if (ir == k_absent && ss == k_absent) {
|
||||
send_err(id, "video_set needs internal_resolution and/or supersample");
|
||||
return;
|
||||
}
|
||||
if (ir != k_absent && !(ir == 1 || ir == -1 || (ir >= 2 && ir <= 8640))) {
|
||||
send_err(id, "invalid internal_resolution (1 native, -1 match display, 2..8640 lines)");
|
||||
return;
|
||||
}
|
||||
if (ss != k_absent && (ss < 1000 || ss > 4000)) {
|
||||
send_err(id, "invalid supersample (1000..4000 thousandths)");
|
||||
return;
|
||||
}
|
||||
PsxHostVideoSettings v;
|
||||
psx_video_current(&v);
|
||||
const int ir = json_get_int(json, "internal_resolution", 0);
|
||||
const int ss = json_get_int(json, "supersample", 0);
|
||||
if (ir) v.internal_resolution = ir;
|
||||
if (ss) v.supersample_milli = ss;
|
||||
if (ir != k_absent) v.internal_resolution = ir;
|
||||
if (ss != k_absent) v.supersample_milli = ss;
|
||||
const unsigned restart = psx_video_apply_live(&v);
|
||||
int air = 0, ass = 0, cap = 0, pend = 0;
|
||||
psx_video_resolution_applied(&air, &ass, &cap, &pend);
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"restart\":%u,\"internal_resolution\":%d,"
|
||||
"\"supersample\":%d}\n", id, restart, v.internal_resolution, v.supersample_milli);
|
||||
"\"supersample\":%d,\"applied_internal_resolution\":%d,"
|
||||
"\"applied_supersample\":%d,\"live_capable\":%d,\"restart_pending\":%d}\n",
|
||||
id, restart, v.internal_resolution, v.supersample_milli, air, ass, cap, pend);
|
||||
}
|
||||
|
||||
static void handle_video_info(int id, const char *json)
|
||||
|
||||
@@ -11062,6 +11062,13 @@ int gl_renderer_request_internal_scale(int scale) {
|
||||
* ceiling (clamped like the boot scale to the full-VRAM limit) and queues the
|
||||
* step. Returns the scale that will apply, 0 when not possible (windowed
|
||||
* high-resolution mode, CPU-authoritative dual rendering, no context). */
|
||||
/* Whether gl_renderer_set_internal_scale_live can move the scale on this
|
||||
* session at all: not in windowed high-resolution mode, not with
|
||||
* CPU-authoritative dual rendering, and only with a live raster context. */
|
||||
int gl_renderer_internal_scale_live_capable(void) {
|
||||
return (s_ctx && s_raster_ok && !s_hiw && !s_cpu_auth_dual) ? 1 : 0;
|
||||
}
|
||||
|
||||
int gl_renderer_set_internal_scale_live(int scale) {
|
||||
if (s_ctx) GL_RT_SYNC("set_internal_scale_live");
|
||||
if (!s_ctx || !s_raster_ok || s_hiw || s_cpu_auth_dual) return 0;
|
||||
|
||||
@@ -152,6 +152,17 @@ unsigned psx_host_video_restart_bits(const PsxHostVideoSettings *boot,
|
||||
return m;
|
||||
}
|
||||
|
||||
unsigned psx_host_video_restart_bits_backend(const PsxHostVideoSettings *boot,
|
||||
const PsxHostVideoSettings *next,
|
||||
int res_live_ok) {
|
||||
unsigned m = psx_host_video_restart_bits(boot, next);
|
||||
if (boot && next && !res_live_ok &&
|
||||
(boot->internal_resolution != next->internal_resolution ||
|
||||
boot->supersample_milli != next->supersample_milli))
|
||||
m |= PSX_VIDEO_RESTART_RESOLUTION;
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ---- seamless relaunch ----------------------------------------------------- */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
+68
-11
@@ -10240,6 +10240,38 @@ static void dynres_setup(void) {
|
||||
static std::filesystem::path g_overlay_settings_path; /* settings.toml */
|
||||
static PsxHostVideoSettings g_video_boot; /* as the session booted */
|
||||
static bool g_video_boot_valid = false;
|
||||
/* Live Internal resolution / Supersample. g_video_internal_res and
|
||||
* g_video_ss_milli are what the player requested (shown and saved); these are
|
||||
* what the backend last actually applied. g_video_res_live_failed holds a
|
||||
* relaunch requirement after a live change the backend refused, and
|
||||
* g_video_res_live_capable mirrors gl_renderer_internal_scale_live_capable()
|
||||
* for the menu thread. */
|
||||
static int g_video_res_applied_ir = PSX_IR_UNSET;
|
||||
static int g_video_res_applied_ss = 1000;
|
||||
static std::atomic<bool> g_video_res_live_failed{false};
|
||||
static std::atomic<bool> g_video_res_live_capable{false};
|
||||
|
||||
static void video_res_refresh_capable(void) {
|
||||
g_video_res_live_capable.store(g_gl_active && g_video_scale_applies &&
|
||||
gl_renderer_internal_scale_live_capable() != 0);
|
||||
}
|
||||
|
||||
/* psx_host_video_restart_bits plus the resolution group whenever the
|
||||
* requested resolution differs from boot and cannot be (or was not) applied
|
||||
* live on this backend. */
|
||||
static unsigned video_restart_bits(const PsxHostVideoSettings *boot,
|
||||
const PsxHostVideoSettings *next) {
|
||||
return psx_host_video_restart_bits_backend(
|
||||
boot, next, g_video_res_live_capable.load() && !g_video_res_live_failed.load());
|
||||
}
|
||||
|
||||
extern "C" void psx_video_resolution_applied(int *ir, int *ss_milli, int *live_capable,
|
||||
int *restart_pending) {
|
||||
if (ir) *ir = g_video_res_applied_ir;
|
||||
if (ss_milli) *ss_milli = g_video_res_applied_ss;
|
||||
if (live_capable) *live_capable = g_video_res_live_capable.load() ? 1 : 0;
|
||||
if (restart_pending) *restart_pending = g_video_res_live_failed.load() ? 1 : 0;
|
||||
}
|
||||
|
||||
extern "C" void psx_video_current(PsxHostVideoSettings *v) {
|
||||
if (!v) return;
|
||||
@@ -10260,7 +10292,12 @@ extern "C" void psx_video_current(PsxHostVideoSettings *v) {
|
||||
v->render_thread = g_render_thread ? 1 : 0;
|
||||
v->present_thread = g_present_thread ? 1 : 0;
|
||||
v->supersample_milli = g_video_ss_milli;
|
||||
if (!g_video_boot_valid) { g_video_boot = *v; g_video_boot_valid = true; }
|
||||
video_res_refresh_capable();
|
||||
if (!g_video_boot_valid) {
|
||||
g_video_boot = *v; g_video_boot_valid = true;
|
||||
g_video_res_applied_ir = v->internal_resolution;
|
||||
g_video_res_applied_ss = v->supersample_milli;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" unsigned psx_video_apply_live(const PsxHostVideoSettings *next) {
|
||||
@@ -10306,15 +10343,30 @@ extern "C" unsigned psx_video_apply_live(const PsxHostVideoSettings *next) {
|
||||
* frame (gl_renderer_set_internal_scale_live), no relaunch. */
|
||||
if (next->internal_resolution != PSX_IR_UNSET)
|
||||
g_video_internal_res = next->internal_resolution;
|
||||
if (next->supersample_milli >= 1000 && next->supersample_milli <= 4000)
|
||||
if (next->supersample_milli >= PSX_SS_MILLI_MIN &&
|
||||
next->supersample_milli <= PSX_SS_MILLI_MAX)
|
||||
g_video_ss_milli = next->supersample_milli;
|
||||
apply_internal_resolution(match_display_px_h(sdl_window));
|
||||
if (g_gl_active && g_video_scale_applies) {
|
||||
const int applied = gl_renderer_set_internal_scale_live(g_video_scale);
|
||||
if (applied) {
|
||||
std::fprintf(stdout, "psxrecomp: internal scale %dx (live)\n", applied);
|
||||
dynres_setup(); /* the new scale is the controller's ceiling */
|
||||
}
|
||||
/* The request is kept (and saved) either way; the session's scale
|
||||
* only moves when the backend takes it. Otherwise the change waits
|
||||
* for a relaunch and the restart bits say so. */
|
||||
const int prev_scale = g_video_scale;
|
||||
video_res_refresh_capable();
|
||||
int applied = 0;
|
||||
if (g_video_res_live_capable.load()) {
|
||||
apply_internal_resolution(match_display_px_h(sdl_window));
|
||||
applied = gl_renderer_set_internal_scale_live(g_video_scale);
|
||||
}
|
||||
if (applied) {
|
||||
std::fprintf(stdout, "psxrecomp: internal scale %dx (live)\n", applied);
|
||||
g_video_res_applied_ir = g_video_internal_res;
|
||||
g_video_res_applied_ss = g_video_ss_milli;
|
||||
g_video_res_live_failed.store(false);
|
||||
dynres_setup(); /* the new scale is the controller's ceiling */
|
||||
} else {
|
||||
g_video_scale = prev_scale;
|
||||
g_video_res_live_failed.store(true);
|
||||
std::fprintf(stdout, "psxrecomp: internal resolution change needs a "
|
||||
"relaunch (live change unavailable)\n");
|
||||
}
|
||||
}
|
||||
if ((m & PSX_VIDEO_LIVE_FRAME_GEN) && g_gl_active) {
|
||||
@@ -10344,7 +10396,7 @@ extern "C" unsigned psx_video_apply_live(const PsxHostVideoSettings *next) {
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
return psx_host_video_restart_bits(&g_video_boot, next) | deferred;
|
||||
return video_restart_bits(&g_video_boot, next) | deferred;
|
||||
}
|
||||
|
||||
#ifndef PSX_NO_DEBUG_TOOLS
|
||||
@@ -16919,8 +16971,13 @@ unsigned mh_restart_bits(const PsxHostVideoSettings& next) {
|
||||
* with. The live internal resolution moves under dynamic resolution, so
|
||||
* comparing with boot alone flagged edits that never touched it (an
|
||||
* in-game texture filter change relaunched the game). */
|
||||
unsigned out = psx_host_video_restart_bits(&g_ovl_video_boot, &next) &
|
||||
unsigned out = video_restart_bits(&g_ovl_video_boot, &next) &
|
||||
psx_host_video_restart_bits(&now, &next);
|
||||
/* A resolution edit the backend cannot apply live waits for a relaunch
|
||||
* (the pure helper treats resolution as live). */
|
||||
if ((now.internal_resolution != next.internal_resolution ||
|
||||
now.supersample_milli != next.supersample_milli))
|
||||
out |= video_restart_bits(&g_ovl_video_boot, &next) & PSX_VIDEO_RESTART_RESOLUTION;
|
||||
/* In a netplay match only presentation applies live (psx_video_apply_live);
|
||||
* the rest waits for the next session. */
|
||||
if (g_ovl_netplay &&
|
||||
|
||||
Reference in New Issue
Block a user