mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
Add opt-in native-wide presentation hold for retained loading scenes
This commit is contained in:
@@ -602,3 +602,25 @@ off, so the default presentation is unchanged: letterbox and pillarbox margins
|
||||
remain black. Enabling the feature without choosing artwork is also a no-op.
|
||||
The package supplies only the declaration and trusted plugin selection; archives
|
||||
still cannot load native code.
|
||||
|
||||
### Retained-scene loading presentation (native-wide opt-in)
|
||||
|
||||
A trusted game plugin can register
|
||||
`psx_mod_set_retained_scene_predicate(predicate)` from `mod_plugins.h` when the
|
||||
game keeps displaying its previous framebuffer while loading. The cheap, pure
|
||||
emulation-thread callback must return nonzero only while that same scene is
|
||||
retained. It must not call presentation APIs recursively. Passing NULL removes
|
||||
the opt-in. Without registration, existing presentation behavior is unchanged.
|
||||
|
||||
In native-wide mode this holds the previous wide/4:3 classification even if a
|
||||
game-state flag or absent GTE activity would normally classify loading as 2D.
|
||||
It does not force menus wide, stretch artwork, change guest rendering or memory,
|
||||
or override the FMV veto. Release the predicate when a new scene replaces the
|
||||
retained image, so ordinary classification resumes. Crash's experimental
|
||||
adaptive feature uses its pending level transition and draw-skip globals for
|
||||
this; these game-specific addresses do not belong in the framework.
|
||||
|
||||
GPU reset and savestate restore discard this host-only history. A save loaded
|
||||
directly into a frozen loading frame cannot recreate wide reveal strips absent
|
||||
from the canonical saved framebuffer. `ws_scene_hold_test` covers long holds,
|
||||
menu release, retained 4:3 scenes, FMV priority and timeline reset.
|
||||
|
||||
@@ -23,6 +23,9 @@ psxrecomp_add_runtime_target(psx-runtime
|
||||
# (docs/ENHANCEMENTS.md G1.10). Titles get their <exe>_pgxp the same way.
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(ws_scene_hold_test tests/test_ws_scene_hold.c)
|
||||
target_include_directories(ws_scene_hold_test PRIVATE include)
|
||||
add_test(NAME ws_scene_hold_test COMMAND ws_scene_hold_test)
|
||||
add_executable(mod_texture_banks_test tests/test_mod_texture_banks.c src/mod_texture_banks.c)
|
||||
target_include_directories(mod_texture_banks_test PRIVATE include)
|
||||
add_test(NAME mod_texture_banks_test COMMAND mod_texture_banks_test)
|
||||
|
||||
@@ -104,6 +104,16 @@ uint32_t psx_mod_display_width(void);
|
||||
/* Height companion to psx_mod_display_width(); same conventions. */
|
||||
uint32_t psx_mod_display_height(void);
|
||||
|
||||
/* Opt-in presentation hold for a game that retains its previous framebuffer
|
||||
* while loading. The pure, cheap emulation-thread predicate returns nonzero
|
||||
* only while that SAME scene remains displayed; no GPU/API recursion allowed.
|
||||
* Native-wide retains its prior wide/4:3 classification, never stretches art
|
||||
* and never overrides FMV. NULL removes the opt-in. Host history is discarded
|
||||
* on GPU reset/savestate restore, so loading a frozen scene cannot recreate
|
||||
* missing widescreen strips. This does not change guest rendering or memory. */
|
||||
typedef int (*PSXModRetainedScenePredicate)(void);
|
||||
void psx_mod_set_retained_scene_predicate(PSXModRetainedScenePredicate predicate);
|
||||
|
||||
/*
|
||||
* Read the committed value of one of this package's declared options, as the
|
||||
* player left it in the launcher (or the manifest default when untouched).
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
/* Host presentation history, not guest hardware state. Never serialize it:
|
||||
* a restored canonical framebuffer cannot reconstruct discarded wide strips. */
|
||||
typedef struct WsSceneHold { int valid, native_43; } WsSceneHold;
|
||||
static inline void ws_scene_hold_reset(WsSceneHold* state) {
|
||||
state->valid = 0; state->native_43 = 1;
|
||||
}
|
||||
static inline int ws_scene_hold_classify(WsSceneHold* state, int hold,
|
||||
int native_43, int fmv) {
|
||||
/* FMV always wins, and cannot seed a later wide retained scene. */
|
||||
if (fmv) {
|
||||
state->valid = 1; state->native_43 = 1;
|
||||
return 1;
|
||||
}
|
||||
if (hold && state->valid) return state->native_43;
|
||||
state->valid = 1; state->native_43 = native_43 != 0;
|
||||
return state->native_43;
|
||||
}
|
||||
+17
-2
@@ -27,6 +27,8 @@
|
||||
#include "event_ring.h"
|
||||
#include "color_lut.h"
|
||||
#include "mod_runtime.h"
|
||||
#include "mod_plugins.h"
|
||||
#include "ws_scene_hold.h"
|
||||
#include "sio.h"
|
||||
#include "ws_cull_detect.h"
|
||||
#include "ws_aspect_cone_math.h"
|
||||
@@ -374,17 +376,28 @@ static int ws_2d_only_scene(void) {
|
||||
|
||||
static uint32_t s_ws_fmv_frame_cache = 0xFFFFFFFFu;
|
||||
static int s_ws_fmv_cached = 0;
|
||||
static PSXModRetainedScenePredicate s_ws_retained_scene_predicate;
|
||||
static WsSceneHold s_ws_scene_hold;
|
||||
|
||||
void psx_mod_set_retained_scene_predicate(PSXModRetainedScenePredicate predicate) {
|
||||
s_ws_retained_scene_predicate = predicate;
|
||||
ws_scene_hold_reset(&s_ws_scene_hold);
|
||||
}
|
||||
|
||||
int gpu_ws_present_native_43(void) {
|
||||
if (!ws_engaged()) return 0;
|
||||
if (!ws_game_mode()) return 1; /* full-2D screen */
|
||||
if (ws_2d_only_scene()) return 1; /* 2D-only gameplay scene */
|
||||
int native_43 = !ws_game_mode() || ws_2d_only_scene();
|
||||
int hold_enabled = ws_mode == 2 && s_ws_retained_scene_predicate != NULL;
|
||||
if (!hold_enabled && native_43) return 1; /* unchanged default */
|
||||
uint32_t f = (uint32_t)s_frame_count;
|
||||
if (f != s_ws_fmv_frame_cache) {
|
||||
s_ws_fmv_frame_cache = f;
|
||||
GpuDisplayInfo di; gpu_get_display_info(&di);
|
||||
s_ws_fmv_cached = di.depth24 || mdec_recently_active(WS_FMV_HYSTERESIS);
|
||||
}
|
||||
if (hold_enabled)
|
||||
return ws_scene_hold_classify(&s_ws_scene_hold,
|
||||
s_ws_retained_scene_predicate(), native_43, s_ws_fmv_cached);
|
||||
return s_ws_fmv_cached;
|
||||
}
|
||||
|
||||
@@ -2853,6 +2866,7 @@ static void gpu_reset_state(int clear_vram) {
|
||||
gpustat_poll_count = 0;
|
||||
s_ws_fmv_frame_cache = 0xFFFFFFFFu;
|
||||
s_ws_fmv_cached = 0;
|
||||
ws_scene_hold_reset(&s_ws_scene_hold);
|
||||
s_d24_upload_x1 = 0;
|
||||
s_d24_present_hold = 0;
|
||||
s_d24_prev_disp_h = 0;
|
||||
@@ -6123,6 +6137,7 @@ int gpu_snapshot_read(const uint8_t *p, uint32_t len) {
|
||||
if (len != gpu_snapshot_bytes()) return 0;
|
||||
pst_r_init(&r, p, len);
|
||||
if (!gpu_snap_parse(&r)) return 0;
|
||||
ws_scene_hold_reset(&s_ws_scene_hold);
|
||||
ws_hud_anchor_clear(ws_hud_anchor_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_hud_anchor_clear(ws_reveal_clear_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_repeat_rect_tag_clear(ws_repeat_rect_tags);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "ws_scene_hold.h"
|
||||
#undef NDEBUG /* the Release test target must execute its assertions */
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
WsSceneHold s;
|
||||
ws_scene_hold_reset(&s);
|
||||
assert(ws_scene_hold_classify(&s,0,0,0)==0); /* world */
|
||||
for (int i=0;i<300;++i)
|
||||
assert(ws_scene_hold_classify(&s,1,1,0)==0); /* no GTE during load */
|
||||
assert(ws_scene_hold_classify(&s,0,1,0)==1); /* actual menu replaces it */
|
||||
assert(ws_scene_hold_classify(&s,1,0,0)==1); /* never force a 2D scene wide */
|
||||
assert(ws_scene_hold_classify(&s,0,0,0)==0);
|
||||
assert(ws_scene_hold_classify(&s,1,0,1)==1); /* FMV veto */
|
||||
assert(ws_scene_hold_classify(&s,1,0,0)==1);
|
||||
assert(ws_scene_hold_classify(&s,0,0,0)==0);
|
||||
ws_scene_hold_reset(&s); /* cannot reuse unrelated pre-restore margins */
|
||||
assert(ws_scene_hold_classify(&s,1,1,0)==1);
|
||||
assert(ws_scene_hold_classify(&s,0,0,0)==0);
|
||||
puts("PASS retained scene, menu release, FMV veto and timeline reset");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user