Add 5120x1440 (32:9) Output resolution #1290

Merged
mockcomic merged 3 commits from main into main 2026-11-10 04:25:24 +00:00
mockcomic commented 2026-11-09 20:54:15 +00:00 (Migrated from github.com)

What

Adds 5120x1440 as a fifth Output resolution in the Insert → Output
resolution row, for 32:9 (dual-QHD) ultrawide monitors. It takes the next free
id 4, so the load-bearing ids 0–3 (saved output_res= and HOS_OUTPUT_RES)
keep their meaning; nothing is renumbered.

Why

The row offered 1920x1080, 2048x1152, 2560x1440 and 3840x2160. A 5120x1440 user
had no pixel-perfect choice, so the frame was scaled to the window and the HUD
blurred. The picture is now offered as a discrete native size.

How

  • host/nn_applet.c — hos_output_size() case 4; "5120x1440" appended to the
    names[] table (its sizeof bounds guard auto-widens to accept v=4); comment.
  • gpu/vk/vk_menu.cpp — kOutputs += {5120, 1440}; kOutputOrder += 4
    (5120 is the largest, shown after 3840x2160); the 5th hn_output_res hint.
    kLastOutput, output_choice, step_output_choice, shown_output,
    tx_output_res all derive from those tables — no logic change.
  • gpu/vk/vk_menu.h — id-map comment.
  • readme.md — size list and the HOS_OUTPUT_RES 4 id.
  • tests/test_output_resolution.py — pins the new world.

No new code paths: everything routes through the existing helpers.

Notes for reviewers

  • Graphics scale needs no change. host/nn_applet.c:377 already returns the
    x1 scale enum for any nonzero mode (output_mode() ? 0 : 1), so mode 4 gets
    its exact 5120x1440.
  • Render-target pool is 610 MB at 5120x1440 — 2.0× the shipped 2560x1440,
    matching the existing tests/test_rt_pool_minimum.py:52 case, not a new number.
  • This is a discrete output size, separate from the Ultrawide (Hor+) row,
    which already computes a ~5400x1520 frame on a 32:9 screen and is untouched.
    hos_main.c:1382 caps the widescreen frame at 4K's pixels, so mode 4 gives a
    pixel-exact 32:9 frame when Ultrawide is set to 16:9 (the frame then fills a
    32:9 window natively, no letterbox).

Testing

  • tests/test_output_resolution.py passes: output_res=4 → 5120x1440, label
    "5120x1440", pool 610 MB, output_choice(4)==4 / output_choice(5)==0, and
    the full step_output_choice walk. Before the change that id clamped to 0
    (2048x1152) — the regression proof.
  • No collateral: test_menu_restart.py, test_oot_wide_save_picture.py,
    test_rt_pool_minimum.py, test_upscaler_rows.py, test_menu_hit.py,
    test_rumble_menu.py all pass.
  • 5120x1440 present in the built hookshot.exe; the hint string in
    hookshot_vk.dll.
## What Adds **5120x1440** as a fifth Output resolution in the Insert → Output resolution row, for 32:9 (dual-QHD) ultrawide monitors. It takes the next free id **4**, so the load-bearing ids 0–3 (saved `output_res=` and `HOS_OUTPUT_RES`) keep their meaning; nothing is renumbered. ## Why The row offered 1920x1080, 2048x1152, 2560x1440 and 3840x2160. A 5120x1440 user had no pixel-perfect choice, so the frame was scaled to the window and the HUD blurred. The picture is now offered as a discrete native size. ## How - `host/nn_applet.c` — `hos_output_size()` `case 4`; `"5120x1440"` appended to the `names[]` table (its `sizeof` bounds guard auto-widens to accept v=4); comment. - `gpu/vk/vk_menu.cpp` — `kOutputs` += `{5120, 1440}`; `kOutputOrder` += `4` (5120 is the largest, shown after 3840x2160); the 5th `hn_output_res` hint. `kLastOutput`, `output_choice`, `step_output_choice`, `shown_output`, `tx_output_res` all derive from those tables — no logic change. - `gpu/vk/vk_menu.h` — id-map comment. - `readme.md` — size list and the `HOS_OUTPUT_RES` `4` id. - `tests/test_output_resolution.py` — pins the new world. No new code paths: everything routes through the existing helpers. ## Notes for reviewers - **Graphics scale needs no change.** `host/nn_applet.c:377` already returns the x1 scale enum for any nonzero mode (`output_mode() ? 0 : 1`), so mode 4 gets its exact 5120x1440. - **Render-target pool is 610 MB** at 5120x1440 — 2.0× the shipped 2560x1440, matching the existing `tests/test_rt_pool_minimum.py:52` case, not a new number. - This is a discrete output size, separate from the **Ultrawide (Hor+)** row, which already computes a ~5400x1520 frame on a 32:9 screen and is untouched. `hos_main.c:1382` caps the *widescreen* frame at 4K's pixels, so mode 4 gives a pixel-exact 32:9 frame when Ultrawide is set to 16:9 (the frame then fills a 32:9 window natively, no letterbox). ## Testing - `tests/test_output_resolution.py` passes: `output_res=4` → 5120x1440, label `"5120x1440"`, pool 610 MB, `output_choice(4)==4` / `output_choice(5)==0`, and the full `step_output_choice` walk. Before the change that id clamped to 0 (2048x1152) — the regression proof. - No collateral: `test_menu_restart.py`, `test_oot_wide_save_picture.py`, `test_rt_pool_minimum.py`, `test_upscaler_rows.py`, `test_menu_hit.py`, `test_rumble_menu.py` all pass. - `5120x1440` present in the built `hookshot.exe`; the hint string in `hookshot_vk.dll`. ```
hckrman101 (Migrated from github.com) reviewed 2026-11-09 22:47:12 +00:00
hckrman101 (Migrated from github.com) left a comment

Nice small change. Verified against current main: ids 0-3 keep their meaning (id 4 appended, kOutputOrder {3,0,1,2,4}), the names[] bounds guard widens to accept 4, and every menu path (row max, click/step, HOS_OUTPUT_RES parse, labels, shown_output) derives from kOutputs/kLastOutput, so no hardcoded 0-2 left behind. No collision with the other option ids. The 610 MB pool also checks out: 305 MB x (5120x1440)/(2560x1440) = 610, and it matches the existing WIDE(5120,1440) case in test_rt_pool_minimum.py.

One question, not a blocker. hos_output_size only changes the display size; as far as I can see from apply_output_size and the hos_main.c Hor+ path, the camera/projection aspect only follows the frame when Ultrawide (Hor+) is active. With Ultrawide at 16:9 and output 5120x1440, is the 3D/HUD rendered at the right 32:9 aspect, or is it a 16:9 image stretched across a 32:9 frame? The PR text says the frame fills a 32:9 window natively, which is true for the frame size but I could not confirm the content is not stretched. A screenshot or a line in the readme about pairing it with Ultrawide would settle it. Related: #1339 changes present_rect to take a display aspect rather than the texture's pixel aspect; they touch different files, so no textual conflict, but if mode 4 does end up stretched that is the area that would be affected.

CI is still pending on this head, and the branch merges cleanly. I did not run the tests; test_output_resolution.py is failing on main, so check that it is green here once CI finishes (the changes to it look consistent with the code).

Nit: the old 4->clamped-to-0 case moved to 5, good; maybe also add a HOS_OUTPUT_RES=4 handheld row to confirm handheld still wins (the code does handle it, it's just untested).

Nice small change. Verified against current main: ids 0-3 keep their meaning (id 4 appended, kOutputOrder {3,0,1,2,4}), the `names[]` bounds guard widens to accept 4, and every menu path (row max, click/step, HOS_OUTPUT_RES parse, labels, shown_output) derives from kOutputs/kLastOutput, so no hardcoded 0-2 left behind. No collision with the other option ids. The 610 MB pool also checks out: 305 MB x (5120x1440)/(2560x1440) = 610, and it matches the existing WIDE(5120,1440) case in test_rt_pool_minimum.py. One question, not a blocker. `hos_output_size` only changes the display size; as far as I can see from `apply_output_size` and the hos_main.c Hor+ path, the camera/projection aspect only follows the frame when Ultrawide (Hor+) is active. With Ultrawide at 16:9 and output 5120x1440, is the 3D/HUD rendered at the right 32:9 aspect, or is it a 16:9 image stretched across a 32:9 frame? The PR text says the frame fills a 32:9 window natively, which is true for the frame size but I could not confirm the content is not stretched. A screenshot or a line in the readme about pairing it with Ultrawide would settle it. Related: #1339 changes present_rect to take a display aspect rather than the texture's pixel aspect; they touch different files, so no textual conflict, but if mode 4 does end up stretched that is the area that would be affected. CI is still pending on this head, and the branch merges cleanly. I did not run the tests; test_output_resolution.py is failing on main, so check that it is green here once CI finishes (the changes to it look consistent with the code). Nit: the old 4->clamped-to-0 case moved to 5, good; maybe also add a HOS_OUTPUT_RES=4 handheld row to confirm handheld still wins (the code does handle it, it's just untested).
mockcomic commented 2026-11-09 23:42:37 +00:00 (Migrated from github.com)

Confirmed on a 32:9 display. With Ultrawide disabled, the frame is native 32:9, but the camera and HUD remain 16:9, stretching the picture across the screen. You were right that the PR description only covers the frame size. I added a README note explaining that 5120x1440 should be used with Ultrawide enabled and what happens when it is disabled.

No changes to the camera logic are needed. With Ultrawide enabled, the Hor+ code uses the selected output height to produce exactly 5120x1440. Both settings align, and the pool calculation matches what you already checked.

I also addressed the test suggestion by adding a handheld case for HOS_OUTPUT_RES=4, since mode 4 clamps to 0 in handheld mode. Ran test_output_resolution.py locally, and all tests passed.

Confirmed on a 32:9 display. With Ultrawide disabled, the frame is native 32:9, but the camera and HUD remain 16:9, stretching the picture across the screen. You were right that the PR description only covers the frame size. I added a README note explaining that 5120x1440 should be used with Ultrawide enabled and what happens when it is disabled. No changes to the camera logic are needed. With Ultrawide enabled, the Hor+ code uses the selected output height to produce exactly 5120x1440. Both settings align, and the pool calculation matches what you already checked. I also addressed the test suggestion by adding a handheld case for HOS_OUTPUT_RES=4, since mode 4 clamps to 0 in handheld mode. Ran test_output_resolution.py locally, and all tests passed.
hckrman101 (Migrated from github.com) reviewed 2026-11-10 03:37:25 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks for confirming on a real 32:9 screen, and for the readme note and the handheld test row. Both of my earlier points are addressed: the pairing with Ultrawide is now documented, and (4, 1, None, 0, ...) covers handheld winning over mode 4.

One thing to fix in the new readme text: it says Ultrawide "keeps the HUD and movies in the middle 16:9". That contradicts the Ultrawide row's own hint in gpu/vk/vk_menu.cpp ("The HUD is still stretched") and the table at readme.md:61 ("the HUD is stretched and the picture is softer"). If the HUD really does stay centred with Monitor's shape, those two need updating. If not, the sentence should say the HUD is still stretched. Also, "sharp HUD" in the 5120x1440 sentence above it doesn't sit well with a stretched HUD, so it may be worth rewording either way.

Everything else is unchanged and fine. CI shows no check runs for this head, so please confirm test_output_resolution.py is green locally (you said it is).

Thanks for confirming on a real 32:9 screen, and for the readme note and the handheld test row. Both of my earlier points are addressed: the pairing with Ultrawide is now documented, and `(4, 1, None, 0, ...)` covers handheld winning over mode 4. One thing to fix in the new readme text: it says Ultrawide "keeps the HUD and movies in the middle 16:9". That contradicts the Ultrawide row's own hint in `gpu/vk/vk_menu.cpp` ("The HUD is still stretched") and the table at readme.md:61 ("the HUD is stretched and the picture is softer"). If the HUD really does stay centred with Monitor's shape, those two need updating. If not, the sentence should say the HUD is still stretched. Also, "sharp HUD" in the 5120x1440 sentence above it doesn't sit well with a stretched HUD, so it may be worth rewording either way. Everything else is unchanged and fine. CI shows no check runs for this head, so please confirm `test_output_resolution.py` is green locally (you said it is).
Commenting is not possible because the repository is archived.
No description provided.