docs(widescreen): document 21:9 as stubbed-but-hidden + the 3-way future

The launcher widescreen toggle is settled at Off / 16:9. 21:9 stays supported in
the engine (all aspect-derived: cull, compositor, present) but is intentionally
NOT exposed: the parallax/far-backdrop pipeline only generates ~16:9 of coverage,
so 21:9 voids the far background (8C generation work, possibly bounded by the
game's authored coverage). When that is widened, promote the 2-state toggle to a
3-way Off / 16:9 / 21:9 (cycle_aspect already cycles aspect_index 0/1/2 = the
scaffold). Comment at toggle_widescreen + status section in WIDESCREEN.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-06-15 17:51:05 -07:00
parent d0d199360c
commit 890a1cbf52
2 changed files with 42 additions and 2 deletions
+33
View File
@@ -228,3 +228,36 @@ F:/Projects/psxrecomp/psxrecomp/recompiler/build/psxrecomp-game.exe --config gam
```
`build-stable` is RelWithDebInfo + debug tools (TCP port 4470) + launcher ON.
Always `taskkill /F /IM psx-runtime.exe` before relaunching.
## Native-wide 16:9 (shipped) and 21:9 (stubbed, hidden)
The shipped enhancement is **native-wide 16:9**, exposed as the launcher's
experimental Widescreen On/Off toggle (software renderer only; hidden on
OpenGL). It is off by default (`game.toml [video] aspect_ratio = "4:3"`), so the
default build is byte-identical to faithful. On = `16:9`. The dominant fix is
`[widescreen.cull] auto_screen_x` — the recompiler auto-detects the GTE
render-funnel screen-extent reject (`sltiu …,0x140` width paired with
`sltiu …,0xE0` height) and widens every width compare through
`psx_ws_x_margin()`, sign-extended so both margins reveal. Full-screen 2D
overlays (pause dim, load fade) are extended across the wide compositor surface.
**21:9 is intentionally stubbed and hidden.** Everything numeric is
aspect-derived (`ws_nw_offset` etc.), so `aspect_ratio = "21:9"` already widens
the cull, compositor, and present correctly — the **3D world and foreground fill
21:9 for free**. It is *not* offered in the UI because two layers don't reach
21:9:
- **Background** — the parallax + far-backdrop pipeline only *generates* ~16:9
of coverage (per-layer spawn windows in the `FUN_80027600` builders +
`FUN_801216BC`-family overlay handlers). The per-prim cull already widens past
this; the tiles simply aren't produced, so 21:9 voids the far background. This
is the 8C generation work, and may hit the game's *authored* coverage limit
(beyond which only tile/stretch/void remain) — which is what caps clean
widescreen at 16:9.
- **Foreground edge tips** — the cull widens exactly to the display surface
edge; fixable by over-rendering (decouple the render aspect from the display
aspect) — low effort, but pointless until the background is solved.
To enable 21:9 later: widen the backdrop generation, then promote the launcher's
2-state toggle to a 3-way Off / 16:9 / 21:9 (the `cycle_aspect` callback already
cycles `aspect_index` 0/1/2 and is the scaffold). Full feasibility analysis in
the session memory `native_wide_fov_autocull.md`.
+9 -2
View File
@@ -728,8 +728,15 @@ Result run(SDL_Window* window, void* gl_context,
handle.DirtyVariable("winsize_label"); /* height follows aspect */
});
// EXPERIMENTAL widescreen On/Off (software renderer only). On => 16:9 native-
// wide (aspect_index 1), Off => 4:3. The toggle is hidden when ws_eligible is
// false (non-software renderer), so this is a no-op there as a safety net.
// wide (aspect_index 1), Off => 4:3 (aspect_index 0). Hidden when ws_eligible
// is false (non-software renderer), so this is a no-op there as a safety net.
//
// 21:9 (kAspects[2]) is STUBBED but intentionally hidden: the engine handles
// it (offset / cull / compositor are all aspect-derived), but the parallax +
// far-backdrop pipeline only generates ~16:9 of coverage, so 21:9 voids the
// far background. When that pipeline is widened, promote this 2-state toggle
// to a 3-way Off / 16:9 / 21:9 — the existing cycle_aspect callback already
// cycles aspect_index 0/1/2 and is the scaffold for it.
c.BindEventCallback("toggle_widescreen",
[&m, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
if (m.renderer != 0) return; // SW-only