From 9c1e716a61e0b01bbcd4e6d50f4940dfc1829895 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Fri, 2 Oct 2026 18:05:59 -0700 Subject: [PATCH] Preserve PGXP mod selection through renderer initialization --- docs/MOD_PACKAGES.md | 7 ++++++ runtime/CMakeLists.txt | 3 +++ runtime/include/mod_pgxp_policy.h | 28 ++++++++++++++++++++++ runtime/include/mod_plugins.h | 6 +++++ runtime/src/main.cpp | 35 ++++++++++++++++++++-------- runtime/src/mod_builtin_pgxp.c | 8 +------ runtime/tests/test_mod_pgxp_policy.c | 34 +++++++++++++++++++++++++++ 7 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 runtime/include/mod_pgxp_policy.h create mode 100644 runtime/tests/test_mod_pgxp_policy.c diff --git a/docs/MOD_PACKAGES.md b/docs/MOD_PACKAGES.md index c6e5aedd..dd91fdd2 100644 --- a/docs/MOD_PACKAGES.md +++ b/docs/MOD_PACKAGES.md @@ -733,6 +733,13 @@ some selections of one feature is a `[[requirement]]`, not a dependency. ## Implicit requirements across packages +The shared PGXP plugin uses `psx_mod_set_pgxp_precision(enabled, cpu_mode)`. +This stores a session selection as well as setting live correction flags, so +later renderer initialization cannot erase mod activation with the base video +settings. Session start clears the selection before activating the new plan; +the player's persistent settings remain unchanged. Explicit validation +environment overrides still take precedence at renderer initialization. + Package format 7 lets a feature, while a condition on its own options holds, need a feature of **another** package: diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index f139b5ec..07c58bcd 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -46,6 +46,9 @@ if(BUILD_TESTING) add_executable(mod_session_baseline_test tests/test_mod_session_baseline.c) target_include_directories(mod_session_baseline_test PRIVATE include) add_test(NAME mod_session_baseline_test COMMAND mod_session_baseline_test) + add_executable(mod_pgxp_policy_test tests/test_mod_pgxp_policy.c) + target_include_directories(mod_pgxp_policy_test PRIVATE include) + add_test(NAME mod_pgxp_policy_test COMMAND mod_pgxp_policy_test) # Netplay: libjuice resolves from third_party/ with the network sealed, # and a recomp-net pinning another libjuice stops the configure. foreach(_psx_juice_case same other) diff --git a/runtime/include/mod_pgxp_policy.h b/runtime/include/mod_pgxp_policy.h new file mode 100644 index 00000000..7f7130af --- /dev/null +++ b/runtime/include/mod_pgxp_policy.h @@ -0,0 +1,28 @@ +#ifndef PSX_MOD_PGXP_POLICY_H +#define PSX_MOD_PGXP_POLICY_H + +/* Session overrides are independent of the player's persistent video config. + * Renderer initialization resolves them again after activation, because GTE + * and GPU initialization may reset the live correction flags. */ +typedef struct PSXModPgxpConfig { + int geometry, textures, cpu; +} PSXModPgxpConfig; +typedef struct PSXModPgxpPolicy { + int enabled, cpu; /* -1: no mod selection */ +} PSXModPgxpPolicy; +static inline void psx_mod_pgxp_reset(PSXModPgxpPolicy* policy) { + policy->enabled = policy->cpu = -1; +} +static inline void psx_mod_pgxp_select(PSXModPgxpPolicy* policy, int enabled, int cpu) { + policy->enabled = enabled != 0; + policy->cpu = cpu != 0; +} +static inline PSXModPgxpConfig psx_mod_pgxp_resolve( + const PSXModPgxpPolicy* policy, PSXModPgxpConfig base) { + if (policy->enabled >= 0) { + base.geometry = base.textures = policy->enabled; + base.cpu = policy->cpu; + } + return base; +} +#endif diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index b380e94d..f051f0f8 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -138,6 +138,12 @@ uint32_t psx_mod_alloc_gpu_dma_memory(uint32_t size, uint32_t alignment); */ int psx_mod_set_main_ram_8mb(int enabled); +/* Session-owned precision selection, applied immediately and retained across + * renderer initialization. Disabling a mod restores the player's base config + * at the next session; explicit debug environment overrides still take priority. + * Does not modify persistent settings or architectural GTE state. */ +void psx_mod_set_pgxp_precision(int enabled, int cpu_mode); + /* Current per-side widescreen reveal in native game pixels (zero at 4:3). */ int32_t psx_mod_widescreen_x_margin(void); diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index af600119..9876f664 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -94,6 +94,7 @@ extern "C" void psx_event_step_conservative_env_init(void); #include "game_options.h" #include "mod_plugins.h" #include "mod_session_baseline.h" +#include "mod_pgxp_policy.h" #include "mod_runtime.h" #include "mod_packages.h" #include "present_image_ring.h" @@ -1245,6 +1246,14 @@ static int g_video_geometry_correction = 0; static int g_video_perspective_texturing = 0; static int g_video_pgxp_cpu_mode = 0; static float g_video_pgxp_tolerance = 0.5f; +static PSXModPgxpPolicy g_mod_pgxp_policy = {-1, -1}; + +extern "C" void psx_mod_set_pgxp_precision(int enabled, int cpu_mode) { + psx_mod_pgxp_select(&g_mod_pgxp_policy, enabled, cpu_mode); + gte_geometry_correction_set(enabled != 0); + gpu_texture_correction_set(enabled != 0); + pgxp_set_cpu_mode(cpu_mode != 0); +} static int g_video_renderer = PSXRecompV4::DEFAULT_VIDEO_RENDERER; /* Settings -> Display -> Internal resolution (internal_resolution.h). The @@ -1533,6 +1542,10 @@ extern "C" int psx_mod_set_adaptive_display_aspect( static PSXModSessionBaseline g_mod_owned_baseline; static void reset_mod_owned_presentation(void) { + psx_mod_pgxp_reset(&g_mod_pgxp_policy); + gte_geometry_correction_set(g_video_geometry_correction); + gpu_texture_correction_set(g_video_perspective_texturing); + pgxp_set_cpu_mode(g_video_pgxp_cpu_mode); PSXModSessionScalars live; live.video_vsync = g_video_vsync; live.frame_interpolation = g_frame_interpolation; @@ -15933,15 +15946,17 @@ session_reboot: * corrections from process start so free-running (headless) boots can be * measured from the first projected vertex — a TCP toggle always arrives * after the interesting window. '0' = off, anything else = on. */ + PSXModPgxpConfig precision = psx_mod_pgxp_resolve(&g_mod_pgxp_policy, + {g_video_geometry_correction, g_video_perspective_texturing, g_video_pgxp_cpu_mode}); if (const char* e = std::getenv("PSX_GEOMETRY_CORRECTION")) - g_video_geometry_correction = (*e && *e != '0') ? 1 : 0; + precision.geometry = (*e && *e != '0') ? 1 : 0; if (const char* e = std::getenv("PSX_PERSPECTIVE_TEXTURING")) - g_video_perspective_texturing = (*e && *e != '0') ? 1 : 0; + precision.textures = (*e && *e != '0') ? 1 : 0; if (const char* e = std::getenv("PSX_PGXP_CPU_MODE")) - g_video_pgxp_cpu_mode = (*e && *e != '0') ? 1 : 0; - gte_geometry_correction_set(g_video_geometry_correction); - gpu_texture_correction_set(g_video_perspective_texturing); - pgxp_set_cpu_mode(g_video_pgxp_cpu_mode); + precision.cpu = (*e && *e != '0') ? 1 : 0; + gte_geometry_correction_set(precision.geometry); + gpu_texture_correction_set(precision.textures); + pgxp_set_cpu_mode(precision.cpu); pgxp_set_tolerance(g_video_pgxp_tolerance); /* Scanlines: env override wins over config, same as the corrections above, * so a headless/free-run boot can be captured with the effect armed from the @@ -15955,12 +15970,12 @@ session_reboot: } gl_renderer_set_scanlines(g_video_scanlines ? 1 : 0, g_video_scanline_strength); - if (g_video_geometry_correction || g_video_perspective_texturing) { + if (precision.geometry || precision.textures) { std::fprintf(stdout, "psxrecomp: geometry correction %s, perspective texturing %s%s\n", - g_video_geometry_correction ? "on" : "off", - g_video_perspective_texturing ? "on" : "off", - (g_video_geometry_correction && requested_scale < 2) + precision.geometry ? "on" : "off", + precision.textures ? "on" : "off", + (precision.geometry && requested_scale < 2) ? " (needs [video] supersampling >= 2 to be visible)" : ""); } /* Display aspect. Identity at the default 4:3. The present letterbox uses diff --git a/runtime/src/mod_builtin_pgxp.c b/runtime/src/mod_builtin_pgxp.c index f0b25755..f53b7519 100644 --- a/runtime/src/mod_builtin_pgxp.c +++ b/runtime/src/mod_builtin_pgxp.c @@ -21,10 +21,6 @@ #define PKG_PGXP "psx.enhancement.pgxp" -/* gte.cpp / gpu.c — the two correction toggles this mod arms. */ -extern void gte_geometry_correction_set(int enabled); -extern void gpu_texture_correction_set(int enabled); - static int pgxp_option_flag(const char* feature, const char* id) { char text[16] = ""; return psx_mod_option_value(PKG_PGXP, feature, id, text, sizeof text) && @@ -32,9 +28,7 @@ static int pgxp_option_flag(const char* feature, const char* id) { } static void builtin_pgxp_activate(void) { - gte_geometry_correction_set(1); - gpu_texture_correction_set(1); /* also arms the shadow engine */ - pgxp_set_cpu_mode(pgxp_option_flag("pgxp", "cpu_mode")); + psx_mod_set_pgxp_precision(1, pgxp_option_flag("pgxp", "cpu_mode")); } PSX_MOD_CONSTRUCTOR(psx_register_builtin_pgxp_plugin) { diff --git a/runtime/tests/test_mod_pgxp_policy.c b/runtime/tests/test_mod_pgxp_policy.c new file mode 100644 index 00000000..3197f8bb --- /dev/null +++ b/runtime/tests/test_mod_pgxp_policy.c @@ -0,0 +1,34 @@ +#include "mod_pgxp_policy.h" +#include +#include + +int main(void) { + PSXModPgxpPolicy policy; + PSXModPgxpConfig configured = {0, 0, 1}; + psx_mod_pgxp_reset(&policy); + PSXModPgxpConfig live = psx_mod_pgxp_resolve(&policy, configured); + assert(!live.geometry && !live.textures && live.cpu); + + /* Activation before renderer initialization must survive re-resolution. */ + psx_mod_pgxp_select(&policy, 1, 0); + live = psx_mod_pgxp_resolve(&policy, configured); + assert(live.geometry && live.textures && !live.cpu); + assert(!configured.geometry && !configured.textures && configured.cpu); + + /* A later vanilla/netplay session inherits the player's config only. */ + psx_mod_pgxp_reset(&policy); + live = psx_mod_pgxp_resolve(&policy, configured); + assert(!live.geometry && !live.textures && live.cpu); + configured.geometry = 1; + live = psx_mod_pgxp_resolve(&policy, configured); + assert(live.geometry && !live.textures && live.cpu); + + psx_mod_pgxp_select(&policy, 0, 1); + live = psx_mod_pgxp_resolve(&policy, configured); + assert(!live.geometry && !live.textures && live.cpu); + psx_mod_pgxp_select(&policy, 1, 1); + live = psx_mod_pgxp_resolve(&policy, configured); + assert(live.geometry && live.textures && live.cpu); + puts("PGXP session policy passed"); + return 0; +}