feat: add a 1920x1080 docked output mode #1144

Merged
arthur-moebios merged 2 commits from feat/docked-1080p into main 2026-11-09 17:17:23 +00:00
arthur-moebios commented 2026-11-09 14:34:34 +00:00 (Migrated from github.com)

Summary

Add 1920×1080 as a docked output size for the full game image and HUD. The new mode uses output_res=3 or HOS_OUTPUT_RES=3. The existing saved mode IDs and the default output are preserved.

Testing

Compared the two output sizes in the same patched v2.0.7 executable on Windows with a Core i7-13700H and RTX 4060 Laptop GPU (driver 617.42). Both headless Vulkan runs loaded the same Hyrule Field save and held the same camera at fixed 100% render scale with frame generation off. Measured one 25-second window per mode after loading. The native present trace measures time between game present-call returns.

Output Mean native FPS Frame time p95 Median GPU time per frame
2048×1152 38.77 29.59 ms 19.40 ms
1920×1080 42.78 26.23 ms 17.40 ms

Verified the 1920×1080 render dimensions in the log and checked HUD placement in local gameplay captures. The regression test covers mode IDs, the environment override, handheld fallback, and render-target budgets. The full local source suite reported 119 passed, 12 skipped and four failures that also reproduced on unmodified main (test_nup_paths.py, test_fps_overlay_order.py, test_precompile_compute_guard.py, and test_program_answer_domains.py).

Refs #1007 and #1134.

## Summary Add 1920×1080 as a docked output size for the full game image and HUD. The new mode uses `output_res=3` or `HOS_OUTPUT_RES=3`. The existing saved mode IDs and the default output are preserved. ## Testing Compared the two output sizes in the same patched v2.0.7 executable on Windows with a Core i7-13700H and RTX 4060 Laptop GPU (driver 617.42). Both headless Vulkan runs loaded the same Hyrule Field save and held the same camera at fixed 100% render scale with frame generation off. Measured one 25-second window per mode after loading. The native present trace measures time between game present-call returns. | Output | Mean native FPS | Frame time p95 | Median GPU time per frame | | --- | ---: | ---: | ---: | | 2048×1152 | 38.77 | 29.59 ms | 19.40 ms | | 1920×1080 | 42.78 | 26.23 ms | 17.40 ms | Verified the 1920×1080 render dimensions in the log and checked HUD placement in local gameplay captures. The regression test covers mode IDs, the environment override, handheld fallback, and render-target budgets. The full local source suite reported 119 passed, 12 skipped and four failures that also reproduced on unmodified `main` (`test_nup_paths.py`, `test_fps_overlay_order.py`, `test_precompile_compute_guard.py`, and `test_program_answer_domains.py`). Refs #1007 and #1134.
hckrman101 (Migrated from github.com) reviewed 2026-11-09 16:19:58 +00:00
hckrman101 (Migrated from github.com) left a comment

Nice addition. I checked the other users of the output size, and the new mode needs nothing more. The game patches in nx2/oot_patches.py all go through hos_output_size(). The graphics-scale answer in nn_applet.c gives x1 for any chosen output. The render-target pool only grows at 4K, so 1080p keeps the shipped one. The ultrawide width in hos_main.c follows hos_output_size(1). Keeping the saved ids stable and sending an out-of-range value to 0 instead of clamping it to 1080p is the right call too.

One note on the motivation: the fps gain over 2048x1152 is mostly the ~13% fewer pixels, which the Resolution row can already trade. I think the stronger case is image quality on 1080p screens, the most common ones. Today the whole frame, HUD and text included, is composed at 2048x1152 and then scaled down to the window, which softens it, and 1080p output maps 1:1. Might be worth saying so in the readme row.

Nice addition. I checked the other users of the output size, and the new mode needs nothing more. The game patches in `nx2/oot_patches.py` all go through `hos_output_size()`. The graphics-scale answer in `nn_applet.c` gives x1 for any chosen output. The render-target pool only grows at 4K, so 1080p keeps the shipped one. The ultrawide width in `hos_main.c` follows `hos_output_size(1)`. Keeping the saved ids stable and sending an out-of-range value to 0 instead of clamping it to 1080p is the right call too. One note on the motivation: the fps gain over 2048x1152 is mostly the ~13% fewer pixels, which the Resolution row can already trade. I think the stronger case is image quality on 1080p screens, the most common ones. Today the whole frame, HUD and text included, is composed at 2048x1152 and then scaled down to the window, which softens it, and 1080p output maps 1:1. Might be worth saying so in the readme row.
@ -77,0 +83,4 @@
static_assert(std::size(kOutputOrder) == std::size(kOutputs));
int step_output_choice(int id, int dir) {
const int* pos = std::find(std::begin(kOutputOrder), std::end(kOutputOrder), output_choice(id));
const int next = std::clamp((int)(pos - std::begin(kOutputOrder)) + dir, 0, kLastOutput);
hckrman101 (Migrated from github.com) commented 2026-11-09 16:19:58 +00:00

With 1080p appended, the menu now cycles 2048x1152 → 2560x1440 → 3840x2160 → 1920x1080, so the smallest size comes after 4K. To keep the saved ids as they are (3 = 1080p) but show the sizes in order, the row could step through a display-order table, e.g. {3, 0, 1, 2} (with Left/Right mapping through it), or tx_output_res / the row could use a small id↔position mapping. Same for the readme table, which lists it last.

With 1080p appended, the menu now cycles 2048x1152 → 2560x1440 → 3840x2160 → 1920x1080, so the smallest size comes after 4K. To keep the saved ids as they are (`3` = 1080p) but show the sizes in order, the row could step through a display-order table, e.g. `{3, 0, 1, 2}` (with Left/Right mapping through it), or `tx_output_res` / the row could use a small id↔position mapping. Same for the readme table, which lists it last.
@ -0,0 +1,84 @@
"""Exercise the host and menu's docked output sizes without a game dump."""
hckrman101 (Migrated from github.com) commented 2026-11-09 16:19:59 +00:00

minor: this asserts on the exact text of the menu row, and the fixture below slices nn_applet.c / vk_menu.cpp between marker strings, so an unrelated rewording or reorder of those files breaks the test. The behavioural checks in the fixture are good. The row's range could be checked through kLastOutput (which the fixture already asserts) rather than the source text. Also a heads-up that #841 rewrites this same row (labels plus per-value help lines), so whichever lands second will need a small merge there.

minor: this asserts on the exact text of the menu row, and the fixture below slices `nn_applet.c` / `vk_menu.cpp` between marker strings, so an unrelated rewording or reorder of those files breaks the test. The behavioural checks in the fixture are good. The row's range could be checked through `kLastOutput` (which the fixture already asserts) rather than the source text. Also a heads-up that #841 rewrites this same row (labels plus per-value help lines), so whichever lands second will need a small merge there.
hckrman101 (Migrated from github.com) reviewed 2026-11-09 16:25:45 +00:00
@ -77,0 +83,4 @@
static_assert(std::size(kOutputOrder) == std::size(kOutputs));
int step_output_choice(int id, int dir) {
const int* pos = std::find(std::begin(kOutputOrder), std::end(kOutputOrder), output_choice(id));
const int next = std::clamp((int)(pos - std::begin(kOutputOrder)) + dir, 0, kLastOutput);
hckrman101 (Migrated from github.com) commented 2026-11-09 16:25:45 +00:00

Maybe we should break away from this int setting now before we add other of other resolutions too

Maybe we should break away from this int setting now before we add other of other resolutions too
arthur-moebios commented 2026-11-09 16:32:25 +00:00 (Migrated from github.com)

Thanks for the review, @hckrman101. I agree that sharper HUD and text on a 1080p display are the stronger reason to add this mode. I'll make that the main point in the README and keep the benchmark in the PR as a measured result.

I'll put 1080p first in the menu and README while keeping the saved IDs stable. I'll also replace the exact menu-row text assertion with checks for the selectable modes and their order.

I'd keep the integer setting for this PR to preserve compatibility with existing configs. If we expect to add more resolutions, I'm happy to discuss a more extensible format separately. Thanks for the heads-up about #841.

Thanks for the review, @hckrman101. I agree that sharper HUD and text on a 1080p display are the stronger reason to add this mode. I'll make that the main point in the README and keep the benchmark in the PR as a measured result. I'll put 1080p first in the menu and README while keeping the saved IDs stable. I'll also replace the exact menu-row text assertion with checks for the selectable modes and their order. I'd keep the integer setting for this PR to preserve compatibility with existing configs. If we expect to add more resolutions, I'm happy to discuss a more extensible format separately. Thanks for the heads-up about #841.
hckrman101 (Migrated from github.com) reviewed 2026-11-09 17:14:51 +00:00
@ -77,0 +83,4 @@
static_assert(std::size(kOutputOrder) == std::size(kOutputs));
int step_output_choice(int id, int dir) {
const int* pos = std::find(std::begin(kOutputOrder), std::end(kOutputOrder), output_choice(id));
const int next = std::clamp((int)(pos - std::begin(kOutputOrder)) + dir, 0, kLastOutput);
hckrman101 (Migrated from github.com) commented 2026-11-09 17:14:51 +00:00

Thanks, the kOutputOrder table does it: Left/Right, wheel and the value halves all go through set_row, so they step in size order, and the saved output_res / HOS_OUTPUT_RES ids keep their meaning. Fine with keeping the int setting here, a different format can be its own PR if more sizes come.

Thanks, the `kOutputOrder` table does it: Left/Right, wheel and the value halves all go through `set_row`, so they step in size order, and the saved `output_res` / `HOS_OUTPUT_RES` ids keep their meaning. Fine with keeping the int setting here, a different format can be its own PR if more sizes come.
hckrman101 (Migrated from github.com) reviewed 2026-11-09 17:14:53 +00:00
@ -0,0 +1,84 @@
"""Exercise the host and menu's docked output sizes without a game dump."""
hckrman101 (Migrated from github.com) commented 2026-11-09 17:14:53 +00:00

Thanks, dropping the row-text assert and testing the step order covers it. The marker slicing is still there but that is fine for now.

Thanks, dropping the row-text assert and testing the step order covers it. The marker slicing is still there but that is fine for now.
hckrman101 (Migrated from github.com) approved these changes 2026-11-09 17:14:55 +00:00
hckrman101 (Migrated from github.com) left a comment

LGTM. 1080p now comes first in the menu and readme with the saved ids unchanged, and the readme leads with the 1:1 HUD/text point.

One small nit, not blocking: the output row now stops at its ends rather than wrapping like the other multi-value rows, so at 3840x2160 Enter/A (which acts as Right) and a click on the right half do nothing. Since the arrow hides at the end that seems fine, just mentioning it in case it wasn't intended.

LGTM. 1080p now comes first in the menu and readme with the saved ids unchanged, and the readme leads with the 1:1 HUD/text point. One small nit, not blocking: the output row now stops at its ends rather than wrapping like the other multi-value rows, so at 3840x2160 Enter/A (which acts as Right) and a click on the right half do nothing. Since the arrow hides at the end that seems fine, just mentioning it in case it wasn't intended.
Commenting is not possible because the repository is archived.
No description provided.