Skip to content

feat(host): a widescreen picture up to 5120x2160's pixels, native for 21:9 at the 4K output - #2120

Merged
hckrman101 merged 2 commits into
NotRexed:mainfrom
pmwork-personal:feat/wide-past-4k
Nov 11, 2026
Merged

hckrman101 merged 2 commits into
NotRexed:mainfrom
pmwork-personal:feat/wide-past-4k

Conversation

@pmwork-personal

@pmwork-personal pmwork-personal commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

On a 5120x2160 (21:9) monitor, the 3840x2160 output with Ultrawide on made a 4416x1864 picture, which the window copy then stretched to the screen: the monitor never got a native frame, and the Output resolution row showed 4416x1864. The cap behind it, 3840x2160's pixels, came from issue 1031, where a 32:9 screen's 7680x2160 picture ran its render targets' pool out. A 21:9 screen's native picture needs a third more pixels than 4K, not twice as many, and the pool already grows with the picture.

What changed

  • host/hos_wide_size.h: a picture wider or narrower than 16:9 takes at most 5120x2160's pixels (was 3840x2160's). At the 4K output a 21:9 screen gets its native 5120x2160, and the Output resolution row says "5120x2160 (native)". A 32:9 screen at the 4K output is still capped, now at 6256x1760 (was 5400x1520), its shape kept.
  • No pool change: host/nn_applet.c already scales the render-target pool with the picture's pixels, to 915 MB at 5120x2160 (test_rt_pool_minimum.py now checks that case).
  • HOS_WIDE_MAX_PX=<pixels> sets another cap without a rebuild (8294400 is the old 3840x2160's), in case a GPU needs the smaller picture. Listed in docs/development.md; the readme gives the new cap.

Relation to #2052
Merged with current main; #2052's code is kept as merged (the output limits, hos_wide_clamp up to 48:9, the 24-row alignment at 48:9). With the new cap:

  • The 7680x1440 output renders at full size, since it has exactly 5120x2160's pixels, instead of 6528x1224. Its pool stays 900 MB, set by the output's own size as before; the 5120x2880 output already plays in that pool with more pixels.
  • 48:9 at the 4K output is kept to 7680x1440, exactly 48:9, with a 915 MB pool: the same pixels and pool as 21:9's 5120x2160.
  • test_widescreen_shapes.py keeps feat(video): support 48:9 triple-monitor picture geometry #2052's 6528x1224 cases (the 24-row alignment and the four-decimal 5.3333) under HOS_WIDE_MAX_PX=8294400, the old cap, and adds the full-size 7680x1440 and 48:9 at 2160. test_output_resolution.py's 7680x1440 output is full size; test_rt_pool_minimum.py adds 48:9 at the 4K output.

Tested

  • In game on Windows 11, RTX 5090 (driver 621.14), 5120x2160 (21:9) monitor, 4K output, picture at the monitor's shape: picture 5120x2160, pool 915 MB, DLAA at 5120x2160. Kokiri Forest and Castle Town, where issue 1031's pool ran out, load. About 57 fps at native DLAA (GPU about 12 ms a frame). That run was before the merge with feat(video): support 48:9 triple-monitor picture geometry #2052.
  • tests/run_all.py on Windows (llvm-mingw) after the merge: 339 passed, 30 skipped, 4 failed. The 4 also fail on untouched main on this PC: test_recorder_sync (its exit case), test_precompile_compute_guard, test_bake_portability, and nx2/test_nup_read_name (no zstandard here). test_widescreen_shapes.py, test_output_resolution.py, test_rt_pool_minimum.py and gpu/vk/tests/present_rect_test.cpp pass.

Not tested

  • 48:9 and 32:9 in game at the new sizes (the full-size 7680x1440 output, 48:9 and 32:9 at the 4K output).
  • GPUs with less memory than the 5090 at these sizes, and Linux.

This and the Aspect ratio PR (#2119) edit the same readme paragraph, so whichever merges second needs a one-line merge.

🤖 Generated with Claude Code

pmwork-personal and others added 2 commits November 10, 2026 21:18
… 21:9 at the 4K output

On a 5120x2160 (21:9) monitor the 3840x2160 output with Ultrawide on
made a 4416x1864 picture, which the window copy then stretched to the
screen: the monitor never got a native frame, and the Output resolution
row showed 4416x1864. The cap behind it, 3840x2160's pixels, came from
issue 1031, where a 32:9 screen's 7680x2160 picture ran its render
targets' pool out. A 21:9 screen's native picture needs a third more
pixels than 4K, not twice as many, and the pool already grows with the
picture.

What changed:
- host/hos_wide_size.h: a picture wider or narrower than 16:9 takes at
  most 5120x2160's pixels (was 3840x2160's). At the 4K output a 21:9
  screen gets its native 5120x2160, and the Output resolution row says
  "5120x2160 (native)". A 32:9 screen at the 4K output is still capped,
  now at 6256x1760 (was 5400x1520), its shape kept.
- No pool change: host/nn_applet.c already scales the render-target
  pool with the picture's pixels, to 915 MB at 5120x2160.
- HOS_WIDE_MAX_PX=<pixels> sets another cap without a rebuild (8294400
  is the old 3840x2160's), in case a GPU needs the smaller picture.
  Listed in docs/development.md; the readme's Output resolution notes
  give the new cap.
- Tests: test_widescreen_shapes.py (21:9 and 32:9 at 2160, and the
  override), test_output_resolution.py (21:9 at the 4K output:
  "5120x2160 (native)"), test_rt_pool_minimum.py (5120x2160: 915 MB).

Tested:
- In game on Windows 11, RTX 5090 (driver 621.14), 5120x2160 (21:9)
  monitor, 4K output, picture at the monitor's shape: picture
  5120x2160, pool 915 MB, DLAA at 5120x2160. Kokiri Forest and Castle
  Town, where issue 1031's pool ran out, load. About 57 fps at native
  DLAA (GPU about 12 ms a frame).
- The three tests above pass with llvm-mingw.
- tests/run_all.py on Windows: 326 passed, 26 skipped, 4 failed. The
  4 touch none of these files and fail the same on
  feat/aspect-monitor-default, which lacks this change:
  test_recorder_sync (its exit case), test_precompile_compute_guard
  (no windows.h for the MSVC target on this PC),
  nx2/test_nup_read_name (no zstandard module here),
  test_make_game_ngx (the Linux builder test's stub has no
  switching()).

Not tested:
- A 32:9 screen at the 4K output in game (its picture grows to
  6256x1760, its pool to about 915 MB too), and GPUs with less memory
  than the 5090 at these sizes.
- Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in NotRexed#2052 (48:9 surround), NotRexed#2041 and the rest of main. NotRexed#2052's
code is kept as merged (output limits, hos_wide_clamp up to 48:9, the
24-row alignment at 48:9); the cap and HOS_WIDE_MAX_PX sit on top.

With the 5120x2160-pixel cap, 48:9 comes out as:
- the 7680x1440 output: full size (it has exactly 5120x2160's pixels)
  instead of 6528x1224. Its pool stays 900 MB, set by the output's own
  size as before; the 5120x2880 output already plays in that pool with
  more pixels.
- 48:9 at the 4K output (11520x2160 wanted): kept to 7680x1440, exactly
  48:9, pool 915 MB, the same pixels and pool as 21:9's 5120x2160.

Tests follow: test_widescreen_shapes.py takes the full-size 7680x1440
and a 48:9-at-2160 case, and keeps NotRexed#2052's 6528x1224 cases (the 24-row
alignment and the four-decimal 5.3333) under HOS_WIDE_MAX_PX=8294400,
the old cap; test_output_resolution.py's 7680x1440 output is full size
(its scene special case is no longer needed); test_rt_pool_minimum.py
has 48:9 at the 4K output (915 MB). The readme's 7680x1440 sentence
and the cap follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks right to me. I checked the render-target pool for each new size against hos_rt_pool_size (host/nn_applet.c:180):

  • 21:9 at the 4K output (5120x2160) gets 305 × 11.06 / 3.69 ≈ 915 MB. The output itself isn't above 4K's pixels, so the 900 MB cap doesn't apply.
  • 32:9 at the 4K output (6256x1760) gets about 911 MB, scaled with the picture as #1031 needed.
  • The 7680x1440 output now renders a full 7680x1440 picture. That output is above 4K's pixels, so the pool stays capped at 900 MB, and as you say, the 5120x2880 output already plays in 900 MB with more pixels (14.7M against 11.06M).

The test changes move expectations to the new sizes and keep #2052's 24-row and 5.3333 cases under HOS_WIDE_MAX_PX=8294400 instead of dropping them, which is what I'd want. Stripping HOS_WIDE_MAX_PX from the fixture's environment keeps a developer's own setting from leaking in. The new switch is documented in docs/development.md, the readme sentence is updated, and the host and menu agree because both call the same hos_wide_max_px() (the menu already reads HOS_OUTPUT_RES the same way). It merges cleanly with current main, and CI is green.

A few small things, none blocking:

  • minor host/hos_wide_size.h:35-39: HOS_WIDE_MAX_PX has a floor but no ceiling. The documented use is lowering the cap, but a value above the default at the 4K output brings back exactly #1031. 7680x2160 asks for a 1373 MB pool, and the description of #1031 says a pool that size failed the game's reservation at boot. Clamping it (v > 5120.0 * 2160.0 ? 5120.0 * 2160.0 : v), or saying in the development.md row that it's for lowering only, would stop someone "raising" it into a crash.
  • question: The default now gives 21:9 players at the 4K output 34% more pixels than before (5120x2160 against 4416x1864), and in-game testing so far is on a 5090. On an 8 GB card at that output, is there a VRAM risk worth a line in the readme pointing at HOS_WIDE_MAX_PX=8294400? As it stands, the only way back is an undocumented-for-players environment variable. Not a blocker; just asking whether you looked at memory use on a smaller card.
  • minor: As you note, this and #2119 touch the same readme paragraph. Whichever lands second needs the one-line merge.

@hckrman101
hckrman101 merged commit d379d88 into NotRexed:main Nov 11, 2026
13 checks passed
hckrman101 added a commit that referenced this pull request Nov 11, 2026
…ape by default (#2119)

A player on an ultrawide, 16:10 or 4:3 screen starts with the game's
16:9 picture until they find the Graphics tab's **Ultrawide** row,
change it and restart. The name also hides that the same row shapes the
picture on screens narrower than 16:9. PC games usually follow the
monitor's shape from the first start, with 16:9 as a choice.

**What changed**
- The **Ultrawide** row is now **Aspect ratio**, and **Monitor's shape**
is its default (`gpu/vk/vk_menu.h`; `host/hos_main.c` reads an options
file with no `ultrawide=` line as Monitor's shape). 16:9 stays a choice.
On a 16:9 screen nothing changes.
- The saved key stays `ultrawide=` (`ini_key`), so a saved `ultrawide=0`
still means 16:9.
- **Older options files on a screen narrower than 16:9.** The game saves
every row, so a file from before this PR holds
`ultrawide=0`, the old default, whether chosen or not. On a screen
narrower than 16:9 (16:10 such as the Steam Deck's
1280x800, or 4:3), where 16:9 only adds bars above and below, that
file's 16:9 moves to Monitor's shape; an ultrawide keeps
its 16:9 (often a real choice there). The rule is one function,
`hos_aspect_on` in `host/hos_wide_size.h`: `host/hos_main.c`
starts the game with it, and the menu's row follows what the host
started with (`host->aspect_ratio()`, in `menu_init`
before `restart_init`), so the two agree on Linux too, where the menu
has no screen size of its own. `save()` writes
  `options_version=6`, so a 16:9 chosen after the update stays.
- The other value reads **16:9** again instead of #2078's "Off (16:9)".
#2078 added "Off" because "Ultrawide: 16:9" read as if ultrawide were
already on; under the name Aspect ratio the value is a shape, and
"Aspect ratio: Off (16:9)" would not read well. Happy to keep "Off
(16:9)" if you prefer it.
- `recomplauncher.json`: the same label and help, and its default is
`1`, so the launcher does not write 16:9 over the new default.
- New help text for the row, and for **Ultrawide menus**, whose text
named the old row. The readme's settings table and Output resolution
notes, `menu_restart.h` and the comments that named the row follow
(#2052's wording kept, with the new name).
- Tests: `test_menu_restart.py` and `test_menu_restart.cpp` use the new
name; `test_audio_output_menu.py` and `test_stick_dead_zone.py` give
their option stubs the `ultrawide` field that `ini_key` now reads. New
`test_aspect_default.py`: `hos_aspect_on` over 16:10, 4:3, 5:4, 16:9,
1366x768, 21:9, 32:9, portrait and no screen size, plus the host and
menu source checks.

**Tested**
- In game on Windows 11, RTX 5090 (driver 621.14), 5120x2160 (21:9)
monitor, in one build with the widescreen cap change from #2120: the
picture takes the monitor's shape, and Kokiri Forest and Castle Town
load. That run was before the merge with current main (#2052, #2041,
#2078). This PR only moves a default and renames a row; the code that
sizes the picture is unchanged.
- `tests/run_all.py` on Windows (llvm-mingw) after merging main at
06c30bd (with #2197): 392 passed, 31 skipped, 1 failed:
`test_vram_budget_row`, which fails the same way on main's own sources
on this PC (clang stops on the unused `kVramMinimumOnly`).
- A build of this branch compiled (`hos_main.c`, `vk_menu.cpp`) before
that merge; the merge itself only adds the version 6 move next to
#2197's. The move for older files was not run in game: this PC has a
21:9 screen, where an older 16:9 stays. The tests this PR touches pass,
and so do the others that build `ini_key` or the row:
`test_framegen_preset`, `test_menu_env_on`, `test_recomplauncher_json`,
`test_rumble_menu`, `test_upscaler_rows`, `test_vsync_row`,
`test_output_resolution`, `test_widescreen_shapes`.

**Not tested**
- 16:10, 4:3, 32:9 and 48:9 screens in game (`test_widescreen_shapes.py`
covers their sizes).
- Linux and the Steam Deck: SDL gives the screen's size there too, so a
Steam Deck (16:10) now starts with a 16:10 picture instead of 16:9.
- If #1864's translations name the row, they would need the new name.

**Screenshots**

From a headless run of this branch (head 2a74ec4) on Windows 11, RTX
5090, with a 5120x2160 (21:9) monitor and no options file, so a first
start (`HOS_HEADLESS=1`, `RX_VK_SHOTS` at 2560x1080; the save loaded
with `HOS_INPUT_SCRIPT` presses). The log: `widescreen: picture
3416x1440, scene 2560x1080, camera ratio 2.3722`.

![The Graphics tab: Aspect ratio at Monitor's shape, the default,
selected with its
help](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/aspect-2119/aspect-ratio-row.webp)

![First start on the 21:9 monitor: Kokiri Forest at the monitor's shape,
the HUD in the middle
16:9](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/aspect-2119/first-start-21x9-kokiri.webp)

_The menu shot is the middle 16:9 of the frame: behind it the title
movie stays 16:9 with black sides. Its build had one extra line so the
list scrolls headless (`ease_step = 1.0f` in `menu_record_image`), not
part of this PR._

The widescreen cap PR (#2120) edited the same readme paragraph; it has
merged, and the paragraph is merged with it here.

**`options_version`:** #2197 (merged) took 5 for the Esc default, so
this writes 6 and moves an older file's 16:9 from files
before 6 (`version < 6` in `hos_aspect_on` and `menu_init`). #2110 also
writes 5 today; whichever of the two lands later
takes the next number. #2197's `test_menu_keys.py` compiles
`menu_init`'s options read, which now also reads
`host->aspect_ratio`, so its `host` stub gains that member. Its check of
older files expected every version below the one
`save()` writes to move Insert or F1 to Insert, F1 or Esc, which would
move a version 5 file's chosen Insert or F1 again; it
now reads the move's version from `menu_init` and checks that files from
it up to `save()`'s version keep their choice.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: hckrman101 <239247847+hckrman101@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants