mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
feat: add adaptive native widescreen
Add a resize-driven aspect option, keep native-wide culling and spawning hooks engaged across live aspect changes, and widen host GP0 draw-area pruning only for the active wide framebuffer. Credit OpokXeno's independently discovered draw-area pruning fix documented in PR #73. Co-authored-by: Opok <novelasevilliousesp@gmail.com>
This commit is contained in:
@@ -70,8 +70,26 @@ clear_reveal = true # clear synthetic native-wide side margin
|
||||
nw_left_hud_packet_lo = "0x000E3400" # optional targeted left-HUD packet range
|
||||
nw_left_hud_packet_hi = "0x000E4100" # (half-open); avoids shifting 2D scenery.
|
||||
offer_ultrawide = true # separate experimental 21:9 launcher row.
|
||||
adaptive_view = true # expose live resize-driven aspect mode.
|
||||
```
|
||||
|
||||
When `adaptive_view` is enabled by the game, the launcher's **Aspect ratio**
|
||||
selector gains an **Adaptive** entry. The previously selected fixed aspect
|
||||
still determines the initial window shape. Once the game window exists,
|
||||
resizing it continuously
|
||||
updates the GTE/native-wide projection, cull margins, wide render target, and
|
||||
present aspect. The live ratio is clamped to 4:3 on the narrow side and to the
|
||||
widest mode the game offers (`16:9`, or `21:9` with `offer_ultrawide`). BIOS,
|
||||
FMV, menus, and other title-classified 2D frames retain their existing 4:3
|
||||
pillarbox policy. The user choice persists as `[video] adaptive_view` in
|
||||
`settings.toml`.
|
||||
|
||||
The native-wide draw-area early-out correction was independently identified
|
||||
and contributed by **OpokXeno** in
|
||||
[PR #73](https://github.com/mstan/psxrecomp/pull/73). The landed variant keeps
|
||||
that mirror-space correction while restricting it to active framebuffer
|
||||
targets, so offscreen texture targets retain the original host-side pruning.
|
||||
|
||||
Tomba's values are Ghidra-evidenced: `0x8005E08C` is the shared per-prim helper
|
||||
all ~23 character render functions (RTPS cluster `0x800459E0`–`0x8004FB54`) call;
|
||||
the RTPS preamble stores the anchor SXY to scratchpad `0x1F800070`.
|
||||
|
||||
@@ -889,6 +889,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
|
||||
std::vector<WidescreenSignedBoundSite> ws_signed_x_bound_sites;
|
||||
bool ws_offered = true;
|
||||
bool vulkan_offered = false;
|
||||
bool ws_adaptive_view = false;
|
||||
bool ws_ultrawide_offered = false;
|
||||
if (cfg.contains("video")) {
|
||||
const toml::value& video = toml::find(cfg, "video");
|
||||
@@ -1060,6 +1061,8 @@ GameConfig load_game_config(const fs::path& config_path_in) {
|
||||
ws_offered = toml::find<bool>(ws, "offer");
|
||||
if (ws.contains("offer_ultrawide"))
|
||||
ws_ultrawide_offered = toml::find<bool>(ws, "offer_ultrawide");
|
||||
if (ws.contains("adaptive_view"))
|
||||
ws_adaptive_view = toml::find<bool>(ws, "adaptive_view");
|
||||
}
|
||||
|
||||
// Optional [widescreen.cull] block — world-space draw-cull widening.
|
||||
@@ -1279,6 +1282,7 @@ GameConfig load_game_config(const fs::path& config_path_in) {
|
||||
/*ws_signed_x_bound_sites*/ ws_signed_x_bound_sites,
|
||||
/*ws_offered*/ ws_offered,
|
||||
/*vulkan_offered*/ vulkan_offered,
|
||||
/*ws_adaptive_view*/ ws_adaptive_view,
|
||||
/*ws_ultrawide_offered*/ ws_ultrawide_offered,
|
||||
/*ws_bg2d_count_site*/ ws_bg2d_count_site,
|
||||
/*ws_bg2d_startcol_site*/ ws_bg2d_startcol_site,
|
||||
@@ -1438,6 +1442,10 @@ UserSettings load_user_settings(const fs::path& path) {
|
||||
s.aspect_num = n; s.aspect_den = d; s.has_aspect_ratio = true;
|
||||
}
|
||||
});
|
||||
if (v.contains("adaptive_view")) try_get([&]{
|
||||
s.adaptive_view = toml::find<bool>(v, "adaptive_view");
|
||||
s.has_adaptive_view = true;
|
||||
});
|
||||
}
|
||||
if (doc.contains("audio")) {
|
||||
const toml::value& a = toml::find(doc, "audio");
|
||||
@@ -1600,6 +1608,8 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
|
||||
f << "frame_interpolation_fps = " << s.frame_interpolation_fps << "\n";
|
||||
if (s.has_aspect_ratio)
|
||||
f << "aspect_ratio = \"" << s.aspect_num << ":" << s.aspect_den << "\"\n";
|
||||
if (s.has_adaptive_view)
|
||||
f << "adaptive_view = " << (s.adaptive_view ? "true" : "false") << "\n";
|
||||
f << "\n[audio]\n";
|
||||
if (s.has_spu_hq)
|
||||
f << "spu_hq = " << (s.spu_hq ? "true" : "false") << "\n";
|
||||
|
||||
@@ -700,6 +700,12 @@ struct GameConfig {
|
||||
bool ws_offered = true;
|
||||
bool vulkan_offered = false;
|
||||
|
||||
// [widescreen] adaptive_view — let the user opt into a live, resize-driven
|
||||
// aspect instead of selecting only fixed 4:3/16:9/21:9 modes. The fixed
|
||||
// aspect remains the initial window shape; the live view is clamped to the
|
||||
// widest aspect this title offers.
|
||||
bool ws_adaptive_view = false;
|
||||
|
||||
// [widescreen] offer_ultrawide — expose a separate experimental 21:9
|
||||
// launcher choice for titles that have explicitly tested it. Default off;
|
||||
// ordinary widescreen offer remains the independent 16:9 choice.
|
||||
@@ -810,6 +816,7 @@ struct UserSettings {
|
||||
bool has_netplay_lobby_url = false; std::string netplay_lobby_url;
|
||||
bool has_aspect_ratio = false; int aspect_num = 4; // display aspect W:H
|
||||
int aspect_den = 3; // (4:3 = native)
|
||||
bool has_adaptive_view = false; bool adaptive_view = false;
|
||||
// [audio]
|
||||
bool has_spu_hq = false; bool spu_hq = false;
|
||||
// [bios] / [disc] / [memcard]
|
||||
|
||||
+69
-11
@@ -82,6 +82,7 @@ static int ws_hud_sprt = 0; /* edge-anchor untagged HUD SPRTs *
|
||||
* in native-wide mode. */
|
||||
static int ws_mode = 0;
|
||||
static int ws_cfg_num = 4, ws_cfg_den = 3;
|
||||
static void ws_nw_sync_target(void);
|
||||
|
||||
#define WS_TAG_BUCKETS 4096 /* power of two */
|
||||
#define WS_TAG_PROBES 8
|
||||
@@ -279,13 +280,22 @@ static int ws_active(void) { return ws_configured() && !gpu_ws_present_native_43
|
||||
int ws_native_wide_active(void) {
|
||||
return ws_mode == 2 && !gpu_ws_present_native_43();
|
||||
}
|
||||
/* Cull/spawn setup often runs while a scene is loading, before the first GTE
|
||||
* frame can classify it as gameplay. Keep that pre-render setup aware of the
|
||||
* configured native-wide viewport; presentation itself remains gated by
|
||||
* ws_native_wide_active(). */
|
||||
static int ws_native_wide_configured(void) {
|
||||
return ws_mode == 2 && ws_cfg_num * 3 > ws_cfg_den * 4;
|
||||
}
|
||||
static int ws_nw_configured_offset(void) {
|
||||
if (!ws_native_wide_configured()) return 0;
|
||||
int numr = 3 * ws_cfg_num - 4 * ws_cfg_den;
|
||||
int w = (int)ws_disp_w();
|
||||
return (w * numr + 4 * ws_cfg_den) / (8 * ws_cfg_den);
|
||||
}
|
||||
static int ws_nw_offset(void) {
|
||||
if (!ws_native_wide_active()) return 0;
|
||||
int numr = 3 * ws_cfg_num - 4 * ws_cfg_den; /* > 0 for aspects wider than 4:3 */
|
||||
if (numr <= 0) return 0;
|
||||
int w = (int)ws_disp_w();
|
||||
int v = (w * numr + 4 * ws_cfg_den) / (8 * ws_cfg_den); /* round-to-nearest */
|
||||
return v;
|
||||
return ws_nw_configured_offset();
|
||||
}
|
||||
int ws_nw_extra(void) { return 2 * ws_nw_offset(); }
|
||||
|
||||
@@ -446,7 +456,12 @@ int psx_ws_x_margin(void) {
|
||||
* that previously fell outside the 4:3 cull window; the wide compositor then
|
||||
* rasterizes it into the revealed margins. Same recompiler emit sites as the
|
||||
* squash path ([widescreen.cull]); 0 at 4:3 so the cull stays byte-identical. */
|
||||
if (ws_native_wide_active()) return ws_nw_offset() + ws_cull_guard_pixels;
|
||||
/* Unlike rendering/presentation, do not wait for game-mode detection here.
|
||||
* Tomba 2 builds its terrain-cell and actor spawn lists during scene load;
|
||||
* returning zero until the first 3D frame permanently bakes a 4:3 frustum
|
||||
* into those lists. */
|
||||
if (ws_native_wide_configured())
|
||||
return ws_nw_configured_offset() + ws_cull_guard_pixels;
|
||||
if (!ws_active()) return 0;
|
||||
return (160 * (ws_xden - ws_xnum) + ws_xnum / 2) / ws_xnum;
|
||||
}
|
||||
@@ -1261,6 +1276,10 @@ void gpu_ws_configure(int aspect_num, int aspect_den,
|
||||
}
|
||||
ws_anchor_addr = sprite_anchor_addr;
|
||||
ws_hud_sprt = hud_sprt_squash;
|
||||
/* Adaptive view can change mode/extent without the guest reissuing E3/E4
|
||||
* immediately. Keep the active mirror target and its scissor in lockstep
|
||||
* with the new live aspect instead of waiting for another draw-env packet. */
|
||||
ws_nw_sync_target();
|
||||
}
|
||||
|
||||
/* Called from generated code at the entry of each [widescreen]
|
||||
@@ -2349,9 +2368,44 @@ static void raster_pixel(int32_t x, int32_t y, uint16_t color) {
|
||||
* clips those for free; our GL path was building two triangles per clipped
|
||||
* prim (gpu_share ~0.9, host FPS ~5–10). Skip the host rasterizer when the
|
||||
* post-offset bbox cannot touch the draw area. Side effects that must still
|
||||
* run (texpage latch, oversize reject) happen before these checks. */
|
||||
* run (texpage latch, oversize reject) happen before these checks.
|
||||
*
|
||||
* Native-wide is intentionally different in X: the mirror renderer translates
|
||||
* canonical framebuffer coordinates by the live reveal offset and scissors to
|
||||
* the FULL wide surface, not GP0(E3/E4)'s 4:3 X range. Commit 31015ce originally
|
||||
* compared every primitive only with the guest draw area here, rejecting the
|
||||
* margin geometry before the mirror renderer could see it. Match the mirror's
|
||||
* exact X extent while its framebuffer target is active; Y remains the guest
|
||||
* draw area because native-wide does not extend vertically. At 4:3, in FMV/
|
||||
* menus, in squash mode, and for offscreen texture targets, margin is zero and
|
||||
* this remains the original fast reject byte-for-byte.
|
||||
*
|
||||
* OpokXeno independently identified the same host-side regression on Xenogears
|
||||
* and contributed the original generalized fix in psxrecomp PR #73:
|
||||
* https://github.com/mstan/psxrecomp/pull/73
|
||||
* Keep that credit with this guarded framebuffer-target variant. */
|
||||
static inline int32_t draw_area_wide_x_margin(void) {
|
||||
if (!ws_native_wide_active() || !ws_is_fb_base(draw_area_left)) return 0;
|
||||
return (int32_t)ws_nw_offset();
|
||||
}
|
||||
|
||||
static inline void draw_area_host_x_bounds(int32_t *left, int32_t *right) {
|
||||
int32_t margin = draw_area_wide_x_margin();
|
||||
if (margin > 0) {
|
||||
/* ws_nw_sync_target uses draw_area_left as the framebuffer base and
|
||||
* configures a surface ws_disp_w()+2*margin pixels wide. */
|
||||
*left = (int32_t)draw_area_left - margin;
|
||||
*right = (int32_t)draw_area_left + (int32_t)ws_disp_w() + margin - 1;
|
||||
} else {
|
||||
*left = (int32_t)draw_area_left;
|
||||
*right = (int32_t)draw_area_right;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int draw_area_out_point(int32_t x, int32_t y) {
|
||||
return x < (int32_t)draw_area_left || x > (int32_t)draw_area_right
|
||||
int32_t left, right;
|
||||
draw_area_host_x_bounds(&left, &right);
|
||||
return x < left || x > right
|
||||
|| y < (int32_t)draw_area_top || y > (int32_t)draw_area_bottom;
|
||||
}
|
||||
|
||||
@@ -2363,14 +2417,18 @@ static inline int draw_area_out_bbox(const int32_t *vx, const int32_t *vy, int n
|
||||
if (vy[i] < miny) miny = vy[i];
|
||||
if (vy[i] > maxy) maxy = vy[i];
|
||||
}
|
||||
return maxx < (int32_t)draw_area_left || minx > (int32_t)draw_area_right
|
||||
int32_t left, right;
|
||||
draw_area_host_x_bounds(&left, &right);
|
||||
return maxx < left || minx > right
|
||||
|| maxy < (int32_t)draw_area_top || miny > (int32_t)draw_area_bottom;
|
||||
}
|
||||
|
||||
static inline int draw_area_out_rect(int32_t x, int32_t y, int w, int h) {
|
||||
if (w <= 0 || h <= 0) return 1;
|
||||
return (x + w - 1) < (int32_t)draw_area_left
|
||||
|| x > (int32_t)draw_area_right
|
||||
int32_t left, right;
|
||||
draw_area_host_x_bounds(&left, &right);
|
||||
return (x + w - 1) < left
|
||||
|| x > right
|
||||
|| (y + h - 1) < (int32_t)draw_area_top
|
||||
|| y > (int32_t)draw_area_bottom;
|
||||
}
|
||||
|
||||
+108
-13
@@ -556,6 +556,12 @@ extern "C" void debug_get_fmv_config(int *auto_skip, uint32_t *total_table,
|
||||
* in config_loader.h). */
|
||||
static int g_video_aspect_num = 4;
|
||||
static int g_video_aspect_den = 3;
|
||||
/* Resize-driven widescreen. The user's fixed aspect is still used to shape the
|
||||
* initial window; after the game window exists these values follow its live
|
||||
* aspect, clamped to 4:3..the widest mode offered by the title. */
|
||||
static bool g_ws_adaptive_view = false;
|
||||
static int g_ws_adaptive_max_num = 16;
|
||||
static int g_ws_adaptive_max_den = 9;
|
||||
/* [widescreen] per-game hooks (see config_loader.h): anchor scratch addr for
|
||||
* tagged sprite prims + HUD SPRT center-squash. Inert at 0/false. */
|
||||
static uint32_t g_ws_anchor_addr = 0;
|
||||
@@ -593,6 +599,53 @@ static void clamp_window_aspect(int* w, int* h, int num, int den) {
|
||||
*h = width * den / num;
|
||||
}
|
||||
|
||||
static int aspect_gcd(int a, int b) {
|
||||
while (b) { int t = a % b; a = b; b = t; }
|
||||
return a > 0 ? a : 1;
|
||||
}
|
||||
|
||||
/* Follow the host window without feeding its absolute pixel size into guest
|
||||
* rendering. Only the ratio matters: gpu_ws_configure derives the PSX-native
|
||||
* sidecar width from it, just as the fixed 16:9/21:9 modes do. */
|
||||
static void update_adaptive_widescreen() {
|
||||
if (!g_ws_adaptive_view || !sdl_window) return;
|
||||
|
||||
int width = 0, height = 0;
|
||||
SDL_GetWindowSize(sdl_window, &width, &height);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
int num = width, den = height;
|
||||
if ((int64_t)width * 3 <= (int64_t)height * 4) {
|
||||
num = 4; den = 3;
|
||||
} else if ((int64_t)width * g_ws_adaptive_max_den >=
|
||||
(int64_t)height * g_ws_adaptive_max_num) {
|
||||
num = g_ws_adaptive_max_num;
|
||||
den = g_ws_adaptive_max_den;
|
||||
} else {
|
||||
int divisor = aspect_gcd(num, den);
|
||||
num /= divisor;
|
||||
den /= divisor;
|
||||
}
|
||||
if (num == g_video_aspect_num && den == g_video_aspect_den) return;
|
||||
|
||||
g_video_aspect_num = num;
|
||||
g_video_aspect_den = den;
|
||||
gl_renderer_set_display_aspect(num, den);
|
||||
if (sdl_renderer) {
|
||||
g_logical_w = 480 * num * g_video_scale / den;
|
||||
SDL_RenderSetLogicalSize(sdl_renderer, g_logical_w, 480 * g_video_scale);
|
||||
}
|
||||
|
||||
if (g_ws_engaged) {
|
||||
const bool wide = num * 3 != den * 4;
|
||||
const int mode = wide ? (g_ws_native_wide ? 2 : 1) : 0;
|
||||
gte_set_display_aspect(mode == 1 ? num : 4,
|
||||
mode == 1 ? den : 3);
|
||||
gpu_ws_configure(num, den, g_ws_anchor_addr,
|
||||
g_ws_hud_sprt ? 1 : 0, mode);
|
||||
}
|
||||
}
|
||||
|
||||
/* SDL GL attributes are global inputs to the next context creation. Set the
|
||||
* runtime requirements immediately before every GL window, because launcher
|
||||
* teardown resets them and macOS otherwise supplies a legacy 2.1 context. */
|
||||
@@ -3437,12 +3490,17 @@ static void sdl_vblank_present(void) {
|
||||
/* Mod hooks. Run after all normal input sampling. */
|
||||
mod_call_frame_hooks();
|
||||
|
||||
/* Resize-driven mode updates the pending aspect during BIOS boot too, but
|
||||
* the actual wide compositor remains disengaged until game entry. */
|
||||
update_adaptive_widescreen();
|
||||
|
||||
/* Engage widescreen at game entry: BIOS boot stays authentic 4:3. */
|
||||
if (!g_ws_engaged) {
|
||||
extern int fntrace_is_game_started(void);
|
||||
if (fntrace_is_game_started()) {
|
||||
g_ws_engaged = true;
|
||||
int mode = g_ws_native_wide ? 2 : 1;
|
||||
const bool wide = g_video_aspect_num * 3 != g_video_aspect_den * 4;
|
||||
int mode = wide ? (g_ws_native_wide ? 2 : 1) : 0;
|
||||
/* Native-wide: GTE drawn un-squashed — feed it the 4:3 ratio
|
||||
* (identity squash). Squash mode: feed the real wide aspect. */
|
||||
gte_set_display_aspect(mode == 1 ? g_video_aspect_num : 4,
|
||||
@@ -4522,6 +4580,7 @@ int main(int argc, char** argv) {
|
||||
int ctrl_locked_p2_mode = PSXRecompV4::PAD_MODE_HYBRID;
|
||||
bool ws_offered = true; /* game.toml [widescreen] offer; false hides the launcher toggle + clamps 4:3 */
|
||||
bool ws_ultrawide_offered = false;
|
||||
bool ws_adaptive_view_supported = false;
|
||||
bool vulkan_offered = false; /* game.toml [video] offer_vulkan; developer opt-in for launcher visibility */
|
||||
int resolved_deadzone = -1; /* <0 => keep input.ini/runtime default (12000) */
|
||||
/* Localization: the effective language (game.toml default -> settings.toml ->
|
||||
@@ -4695,6 +4754,7 @@ int main(int argc, char** argv) {
|
||||
gc.ws_cull_h_imms.data(), (int)gc.ws_cull_h_imms.size());
|
||||
ws_offered = gc.ws_offered;
|
||||
ws_ultrawide_offered = gc.ws_ultrawide_offered;
|
||||
ws_adaptive_view_supported = gc.ws_adaptive_view;
|
||||
vulkan_offered = gc.vulkan_offered;
|
||||
/* Register the [widescreen.backdrop] store PCs so the dirty-RAM
|
||||
* interpreter applies the backdrop screenX squash on the interp
|
||||
@@ -5000,6 +5060,7 @@ int main(int argc, char** argv) {
|
||||
g_video_aspect_num = us.aspect_num;
|
||||
g_video_aspect_den = us.aspect_den;
|
||||
}
|
||||
if (us.has_adaptive_view) g_ws_adaptive_view = us.adaptive_view;
|
||||
if (us.has_spu_hq) g_audio_spu_hq = us.spu_hq;
|
||||
if (us.has_bios_path && !bios_from_cli) {
|
||||
settings_bios_storage = us.bios_path.string();
|
||||
@@ -5058,6 +5119,9 @@ int main(int argc, char** argv) {
|
||||
g_video_aspect_num = ws_offered ? 16 : 4;
|
||||
g_video_aspect_den = ws_offered ? 9 : 3;
|
||||
}
|
||||
if (!ws_adaptive_view_supported) g_ws_adaptive_view = false;
|
||||
g_ws_adaptive_max_num = ws_ultrawide_offered ? 21 : 16;
|
||||
g_ws_adaptive_max_den = 9;
|
||||
|
||||
/* Latency knobs: env overrides win over config (for A/B measurement).
|
||||
* PSX_LOW_LATENCY_INPUT=0/1 ; PSX_VSYNC=1(vsync)/0(immediate)/-1(adaptive);
|
||||
@@ -5173,6 +5237,7 @@ int main(int argc, char** argv) {
|
||||
seed.has_frame_interpolation_fps = true;
|
||||
seed.aspect_num = g_video_aspect_num;
|
||||
seed.aspect_den = g_video_aspect_den; seed.has_aspect_ratio = true;
|
||||
seed.adaptive_view = g_ws_adaptive_view; seed.has_adaptive_view = true;
|
||||
seed.spu_hq = g_audio_spu_hq; seed.has_spu_hq = true;
|
||||
seed.skip_launcher = skip_launcher_setting; seed.has_skip_launcher = true;
|
||||
if (has_netplay_player_name) {
|
||||
@@ -5231,9 +5296,13 @@ int main(int argc, char** argv) {
|
||||
has_netplay_player_name ? netplay_player_name.c_str() : "");
|
||||
ls.pad_mode[0] = seed.p1_mode;
|
||||
ls.pad_mode[1] = seed.p2_mode;
|
||||
/* aspect_index: 0 = 4:3, 1 = 16:9, 2 = 21:9 (see RecompLauncherCSettings). */
|
||||
ls.aspect_index = (seed.aspect_num * 9 == seed.aspect_den * 21) ? 2 :
|
||||
(seed.aspect_num == 16 && seed.aspect_den == 9) ? 1 : 0;
|
||||
/* Adaptive is part of the aspect vocabulary, not a second toggle.
|
||||
* It follows 21:9 when ultrawide is offered, otherwise 16:9. */
|
||||
const int adaptive_aspect_index = ws_ultrawide_offered ? 3 : 2;
|
||||
ls.aspect_index = seed.adaptive_view ? adaptive_aspect_index :
|
||||
(seed.aspect_num * 9 == seed.aspect_den * 21) ? 2 :
|
||||
(seed.aspect_num == 16 && seed.aspect_den == 9) ? 1 : 0;
|
||||
ls.adaptive_view = seed.adaptive_view ? 1 : 0;
|
||||
|
||||
/* ---- deeper PSX-style settings (capability-gated via launcher_profile
|
||||
* below). Sourced 1:1 from PSXRecompV4::UserSettings (config_loader.h). */
|
||||
@@ -5301,6 +5370,17 @@ int main(int argc, char** argv) {
|
||||
"OpenGL (Recommended)",
|
||||
"Vulkan"
|
||||
};
|
||||
static const char* const kAdaptiveAspectLabels[] = {
|
||||
"4:3 (Native)",
|
||||
"16:9 (Widescreen)",
|
||||
"21:9 (Ultrawide)",
|
||||
"Adaptive"
|
||||
};
|
||||
static const char* const kAdaptiveAspectLabelsNoUltrawide[] = {
|
||||
"4:3 (Native)",
|
||||
"16:9 (Widescreen)",
|
||||
"Adaptive"
|
||||
};
|
||||
/* System identity + full PS1 settings-surface capability set (theme,
|
||||
* platform label, rom_noun, pad-mode support, aspect mask base, and
|
||||
* all has_* deep-settings flags) — one profile call keeps them from
|
||||
@@ -5323,6 +5403,12 @@ int main(int argc, char** argv) {
|
||||
gi.locked_pad_mode = p1_mode; /* force the game's declared mode (default_mode) */
|
||||
gi.lock_device = ctrl_lock_device ? 1 : 0;
|
||||
gi.aspect_mask = 0x1 | (ws_offered ? 0x2 : 0) | (ws_ultrawide_offered ? 0x4 : 0);
|
||||
if (ws_adaptive_view_supported) {
|
||||
gi.aspect_labels = ws_ultrawide_offered
|
||||
? kAdaptiveAspectLabels : kAdaptiveAspectLabelsNoUltrawide;
|
||||
gi.num_aspect_labels = ws_ultrawide_offered ? 4 : 3;
|
||||
gi.aspect_experimental = 1;
|
||||
}
|
||||
gi.renderer_labels = kPsxRendererLabels;
|
||||
gi.num_renderers = vulkan_offered ? 3 : 2;
|
||||
/* Localization menu: shown only when the game declares languages. */
|
||||
@@ -5364,14 +5450,21 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
seed.fullscreen = ls.fullscreen; seed.has_fullscreen = true;
|
||||
seed.skip_launcher = ls.skip_launcher != 0; seed.has_skip_launcher = true;
|
||||
/* aspect_index round-trips 0/1/2 -> 4:3 / 16:9 / 21:9, superseding the
|
||||
* legacy ls.widescreen bool (still set above for older callers). */
|
||||
switch (ls.aspect_index) {
|
||||
case 2: seed.aspect_num = 21; seed.aspect_den = 9; break;
|
||||
case 1: seed.aspect_num = 16; seed.aspect_den = 9; break;
|
||||
default: seed.aspect_num = 4; seed.aspect_den = 3; break;
|
||||
/* Adaptive preserves the previous fixed aspect as the initial
|
||||
* window shape. Selecting a fixed entry turns adaptation off. */
|
||||
if (ws_adaptive_view_supported &&
|
||||
ls.aspect_index == adaptive_aspect_index) {
|
||||
seed.adaptive_view = true;
|
||||
} else {
|
||||
seed.adaptive_view = false;
|
||||
switch (ls.aspect_index) {
|
||||
case 2: seed.aspect_num = 21; seed.aspect_den = 9; break;
|
||||
case 1: seed.aspect_num = 16; seed.aspect_den = 9; break;
|
||||
default: seed.aspect_num = 4; seed.aspect_den = 3; break;
|
||||
}
|
||||
}
|
||||
seed.has_aspect_ratio = true;
|
||||
seed.has_adaptive_view = true;
|
||||
/* has_texture_filter is on (PSX profile) so the launcher edits
|
||||
* ls.texture_filter directly (0=nearest,1=bilinear); ls.linear_filter
|
||||
* is the legacy fallback field for consoles without the cap and is
|
||||
@@ -5473,6 +5566,7 @@ int main(int argc, char** argv) {
|
||||
g_frame_interpolation_fps = seed.frame_interpolation_fps;
|
||||
g_video_aspect_num = seed.aspect_num;
|
||||
g_video_aspect_den = seed.aspect_den;
|
||||
g_ws_adaptive_view = seed.adaptive_view;
|
||||
g_audio_spu_hq = seed.spu_hq;
|
||||
skip_launcher_setting = seed.skip_launcher;
|
||||
if (seed.has_bios_path) {
|
||||
@@ -5592,17 +5686,18 @@ session_reboot:
|
||||
* this aspect; native-wide fills it with a genuinely wider frame (no
|
||||
* stretch), squash mode stretches the 4:3 frame into it. */
|
||||
gl_renderer_set_display_aspect(g_video_aspect_num, g_video_aspect_den);
|
||||
if (g_video_aspect_num * 3 != g_video_aspect_den * 4) {
|
||||
if (g_video_aspect_num * 3 != g_video_aspect_den * 4 || g_ws_adaptive_view) {
|
||||
/* Hold widescreen off through the BIOS boot (authentic 4:3 logos);
|
||||
* the per-frame present path engages it at game entry. */
|
||||
g_ws_engaged = false;
|
||||
std::fprintf(stdout,
|
||||
"psxrecomp: widescreen %d:%d (%s%s%s; engages at game entry)\n",
|
||||
"psxrecomp: widescreen %d:%d (%s%s%s%s; engages at game entry)\n",
|
||||
g_video_aspect_num, g_video_aspect_den,
|
||||
g_ws_native_wide ? "native-wide, present 1:1"
|
||||
: "GTE X-squash + stretched present",
|
||||
g_ws_anchor_addr ? " + sprite tags" : "",
|
||||
g_ws_hud_sprt ? " + HUD squash" : "");
|
||||
g_ws_hud_sprt ? " + HUD squash" : "",
|
||||
g_ws_adaptive_view ? " + adaptive view" : "");
|
||||
}
|
||||
/* Present-time screen-colour model (verified-enhancement LUT). Default raw
|
||||
* is byte-identical; PSX_SCREEN env overrides this at scanout. */
|
||||
|
||||
Reference in New Issue
Block a user