feat(ui): the HUD laid out for every screen shape at its 16:9 size, anchored to the screen's edges #95

Open
llun wants to merge 1 commit from llun/Longshot:feat/hud-layout into main
Member

Why

On a screen that isn't 16:9, the HUD looks wrong today. Narrower shapes (5:4, 4:3, 3:2, 16:10) stretch the HUD and menus taller, and wider ones (21:9, 32:9, 48:9) leave the HUD in the middle 16:9, far from the corners. With this change the HUD keeps its 16:9 size on every shape the Aspect ratio row offers, and its parts sit at the screen's edges and corners, as if the game had been made for that shape. 16:9 doesn't change.

What changed

  • HUD layout (host/hos_wide_ui.c): built on ozwaldorf's pane planner from https://github.com/NotRexed/Hookshot/pull/2843 ("Extended" ultrawide menus, still open there; credited in the commit). The planner sees each 2D pane as the game places it (f_8154cab0) and moves the ones at a side of the 16:9 canvas out to the screen's side. This PR changes these things about it:
    • It applies to the HUD on its own, with no setting. HUD layouts are found by their root layout's name (HUD_LAYOUTS: PlayerStatus_00, Location_00, MapMenu_00 for the minimap, Talk_00, MessageLog_01 for the text box's button guide, and the world-placed ones, which stay unmoved).
    • Only the HUD's trees and the loading fade's are noted. A pane is noted when it is a layout's root, or is made from a pane noted in a HUD tree. A menu's panes are never noted, and neither is a HUD part inside a menu (the hearts in the pause menu's bar), so the plan costs the same with a menu open as in play (Measurements).
    • The map is drawn as a menu while it shows. The map (the View button) is the minimap's own MapMenu_00 layout as a root of its own, at the same pane address. While its icons' group is at the canvas's origin (RULE_KEEP_ORIGIN), the root gets as_menu: it isn't walked or moved, and on a narrower screen it's fitted to the width like the other menus. The map page's own rules (RULE_KEEP_TAB) went with that.
    • The text box's choices are held (MessageDialog_00, a new layout kind, LK_HELD): drawn at the HUD's size like Talk_00's box, where the game puts them, never moved and never fitted. Fitted as a menu, they came out at another scale than the box on a narrower screen.
    • The loading fade covers the whole picture. Its pane across the canvas (P_Fade_00) is drawn wider when its layout is in the middle 16:9 (the centred Ultrawide menus modes) and taller when it is at the HUD's size (narrower than 3:2), in hos_wide_ui_size. Its icon keeps its shape and place in the middle 16:9. This is mooki's proposal from #57 for https://github.com/NotRexed/Hookshot/issues/2220 (Longshot #82).
    • The menu-only parts of https://github.com/NotRexed/Hookshot/pull/2843 are left out, since the menus don't move here (this answers the review's ask to simplify): the pause menu's page strips, its background drawn in pieces, the lists widened as they are built, the menus' name rules, and their three hook sites. The file went from 916 to 648 lines.
    • It works on narrower screens. Side panes move in there (ui_ex < 0). Up and down (visit_y, PANE_Y) is new: when the HUD has to be drawn smaller, the top and bottom panes move out to the screen's top and bottom.
    • The plan walks only the used slots. A bitmap of taken slots replaces a sweep of the whole 8 MB table every present, which took 0.7 ms on the presenting thread; it now takes 0.1 ms.
  • The rules per shape (hos_wide_ui_tick):
    • Wider than 16:9: the HUD is scaled from the height (its 16:9 size) and its sides move out to the screen's edges, stopping at 12:5's sides (HUD_MAX; covers every 21:9 screen up to 3840x1600). On a 32:9 screen that keeps the hearts and the minimap near the middle. At 48:9 (three screens) the HUD stays on the middle screen, as before.
    • Narrower than 16:9: the HUD stays at its 16:9 size down to 3:2 (HUD_FULL). Below that it shrinks by the ratio over 3:2 (4:3: 89%, 5:4: 83%) so it doesn't take too much of the width, and its top and bottom move out.
    • Menus (every other root layout) are unchanged on wider screens: the Ultrawide menus row still applies. On narrower ones they are drawn smaller from their root pane to fit the width, centred, never stretched.
  • Projection site (nx2/oot_wide.py, HUD): one across and up scale (hos_ui_qx, hos_ui_qy) for every 2D screen not kept whole. The old site only scaled across, and only on wider screens. The pane sites come from https://github.com/NotRexed/Hookshot/pull/2843.
  • Keep mask (host/rx_backend.c): on a narrower screen the menus' screen is never kept whole (stretched), whatever the Ultrawide menus row says; that row is about wider screens.
  • Tests: tests/test_hud_layout.py is new. It checks the plan at each shape (16:9, 21:9, 3440x1440, 32:9, 48:9, 16:10, 3:2, 4:3, back to 16:9), the menus' fit, the loading fade at 21:9 and 4:3 and its own screen at the projection site (KEEP_VTABLES[3]), the map page (a menu's, then the minimap again), and the text box's choices and guide. tests/test_wide_ui.py checks the planner from https://github.com/NotRexed/Hookshot/pull/2843 on the HUD's layouts, and that a menu notes only its root. Each check added in the review round fails without its change.
  • Docs: the switches are in docs/development.md, and there's a row in docs/tested-configurations.md.

Flags and settings

Flag New or changed What it does Default now Goal: on by default? Helps on Doesn't help on Untested on Untested, but should help on
HOS_WIDE_UI=0 New The HUD as before (wider: the middle 16:9; narrower: fitted to the width, no longer stretched) unset: the HUD laid out per shape, menus as before No (a switch to compare and to fall back) any shape 16:9 (does nothing) AMD, Intel, Linux, real ultrawide monitors ultrawide and 4:3 screens
HOS_WIDE_UI_LOG=<n> New Logs the plan's first n changes (each pane, its layout, its move), each root layout's class, and the plan's time per 600 presents off No (diagnostics) debugging a pane that sits wrong - - -
HOS_HUD_MAX_RATIO=<ratio> New How far the HUD's sides reach on a wide screen; 0: the screen's edges at any width 2.4 (12:5); 48:9 on the middle screen Yes, as the default it is 32:9, 48:9 up to 12:5 (no effect) real 32:9 and triple screens -
HOS_HUD_FULL_RATIO=<ratio> New The narrowest shape at which the HUD keeps its full 16:9 size 1.5 (3:2) Yes, as the default it is 4:3, 5:4 3:2 and wider (no effect) real 4:3 screens -

Measurements

main: 82de567a (2026-11-12): the Longshot-Main game folder. Nothing merged since, up to 514591a3, changes the HUD, the menus' screens, the loading fade or nx2/oot_wide.py.
this PR: feat/hud-layout @ 9c5a39c1 for the frame times (before the simplification and the review round; neither runs in a frame outside the plan, whose time is in its own table). The review round, now 7cedec2d, was measured as 5487b3f1, the same code (the squash added only a docs row and a switch's wording), against 607a186c, the head before it.
Hardware: RTX 5080 16 GB, NVIDIA 621.24, Ryzen 9 9900X3D, 64 GB DDR5, Windows 11 Pro (26300)
Scene: Kokiri Forest at the debug teleport's arrival, standing; output 3440x1440 (21:9) and 1440x1080 (4:3) through HOS_OUTPUT_RES; the options' DLSS row; headless, no sound, HOS_NO_PACING=1 HOS_REALTIME_STEP=0
Method: 1,800 presents standing after the teleport, frame intervals from RX_VK_FRAME_LOG, median per run. 4 runs per shape, interleaved main, PR, main, PR. The table gives the median of the two runs (each run's own in brackets). The plan's own time comes from HOS_WIDE_UI_LOG's line, the last 3 of each run.
How to reproduce: longshot_ctl.py launch <folder> --headless --env HOS_OUTPUT_RES=3440x1440 --env HOS_NO_PACING=1 --env HOS_WIDE_UI_LOG=1 --env RX_VK_FRAME_LOG=frames.csv, then Continue, teleport KokiriForest_Child, wait present --frames 300, then 1,800 presents.

main @ 82de567a this PR @ 9c5a39c1
21:9 3440x1440, frame interval median (ms) 11.44 (11.80, 11.07) 11.37 (11.24, 11.51)
4:3 1440x1080, frame interval median (ms) 11.19 (11.13, 11.25) 11.21 (11.05, 11.38)
The HUD's plan per present, presenting thread (ms) none (no plan) 0.10 to 0.11
16:9 1920x1080 the game's own the same: nothing runs (hos_wide_squeeze 0)
m-main-1440x1080-1   n=1800 median 11.126 ms  p99 17.323 ms
m-pr-1440x1080-1     n=1800 median 11.052 ms  p99 19.311 ms  plan 0.110 ms
m-main-1440x1080-2   n=1800 median 11.249 ms  p99 18.647 ms
m-pr-1440x1080-2     n=1800 median 11.375 ms  p99 19.767 ms  plan 0.109 ms
m-main-3440x1440-1   n=1800 median 11.798 ms  p99 21.766 ms
m-pr-3440x1440-1     n=1800 median 11.239 ms  p99 18.830 ms  plan 0.101 ms
m-main-3440x1440-2   n=1800 median 11.073 ms  p99 18.434 ms
m-pr-3440x1440-2     n=1800 median 11.506 ms  p99 18.438 ms  plan 0.106 ms
wide ui: picture 1.3333: the HUD at 89% of its 16:9 size, its sides moved -300 and its top and bottom 135 canvas units; the menus at 75%
wide ui: picture 2.3889: the HUD at 100% of its 16:9 size, its sides moved 660 and its top and bottom 0 canvas units; the menus at 100%
wide ui: the plan took 0.102 ms a present over the last 600   (9c5a39c1, in play; this head: 0.05 to 0.06)

Before the plan walked only the used slots, it took 0.68 to 0.78 ms a present, and the PR's frame medians were about 0.5 ms above main's at both shapes. That is why the bitmap is in this PR.

The plan with a menu open (review round): one run each at 2560x1080 and 1440x1080, of 607a186c and then of this head. Each run walks out of Kokiri Forest, then holds 1,300 presents in play, with the map open (View) and with the pause menu open. The figures are the HOS_WIDE_UI_LOG lines that fall wholly inside each stretch (2 to 3 of them).

The plan per present (ms), RTX 5080 PC 607a186c, 21:9 this PR, 21:9 607a186c, 4:3 this PR, 4:3
Play 0.107 to 0.110 0.051 to 0.052 0.102 to 0.105 0.057 to 0.060
The map open 0.097 to 0.119 0.047 to 0.051 0.093 to 0.099 0.045 to 0.046
The pause menu open 0.129 to 0.157 0.049 to 0.054 0.168 to 0.188 0.061 to 0.062

tonesi measured 0.47 to 0.56 ms with the pause menu open on 607a186c (i7-12700K). Before the review round the plan built the tree for every pane it saw, the menus' included; now it sees only the HUD's.

The loading fade (the same runs, a shot every 2 presents through the fade after walking out to Hyrule Field). Brightness is the mean, 0 to 255, outside the middle 16:9 ("bands": the sides at 21:9, above and below at 4:3) and inside it, at the shots where the middle is black:

main 82de567a 607a186c this PR
21:9 2560x1080, bands / middle 229 and 168 / 0 (shots 12 and 13) 230 and 166 / 0 (shots 12 and 13) 0 / 0 (shots 7 to 14)
4:3 1440x1080, bands / middle 0.2 / 0 (main's narrower screens are kept whole) 104 and 95 / 0 (shots 13 and 14) 0 / 0 (shots 7 to 14)

As the fade lifts, the bands and the middle come back together (this PR's first lit shot: 21:9 bands 3.6, middle 29.7; 4:3 bands 19.1, middle 24.6).

Testing

Tested: In the game on an RTX 5080, Windows 11, headless, through the control bridge, from a Longshot-Main save: Continue, the debug teleport to Kokiri Forest, play, the pause menu's Items page, and its System page (the controls sheet). I ran that at 5:4 1280x1024, 4:3 1440x1080, 3:2 1920x1280, 16:10 1920x1200, 16:9 1920x1080 and 2560x1440, 21:9 2560x1080 and 3440x1440, 12:5 3840x1600, 32:9 5120x1440 and 48:9 5760x1080, with main @ 82de567a at every shape and the PR at every shape, plus 32:9 and 48:9 with HOS_HUD_MAX_RATIO=0. The title screen and the file select ran at every shape too, on the way in, but I didn't look at them separately.

The review round (5487b3f1, the code of 7cedec2d, against 607a186c and main 82de567a, at 2560x1080 and 1440x1080): the loading fade as it lifts after walking from Kokiri Forest to Hyrule Field (numbers and pictures above), the map (View button) at both shapes, and the plan's time in play, with the map open and with the pause menu open. The log shows the map is the minimap's own MapMenu_00 root, at the same pane address, going from the HUD's to a menu's while it shows and back after. At 21:9 the map is in the middle as on main; at 4:3 it is fitted to the width (main stretches it to the full height). All runs were headless, the RTX 5080's renderer line in each log, and the saves were restored and compared after each.

python -I -B tests/run_all.py on 5487b3f1 (main 514591a3): 490 passed, 6 failed, 39 skipped. All 6 also fail on main 514591a3 on this PC: test_bake_portability, test_check_third_party, test_finish_output, test_lift_cache, test_menu_i18n, test_release. tests/test_hud_layout.py fails on main (there is no host/hos_wide_ui.c) and passes here. There is no CI on Forgejo, so these checks were run locally.

Not tested:

  • A text box in game, with choices: Navi had nothing to say on any D-pad direction in my route, and I have no save next to someone to talk to. MessageDialog_00 held and MessageLog_01 at the edge are checked game-free only. I've asked dukenheimer to check them with his Saria save.
  • The item quick select, shops, the horse and boss HUDs, and movies at each shape. My scripted route didn't open them. Layouts not in HUD_LAYOUTS are treated as menus: centred and fitted, never stretched, but not anchored.
  • Pre-rendered movies on a narrower screen still fill the picture as before. They are not HUD, and are left for a follow-up.
  • A window whose size differs from the output, and real monitors of these shapes: the shots are the game's own pictures, headless.
  • A Linux build of this head: tonesi built 607a186c with clang on Linux (no warnings in hos_wide_ui.c with -Wall -Wextra); the review round adds plain C only. T-QSAV asks for AMD or Intel.

Needs other testers:

  • A real 21:9 or 32:9 monitor: the HUD in the corners in play, and the pause, map and item menus as before, expecting nothing cut off or stretched (T-PX54). dukenheimer ran 607a186c at 5120x2160 (comment 13721); this round fixes his findings 1 and 2.
  • A dialogue with choices at 16:10 and 4:3: the choices at the same scale as the box, the "Hide / Conversation Log" guide at the screen's edge (T-3E3B; asked of dukenheimer on the PR)
  • A real 4:3 or 16:10 screen, or a Steam Deck (16:10): the HUD in the corners at its size and the menus fitted to the width, expecting nothing stretched (T-NAXG)
  • AMD or Intel on Linux: the same check at one ultrawide size through HOS_OUTPUT_RES, expecting the same pictures (T-QSAV). tonesi ran 607a186c on Linux with an RTX 3070 Ti: pass (comment 13030)

Screenshots

Each picture shows main @ 82de567a on top and this PR @ 9c5a39c1 below it: play in Kokiri Forest, then the pause menu's Items page. They are the game's own screenshots, taken headless.

Shape main @ 82de567a (top) and this PR @ 9c5a39c1 (bottom)
5:4 1280x1024 5:4 1280x1024
4:3 1440x1080 4:3 1440x1080
3:2 1920x1280 3:2 1920x1280
16:10 1920x1200 16:10 1920x1200
16:9 (unchanged) 1920x1080 16:9 (unchanged) 1920x1080
21:9 2560x1080 21:9 2560x1080
21:9 3440x1440 21:9 3440x1440
12:5 3840x1600 12:5 3840x1600
32:9 5120x1440 32:9 5120x1440
48:9 5760x1080 48:9 5760x1080

The review round (7cedec2d's code), the same pictures as in comment 15003. The loading fade is shown during the load, before it lifts:

21:9 2560x1080, main (top) and this PR (bottom) the loading fade at 2560x1080, main and this PR
4:3 1440x1080, 607a186c (top) and this PR (bottom) the loading fade at 1440x1080, before and after
The map, 4:3 1440x1080, main (top) and this PR (bottom) the map at 1440x1080, main and this PR
The map, 21:9 2560x1080, main (top) and this PR (bottom) the map at 2560x1080, main and this PR
Play and the pause menu, 21:9 2560x1080, main (top) and this PR (bottom) play and the pause menu at 2560x1080
Play and the pause menu, 4:3 1440x1080, main (top) and this PR (bottom) play and the pause menu at 1440x1080

For maintainers to decide, 32:9 and 48:9: the HUD stopping at 12:5's sides (this PR) or reaching the screen edges (HOS_HUD_MAX_RATIO=0):

32:9 and 48:9: this PR against the screen edges

Risk and rollback

A HUD layout this PR doesn't name, or a menu that is really a HUD element, would sit centred instead of at the edges. That is the old place, but on a narrower screen it is now drawn smaller to fit. A pane the planner misjudges could land at the wrong edge; HOS_WIDE_UI_LOG shows each move. HOS_WIDE_UI=0 puts the HUD back in the middle (narrower: fitted), and reverting the commit restores today's behaviour. At 16:9 nothing runs.

Reviewer notes

  • Start at hos_wide_ui_tick (the rules per shape) and visit_y; the rest of hos_wide_ui.c is the planner from https://github.com/NotRexed/Hookshot/pull/2843, cut down to the HUD.
  • The design choice for maintainers: the 32:9 cap at 12:5 and keeping 48:9 on the middle screen. The last picture under Screenshots compares these with the screen edges (HOS_HUD_MAX_RATIO=0).
  • I first tried to tell the HUD's screen from the menus' by the screen object at the projection site. It doesn't work: about 40 UI screens set the projection each frame, and panes are drawn apart from them. Root layout names are the reliable split.
  • The loading fade on narrower screens keeps its screen going through the projection (it isn't kept whole as main does it). Its pane is drawn taller instead. Kept whole, the 16:9 fade's icon and text would be squeezed across the narrower screen, as on main. This way they keep their shape and the fade still covers everything.
  • mooki's #57 (wide loading fades): this PR now widens the fade's pane as mooki proposed (comment 12471), so #57 can shrink to whatever else it covers. TrashPanda's WIP #287 brings over Hookshot's wide_cover (the renderer closes the sides while the loading cover shows) for the same fade. The two are compatible, but they conflict in the HUD site's text in nx2/oot_wide.py, and whichever lands second has to merge it.

Issues

See #64

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main (514591a3).
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Windows: built and run (llvm-mingw). Linux: 607a186c built clean by tonesi (clang, -Wall -Wextra on hos_wide_ui.c); the review round changes plain C in hos_wide_ui.c only and is not yet built on Linux (T-QSAV). No paths are touched.
  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Flags noted, and the switch reference updated (docs/development.md, the README).
  • Screenshots for a change that shows on screen.
  • A row in docs/tested-configurations.md for each measured run.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Docs updated.

🤖 Generated with Claude Code

## Why On a screen that isn't 16:9, the HUD looks wrong today. Narrower shapes (5:4, 4:3, 3:2, 16:10) stretch the HUD and menus taller, and wider ones (21:9, 32:9, 48:9) leave the HUD in the middle 16:9, far from the corners. With this change the HUD keeps its 16:9 size on every shape the Aspect ratio row offers, and its parts sit at the screen's edges and corners, as if the game had been made for that shape. 16:9 doesn't change. ## What changed - **HUD layout** (`host/hos_wide_ui.c`): built on ozwaldorf's pane planner from https://github.com/NotRexed/Hookshot/pull/2843 ("Extended" ultrawide menus, still open there; credited in the commit). The planner sees each 2D pane as the game places it (`f_8154cab0`) and moves the ones at a side of the 16:9 canvas out to the screen's side. This PR changes these things about it: - **It applies to the HUD on its own, with no setting.** HUD layouts are found by their root layout's name (`HUD_LAYOUTS`: `PlayerStatus_00`, `Location_00`, `MapMenu_00` for the minimap, `Talk_00`, `MessageLog_01` for the text box's button guide, and the world-placed ones, which stay unmoved). - **Only the HUD's trees and the loading fade's are noted.** A pane is noted when it is a layout's root, or is made from a pane noted in a HUD tree. A menu's panes are never noted, and neither is a HUD part inside a menu (the hearts in the pause menu's bar), so the plan costs the same with a menu open as in play (Measurements). - **The map is drawn as a menu while it shows.** The map (the View button) is the minimap's own `MapMenu_00` layout as a root of its own, at the same pane address. While its icons' group is at the canvas's origin (`RULE_KEEP_ORIGIN`), the root gets `as_menu`: it isn't walked or moved, and on a narrower screen it's fitted to the width like the other menus. The map page's own rules (`RULE_KEEP_TAB`) went with that. - **The text box's choices are held** (`MessageDialog_00`, a new layout kind, `LK_HELD`): drawn at the HUD's size like `Talk_00`'s box, where the game puts them, never moved and never fitted. Fitted as a menu, they came out at another scale than the box on a narrower screen. - **The loading fade covers the whole picture.** Its pane across the canvas (`P_Fade_00`) is drawn wider when its layout is in the middle 16:9 (the centred Ultrawide menus modes) and taller when it is at the HUD's size (narrower than 3:2), in `hos_wide_ui_size`. Its icon keeps its shape and place in the middle 16:9. This is mooki's proposal from #57 for https://github.com/NotRexed/Hookshot/issues/2220 (Longshot #82). - **The menu-only parts of https://github.com/NotRexed/Hookshot/pull/2843 are left out**, since the menus don't move here (this answers the review's ask to simplify): the pause menu's page strips, its background drawn in pieces, the lists widened as they are built, the menus' name rules, and their three hook sites. The file went from 916 to 648 lines. - **It works on narrower screens.** Side panes move *in* there (`ui_ex` < 0). Up and down (`visit_y`, `PANE_Y`) is new: when the HUD has to be drawn smaller, the top and bottom panes move out to the screen's top and bottom. - **The plan walks only the used slots.** A bitmap of taken slots replaces a sweep of the whole 8 MB table every present, which took 0.7 ms on the presenting thread; it now takes 0.1 ms. - **The rules per shape** (`hos_wide_ui_tick`): - **Wider than 16:9:** the HUD is scaled from the height (its 16:9 size) and its sides move out to the screen's edges, stopping at 12:5's sides (`HUD_MAX`; covers every 21:9 screen up to 3840x1600). On a 32:9 screen that keeps the hearts and the minimap near the middle. At 48:9 (three screens) the HUD stays on the middle screen, as before. - **Narrower than 16:9:** the HUD stays at its 16:9 size down to 3:2 (`HUD_FULL`). Below that it shrinks by the ratio over 3:2 (4:3: 89%, 5:4: 83%) so it doesn't take too much of the width, and its top and bottom move out. - **Menus** (every other root layout) are unchanged on wider screens: the Ultrawide menus row still applies. On narrower ones they are drawn smaller from their root pane to fit the width, centred, never stretched. - **Projection site** (`nx2/oot_wide.py`, `HUD`): one across and up scale (`hos_ui_qx`, `hos_ui_qy`) for every 2D screen not kept whole. The old site only scaled across, and only on wider screens. The pane sites come from https://github.com/NotRexed/Hookshot/pull/2843. - **Keep mask** (`host/rx_backend.c`): on a narrower screen the menus' screen is never kept whole (stretched), whatever the Ultrawide menus row says; that row is about wider screens. - **Tests:** `tests/test_hud_layout.py` is new. It checks the plan at each shape (16:9, 21:9, 3440x1440, 32:9, 48:9, 16:10, 3:2, 4:3, back to 16:9), the menus' fit, the loading fade at 21:9 and 4:3 and its own screen at the projection site (`KEEP_VTABLES[3]`), the map page (a menu's, then the minimap again), and the text box's choices and guide. `tests/test_wide_ui.py` checks the planner from https://github.com/NotRexed/Hookshot/pull/2843 on the HUD's layouts, and that a menu notes only its root. Each check added in the review round fails without its change. - **Docs:** the switches are in `docs/development.md`, and there's a row in `docs/tested-configurations.md`. ## Flags and settings | Flag | New or changed | What it does | Default now | Goal: on by default? | Helps on | Doesn't help on | Untested on | Untested, but should help on | |---|---|---|---|---|---|---|---|---| | `HOS_WIDE_UI=0` | New | The HUD as before (wider: the middle 16:9; narrower: fitted to the width, no longer stretched) | unset: the HUD laid out per shape, menus as before | No (a switch to compare and to fall back) | any shape | 16:9 (does nothing) | AMD, Intel, Linux, real ultrawide monitors | ultrawide and 4:3 screens | | `HOS_WIDE_UI_LOG=<n>` | New | Logs the plan's first n changes (each pane, its layout, its move), each root layout's class, and the plan's time per 600 presents | off | No (diagnostics) | debugging a pane that sits wrong | - | - | - | | `HOS_HUD_MAX_RATIO=<ratio>` | New | How far the HUD's sides reach on a wide screen; `0`: the screen's edges at any width | 2.4 (12:5); 48:9 on the middle screen | Yes, as the default it is | 32:9, 48:9 | up to 12:5 (no effect) | real 32:9 and triple screens | - | | `HOS_HUD_FULL_RATIO=<ratio>` | New | The narrowest shape at which the HUD keeps its full 16:9 size | 1.5 (3:2) | Yes, as the default it is | 4:3, 5:4 | 3:2 and wider (no effect) | real 4:3 screens | - | ## Measurements main: 82de567a (2026-11-12): the Longshot-Main game folder. Nothing merged since, up to 514591a3, changes the HUD, the menus' screens, the loading fade or `nx2/oot_wide.py`. this PR: feat/hud-layout @ 9c5a39c1 for the frame times (before the simplification and the review round; neither runs in a frame outside the plan, whose time is in its own table). The review round, now 7cedec2d, was measured as 5487b3f1, the same code (the squash added only a docs row and a switch's wording), against 607a186c, the head before it. Hardware: RTX 5080 16 GB, NVIDIA 621.24, Ryzen 9 9900X3D, 64 GB DDR5, Windows 11 Pro (26300) Scene: Kokiri Forest at the debug teleport's arrival, standing; output 3440x1440 (21:9) and 1440x1080 (4:3) through `HOS_OUTPUT_RES`; the options' DLSS row; headless, no sound, `HOS_NO_PACING=1 HOS_REALTIME_STEP=0` Method: 1,800 presents standing after the teleport, frame intervals from `RX_VK_FRAME_LOG`, median per run. 4 runs per shape, interleaved main, PR, main, PR. The table gives the median of the two runs (each run's own in brackets). The plan's own time comes from `HOS_WIDE_UI_LOG`'s line, the last 3 of each run. How to reproduce: `longshot_ctl.py launch <folder> --headless --env HOS_OUTPUT_RES=3440x1440 --env HOS_NO_PACING=1 --env HOS_WIDE_UI_LOG=1 --env RX_VK_FRAME_LOG=frames.csv`, then Continue, `teleport KokiriForest_Child`, `wait present --frames 300`, then 1,800 presents. | | main @ 82de567a | this PR @ 9c5a39c1 | |---|---|---| | 21:9 3440x1440, frame interval median (ms) | 11.44 (11.80, 11.07) | 11.37 (11.24, 11.51) | | 4:3 1440x1080, frame interval median (ms) | 11.19 (11.13, 11.25) | 11.21 (11.05, 11.38) | | The HUD's plan per present, presenting thread (ms) | none (no plan) | 0.10 to 0.11 | | 16:9 1920x1080 | the game's own | the same: nothing runs (`hos_wide_squeeze` 0) | ``` m-main-1440x1080-1 n=1800 median 11.126 ms p99 17.323 ms m-pr-1440x1080-1 n=1800 median 11.052 ms p99 19.311 ms plan 0.110 ms m-main-1440x1080-2 n=1800 median 11.249 ms p99 18.647 ms m-pr-1440x1080-2 n=1800 median 11.375 ms p99 19.767 ms plan 0.109 ms m-main-3440x1440-1 n=1800 median 11.798 ms p99 21.766 ms m-pr-3440x1440-1 n=1800 median 11.239 ms p99 18.830 ms plan 0.101 ms m-main-3440x1440-2 n=1800 median 11.073 ms p99 18.434 ms m-pr-3440x1440-2 n=1800 median 11.506 ms p99 18.438 ms plan 0.106 ms wide ui: picture 1.3333: the HUD at 89% of its 16:9 size, its sides moved -300 and its top and bottom 135 canvas units; the menus at 75% wide ui: picture 2.3889: the HUD at 100% of its 16:9 size, its sides moved 660 and its top and bottom 0 canvas units; the menus at 100% wide ui: the plan took 0.102 ms a present over the last 600 (9c5a39c1, in play; this head: 0.05 to 0.06) ``` Before the plan walked only the used slots, it took 0.68 to 0.78 ms a present, and the PR's frame medians were about 0.5 ms above main's at both shapes. That is why the bitmap is in this PR. **The plan with a menu open** (review round): one run each at 2560x1080 and 1440x1080, of 607a186c and then of this head. Each run walks out of Kokiri Forest, then holds 1,300 presents in play, with the map open (View) and with the pause menu open. The figures are the `HOS_WIDE_UI_LOG` lines that fall wholly inside each stretch (2 to 3 of them). | The plan per present (ms), RTX 5080 PC | 607a186c, 21:9 | this PR, 21:9 | 607a186c, 4:3 | this PR, 4:3 | |---|---|---|---|---| | Play | 0.107 to 0.110 | 0.051 to 0.052 | 0.102 to 0.105 | 0.057 to 0.060 | | The map open | 0.097 to 0.119 | 0.047 to 0.051 | 0.093 to 0.099 | 0.045 to 0.046 | | The pause menu open | 0.129 to 0.157 | 0.049 to 0.054 | 0.168 to 0.188 | 0.061 to 0.062 | tonesi measured 0.47 to 0.56 ms with the pause menu open on 607a186c (i7-12700K). Before the review round the plan built the tree for every pane it saw, the menus' included; now it sees only the HUD's. **The loading fade** (the same runs, a shot every 2 presents through the fade after walking out to Hyrule Field). Brightness is the mean, 0 to 255, outside the middle 16:9 ("bands": the sides at 21:9, above and below at 4:3) and inside it, at the shots where the middle is black: | | main 82de567a | 607a186c | this PR | |---|---|---|---| | 21:9 2560x1080, bands / middle | 229 and 168 / 0 (shots 12 and 13) | 230 and 166 / 0 (shots 12 and 13) | 0 / 0 (shots 7 to 14) | | 4:3 1440x1080, bands / middle | 0.2 / 0 (main's narrower screens are kept whole) | 104 and 95 / 0 (shots 13 and 14) | 0 / 0 (shots 7 to 14) | As the fade lifts, the bands and the middle come back together (this PR's first lit shot: 21:9 bands 3.6, middle 29.7; 4:3 bands 19.1, middle 24.6). ## Testing **Tested:** In the game on an RTX 5080, Windows 11, headless, through the control bridge, from a Longshot-Main save: Continue, the debug teleport to Kokiri Forest, play, the pause menu's Items page, and its System page (the controls sheet). I ran that at 5:4 1280x1024, 4:3 1440x1080, 3:2 1920x1280, 16:10 1920x1200, 16:9 1920x1080 and 2560x1440, 21:9 2560x1080 and 3440x1440, 12:5 3840x1600, 32:9 5120x1440 and 48:9 5760x1080, with main @ 82de567a at every shape and the PR at every shape, plus 32:9 and 48:9 with `HOS_HUD_MAX_RATIO=0`. The title screen and the file select ran at every shape too, on the way in, but I didn't look at them separately. **The review round** (5487b3f1, the code of 7cedec2d, against 607a186c and main 82de567a, at 2560x1080 and 1440x1080): the loading fade as it lifts after walking from Kokiri Forest to Hyrule Field (numbers and pictures above), the map (View button) at both shapes, and the plan's time in play, with the map open and with the pause menu open. The log shows the map is the minimap's own `MapMenu_00` root, at the same pane address, going from the HUD's to a menu's while it shows and back after. At 21:9 the map is in the middle as on main; at 4:3 it is fitted to the width (main stretches it to the full height). All runs were headless, the RTX 5080's renderer line in each log, and the saves were restored and compared after each. `python -I -B tests/run_all.py` on 5487b3f1 (main 514591a3): 490 passed, 6 failed, 39 skipped. All 6 also fail on main 514591a3 on this PC: test_bake_portability, test_check_third_party, test_finish_output, test_lift_cache, test_menu_i18n, test_release. `tests/test_hud_layout.py` fails on main (there is no `host/hos_wide_ui.c`) and passes here. There is no CI on Forgejo, so these checks were run locally. **Not tested:** - **A text box in game, with choices:** Navi had nothing to say on any D-pad direction in my route, and I have no save next to someone to talk to. `MessageDialog_00` held and `MessageLog_01` at the edge are checked game-free only. I've asked dukenheimer to check them with his Saria save. - The item quick select, shops, the horse and boss HUDs, and movies at each shape. My scripted route didn't open them. Layouts not in `HUD_LAYOUTS` are treated as menus: centred and fitted, never stretched, but not anchored. - Pre-rendered movies on a narrower screen still fill the picture as before. They are not HUD, and are left for a follow-up. - A window whose size differs from the output, and real monitors of these shapes: the shots are the game's own pictures, headless. - A Linux build of this head: tonesi built 607a186c with clang on Linux (no warnings in `hos_wide_ui.c` with `-Wall -Wextra`); the review round adds plain C only. T-QSAV asks for AMD or Intel. **Needs other testers:** - A real 21:9 or 32:9 monitor: the HUD in the corners in play, and the pause, map and item menus as before, expecting nothing cut off or stretched (T-PX54). dukenheimer ran 607a186c at 5120x2160 (comment 13721); this round fixes his findings 1 and 2. - A dialogue with choices at 16:10 and 4:3: the choices at the same scale as the box, the "Hide / Conversation Log" guide at the screen's edge (T-3E3B; asked of dukenheimer on the PR) - A real 4:3 or 16:10 screen, or a Steam Deck (16:10): the HUD in the corners at its size and the menus fitted to the width, expecting nothing stretched (T-NAXG) - AMD or Intel on Linux: the same check at one ultrawide size through `HOS_OUTPUT_RES`, expecting the same pictures (T-QSAV). tonesi ran 607a186c on Linux with an RTX 3070 Ti: pass (comment 13030) ## Screenshots Each picture shows **main @ 82de567a** on top and **this PR @ 9c5a39c1** below it: play in Kokiri Forest, then the pause menu's Items page. They are the game's own screenshots, taken headless. | Shape | main @ 82de567a (top) and this PR @ 9c5a39c1 (bottom) | |---|---| | 5:4 1280x1024 | ![5:4 1280x1024](https://git.magicbean.dev/attachments/9ba241a8-92de-4206-a10a-f81f274985d1) | | 4:3 1440x1080 | ![4:3 1440x1080](https://git.magicbean.dev/attachments/cc6a2765-38b3-4097-95b1-41333ebfe328) | | 3:2 1920x1280 | ![3:2 1920x1280](https://git.magicbean.dev/attachments/cbad073c-00cc-4ea7-95ac-b2ada2bb79ca) | | 16:10 1920x1200 | ![16:10 1920x1200](https://git.magicbean.dev/attachments/ef0bc66b-7836-4981-9e64-c3cc4ba9746d) | | 16:9 (unchanged) 1920x1080 | ![16:9 (unchanged) 1920x1080](https://git.magicbean.dev/attachments/4ff7353d-107f-4ba9-b5f3-e1c5b6f9c1e0) | | 21:9 2560x1080 | ![21:9 2560x1080](https://git.magicbean.dev/attachments/57580a47-aa24-42f4-983b-d6264aaef834) | | 21:9 3440x1440 | ![21:9 3440x1440](https://git.magicbean.dev/attachments/98579073-c26e-4060-b264-cfcd37bfbfeb) | | 12:5 3840x1600 | ![12:5 3840x1600](https://git.magicbean.dev/attachments/d1c5cb26-b186-481b-a6ac-f74e2f7036dd) | | 32:9 5120x1440 | ![32:9 5120x1440](https://git.magicbean.dev/attachments/0932a6c3-e1fb-4cd7-816b-436df9fea1ad) | | 48:9 5760x1080 | ![48:9 5760x1080](https://git.magicbean.dev/attachments/17d34da9-7499-402d-ab67-49a92e4345d2) | **The review round** (7cedec2d's code), the same pictures as in comment 15003. The loading fade is shown during the load, before it lifts: | | | |---|---| | 21:9 2560x1080, main (top) and this PR (bottom) | ![the loading fade at 2560x1080, main and this PR](https://git.magicbean.dev/attachments/62d33a09-49c3-4013-9def-a29b09780bca) | | 4:3 1440x1080, 607a186c (top) and this PR (bottom) | ![the loading fade at 1440x1080, before and after](https://git.magicbean.dev/attachments/552cad18-d285-43dd-a300-ae33eff00302) | | The map, 4:3 1440x1080, main (top) and this PR (bottom) | ![the map at 1440x1080, main and this PR](https://git.magicbean.dev/attachments/6265e713-6059-42bd-ab4e-6a18d1b7f4ef) | | The map, 21:9 2560x1080, main (top) and this PR (bottom) | ![the map at 2560x1080, main and this PR](https://git.magicbean.dev/attachments/0f9aa221-3c7e-4adf-b83f-813fa7de1547) | | Play and the pause menu, 21:9 2560x1080, main (top) and this PR (bottom) | ![play and the pause menu at 2560x1080](https://git.magicbean.dev/attachments/f71110e5-5a61-4d1a-9430-88d00e1f1f92) | | Play and the pause menu, 4:3 1440x1080, main (top) and this PR (bottom) | ![play and the pause menu at 1440x1080](https://git.magicbean.dev/attachments/e4496f3d-292d-4e58-ba5c-f0dfbf54a899) | **For maintainers to decide, 32:9 and 48:9:** the HUD stopping at 12:5's sides (this PR) or reaching the screen edges (`HOS_HUD_MAX_RATIO=0`): ![32:9 and 48:9: this PR against the screen edges](https://git.magicbean.dev/attachments/df823b27-6d29-4923-bd4d-dc9f4073690c) ## Risk and rollback A HUD layout this PR doesn't name, or a menu that is really a HUD element, would sit centred instead of at the edges. That is the old place, but on a narrower screen it is now drawn smaller to fit. A pane the planner misjudges could land at the wrong edge; `HOS_WIDE_UI_LOG` shows each move. `HOS_WIDE_UI=0` puts the HUD back in the middle (narrower: fitted), and reverting the commit restores today's behaviour. At 16:9 nothing runs. ## Reviewer notes - Start at `hos_wide_ui_tick` (the rules per shape) and `visit_y`; the rest of `hos_wide_ui.c` is the planner from https://github.com/NotRexed/Hookshot/pull/2843, cut down to the HUD. - **The design choice for maintainers:** the 32:9 cap at 12:5 and keeping 48:9 on the middle screen. The last picture under Screenshots compares these with the screen edges (`HOS_HUD_MAX_RATIO=0`). - I first tried to tell the HUD's screen from the menus' by the screen object at the projection site. It doesn't work: about 40 UI screens set the projection each frame, and panes are drawn apart from them. Root layout names are the reliable split. - **The loading fade on narrower screens** keeps its screen going through the projection (it isn't kept whole as main does it). Its pane is drawn taller instead. Kept whole, the 16:9 fade's icon and text would be squeezed across the narrower screen, as on main. This way they keep their shape and the fade still covers everything. - mooki's #57 (wide loading fades): this PR now widens the fade's pane as mooki proposed (comment 12471), so #57 can shrink to whatever else it covers. TrashPanda's WIP #287 brings over Hookshot's `wide_cover` (the renderer closes the sides while the loading cover shows) for the same fade. The two are compatible, but they conflict in the `HUD` site's text in `nx2/oot_wide.py`, and whichever lands second has to merge it. ## Issues See #64 ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main` (514591a3). - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [ ] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Windows: built and run (llvm-mingw). Linux: 607a186c built clean by tonesi (clang, `-Wall -Wextra` on `hos_wide_ui.c`); the review round changes plain C in `hos_wide_ui.c` only and is not yet built on Linux (T-QSAV). No paths are touched. - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Flags noted, and the switch reference updated (docs/development.md, the README). - [x] Screenshots for a change that shows on screen. - [x] A row in `docs/tested-configurations.md` for each measured run. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
llun changed title from WIP: feat(ui): the HUD laid out for every screen shape at its 16:9 size, anchored to the screen's edges to feat(ui): the HUD laid out for every screen shape at its 16:9 size, anchored to the screen's edges 2026-11-13 02:34:10 +00:00
Owner

Can we simplify code?

Can we simplify code?
llun force-pushed feat/hud-layout from c7c93f1e13
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 2s
Linux AppImage / appimage (pull_request) Has been cancelled
Windows builder / Longshot Builder zip (pull_request) Has been cancelled
Windows builder / Windows Vulkan back end (llvm-mingw) (pull_request) Has been cancelled
Windows builder / dav1d for Windows (llvm-mingw) (pull_request) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / Python source checks (windows-2022, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (windows-2022, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 7a9aca22a5
Some checks failed
PR description / Description follows the template (pull_request_target) Has been cancelled
Linux AppImage / appimage (pull_request) Has been cancelled
Windows builder / Longshot Builder zip (pull_request) Has been cancelled
Windows builder / Windows Vulkan back end (llvm-mingw) (pull_request) Has been cancelled
Windows builder / dav1d for Windows (llvm-mingw) (pull_request) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / Python source checks (windows-2022, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (windows-2022, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-13 03:02:02 +00:00
Compare
llun force-pushed feat/hud-layout from 7a9aca22a5
Some checks failed
PR description / Description follows the template (pull_request_target) Has been cancelled
Linux AppImage / appimage (pull_request) Has been cancelled
Windows builder / Longshot Builder zip (pull_request) Has been cancelled
Windows builder / Windows Vulkan back end (llvm-mingw) (pull_request) Has been cancelled
Windows builder / dav1d for Windows (llvm-mingw) (pull_request) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / Python source checks (windows-2022, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (windows-2022, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 4ad0feadae
Some checks failed
Vulkan back end with NVIDIA DLSS / Windows Vulkan back end with NVIDIA DLSS and AMD FSR 4 (llvm-mingw, link check) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Has been cancelled
PR description / Description follows the template (pull_request_target) Has been cancelled
Linux AppImage / appimage (pull_request) Has been cancelled
Windows builder / Longshot Builder zip (pull_request) Has been cancelled
Windows builder / Windows Vulkan back end (llvm-mingw) (pull_request) Has been cancelled
Windows builder / dav1d for Windows (llvm-mingw) (pull_request) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
2026-11-13 03:02:16 +00:00
Compare
Author
Member

Thanks, done in 4ad0fead (rebased on main 2a2da67f). Since the menus aren't moved here, I dropped everything from https://github.com/NotRexed/Hookshot/pull/2843 that only the menus needed: the pause menu's page strips, its background drawn in pieces, the lists widened as they're built, the menus' name rules and the HOS_WIDE_UI=1 menus mode, plus three of the six hook sites in nx2/oot_wide.py. host/hos_wide_ui.c went from 916 to 648 lines and tests/test_wide_ui.py from 394 to 262; the diff is 506 lines shorter in all. The class arrays became single HUD values too. Shot again in game at 4:3, 21:9 and 32:9 on the simplified build (RTX 5080): the same pictures as before, and the plan still takes 0.10-0.11 ms a present. tests/run_all.py gives 485 passed, and its 6 failures also fail on main here. If you'd like something else trimmed, point me at it.

Thanks, done in 4ad0fead (rebased on main 2a2da67f). Since the menus aren't moved here, I dropped everything from https://github.com/NotRexed/Hookshot/pull/2843 that only the menus needed: the pause menu's page strips, its background drawn in pieces, the lists widened as they're built, the menus' name rules and the HOS_WIDE_UI=1 menus mode, plus three of the six hook sites in nx2/oot_wide.py. host/hos_wide_ui.c went from 916 to 648 lines and tests/test_wide_ui.py from 394 to 262; the diff is 506 lines shorter in all. The class arrays became single HUD values too. Shot again in game at 4:3, 21:9 and 32:9 on the simplified build (RTX 5080): the same pictures as before, and the plan still takes 0.10-0.11 ms a present. tests/run_all.py gives 485 passed, and its 6 failures also fail on main here. If you'd like something else trimmed, point me at it.
llun force-pushed feat/hud-layout from 4ad0feadae
Some checks failed
Vulkan back end with NVIDIA DLSS / Windows Vulkan back end with NVIDIA DLSS and AMD FSR 4 (llvm-mingw, link check) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Has been cancelled
PR description / Description follows the template (pull_request_target) Has been cancelled
Linux AppImage / appimage (pull_request) Has been cancelled
Windows builder / Longshot Builder zip (pull_request) Has been cancelled
Windows builder / Windows Vulkan back end (llvm-mingw) (pull_request) Has been cancelled
Windows builder / dav1d for Windows (llvm-mingw) (pull_request) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
to 489dd39db8
Some checks failed
Linux AppImage / appimage (pull_request) Blocked by required conditions
Windows builder / Longshot Builder zip (pull_request) Blocked by required conditions
Windows builder / Windows Vulkan back end (llvm-mingw) (pull_request) Blocked by required conditions
Windows builder / dav1d for Windows (llvm-mingw) (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-13 03:08:46 +00:00
Compare
Author
Member

489dd39d changes only text: the commit message and the source comments now point to Hookshot's pull request as a full URL, https://github.com/NotRexed/Hookshot/pull/2843, instead of a bare number that links to the wrong place here. Code and behaviour are unchanged; tests/run_all.py gives 486 passed, and its 6 failures also fail on main on this PC.

489dd39d changes only text: the commit message and the source comments now point to Hookshot's pull request as a full URL, https://github.com/NotRexed/Hookshot/pull/2843, instead of a bare number that links to the wrong place here. Code and behaviour are unchanged; tests/run_all.py gives 486 passed, and its 6 failures also fail on main on this PC.
llun force-pushed feat/hud-layout from 489dd39db8
Some checks failed
Linux AppImage / appimage (pull_request) Blocked by required conditions
Windows builder / Longshot Builder zip (pull_request) Blocked by required conditions
Windows builder / Windows Vulkan back end (llvm-mingw) (pull_request) Blocked by required conditions
Windows builder / dav1d for Windows (llvm-mingw) (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 607a186c44
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 2s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
2026-11-13 03:30:34 +00:00
Compare
Author
Member

Rebased onto main b9cdc432 (#105, CI workflows and CONTRIBUTING only; no conflict). Head 607a186c. Full tests/run_all.py on Windows: 486 passed, 6 failed, the same six that fail on main itself on this PC (bake_portability, finish_output, lift_cache, check_third_party, setup_runtime, release).

Rebased onto main b9cdc432 (#105, CI workflows and CONTRIBUTING only; no conflict). Head 607a186c. Full `tests/run_all.py` on Windows: 486 passed, 6 failed, the same six that fail on main itself on this PC (bake_portability, finish_output, lift_cache, check_third_party, setup_runtime, release).
Contributor

Thanks for bringing the HUD layout work over. Reading c7c93f1e, the default loading fade still takes the centred path: wide_sides() keeps kMenus = 0xa, removing loading bit 3 from the full-width mask in the centred modes, while LoadingFade_00 is outside the HUD layouts and menu panes are not widened by default. The new fade assertion uses KEEP_VTABLES[2], the event fade, not the loading fade.

Issue 82 is that loading-fade report. Once this is merged, I will reduce pull request 57 to widening only the fade pane, leaving its icon centred; you are welcome to fold that into this pull request instead. This is a source reading only: I have not built or run this head.

Thanks for bringing the HUD layout work over. Reading `c7c93f1e`, the default loading fade still takes the centred path: [`wide_sides()` keeps `kMenus = 0xa`](https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/c7c93f1e13f7f35d723ddc7278ae67938237f8cc/gpu/vk/vk_framegen.cpp#L567-L581), removing loading bit 3 from the full-width mask in the centred modes, while [`LoadingFade_00` is outside the HUD layouts](https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/c7c93f1e13f7f35d723ddc7278ae67938237f8cc/host/hos_wide_ui.c#L166-L174) and [menu panes are not widened by default](https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/c7c93f1e13f7f35d723ddc7278ae67938237f8cc/host/hos_wide_ui.c#L597-L635). The [new fade assertion uses `KEEP_VTABLES[2]`](https://git.magicbean.dev/MagicBeanDev/Longshot/src/commit/c7c93f1e13f7f35d723ddc7278ae67938237f8cc/tests/test_hud_layout.py#L210-L213), the event fade, not the loading fade. [Issue 82](https://git.magicbean.dev/MagicBeanDev/Longshot/issues/82) is that loading-fade report. Once this is merged, I will reduce [pull request 57](https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/57) to widening only the fade pane, leaving its icon centred; you are welcome to fold that into this pull request instead. This is a source reading only: I have not built or run this head.
Author
Member

Thanks, @mooki: you're right. wide_sides() takes the loading bit out of the full-width mask while LoadingFade_00 has no rule of its own, and the test's fade check reads KEEP_VTABLES[2], the event fade, so it never covered the loading one.

I'll fold the fade-pane widening into this PR (fade pane full width, its icon centred, as you describe for #57), with a check on the loading fade's own vtable, rebase onto main 341d63a4, run the full suite and check the loading fade in game at 21:9 and 4:3 before pushing. That should let #57 close or shrink further; I'll say here when it's up.

Thanks, @mooki: you're right. `wide_sides()` takes the loading bit out of the full-width mask while `LoadingFade_00` has no rule of its own, and the test's fade check reads `KEEP_VTABLES[2]`, the event fade, so it never covered the loading one. I'll fold the fade-pane widening into this PR (fade pane full width, its icon centred, as you describe for #57), with a check on the loading fade's own vtable, rebase onto main 341d63a4, run the full suite and check the loading fade in game at 21:9 and 4:3 before pushing. That should let #57 close or shrink further; I'll say here when it's up.
Contributor

Linux test (NVIDIA; not T-QSAV, which asks for AMD or Intel): pass

Hardware: RTX 3070 Ti 8 GB, NVIDIA 595.104.02, Pop!_OS 24.04 (Linux 7.2), i7-12700K. Built 607a186c (the PR head) with clang from the Linux toolchain, against main 91a1ae8b. The three commits from 91a1ae8b to the PR's base b9cdc432 change only CI, forge scripts and docs, nothing in host/, gpu/ or nx2/.

What I checked:

  • The Linux build: a full source build of the head (clang 23.1.5 with the toolchain's sysroot). It finished with no errors (widescreen: 17 of 17 sites; main has 11). The build compiles host code with -Wno-everything, so I also compiled host/hos_wide_ui.c and host/rx_backend.c with the same flags plus -Wall -Wextra. hos_wide_ui.c gives no warnings. rx_backend.c gives the same 6 -Wmissing-field-initializers that main's gives, none of them in this PR's lines.
  • The checks: tests/test_hud_layout.py and tests/test_wide_ui.py pass with gcc 13.3 and with clang 23.1.5. The full tests/run_all.py gives 499 passed, 0 failed, 33 skipped. Every skip is a Windows-only check or a tool missing here (spirv-val, glslang, dav1d, a Vulkan device for the checks).
  • In the game: headless and muted, with the same options file in both folders. Each run loads a save in Kokiri Forest's village with scripted input, stands in play, then presses + for the pause menu (Items page). I took the game's own screenshots at half the output size, at the same presents in every run. Main and the PR each ran at HOS_OUTPUT_RES 3440x1440, 1440x1080 and 1920x1080, with HOS_WIDE_UI_LOG=1 on the PR's runs.

Result:

Shape main 91a1ae8b this PR 607a186c
21:9 3440x1440, play The HUD at its 16:9 size in the middle 16:9, not stretched Hearts top left, item button top right, minimap bottom right, at the same distance from the corners as at 16:9. Each part is the same size as on main, moved out 220 px in the half-size shot (440 px of the output, the logged 660 canvas units). Not stretched
21:9, pause menu The menu in the middle 16:9 The same as main: the areas beside the menu are pixel-identical, and almost every pixel that differs is in the right half, where the 3D item model turns
4:3 1440x1080, play The HUD over the whole width, squeezed sideways: the item box is about 47x62 px in the 540-high shot, the hearts narrow and tall The HUD in the corners at 89%, shapes kept: the item box is about 55x55 px (62x62 at 16:9), the hearts round
4:3, pause menu The 16:9 menu squeezed into the width at full height Fitted to the width, centred, black above and below (rows 63-475 of 540), not stretched
16:9 1920x1080 (control) - The HUD and the pause menu in the same places as on main

The "Kokiri Forest" place-name card follows the HUD too: at 21:9 it moves to the left edge, and at 4:3 it is no longer squeezed.

The wide ui lines from the PR's runs:

wide ui: picture 2.3889: the HUD at 100% of its 16:9 size, its sides moved 660 and its top and bottom 0 canvas units; the menus at 100%
wide ui: picture 1.3333: the HUD at 89% of its 16:9 size, its sides moved -300 and its top and bottom 135 canvas units; the menus at 75%
wide ui: picture 0.0000: the HUD at 100% of its 16:9 size, its sides moved 0 and its top and bottom 0 canvas units; the menus at 100%
wide ui: the plan took 0.147 ms a present over the last 600
wide ui: the plan took 0.512 ms a present over the last 600
  • The plan's time on this i7-12700K: 0.142-0.150 ms a present in play at both shapes. With the pause menu open it is 0.47-0.56 ms a present at both shapes. These come from the log of ordinary runs, not from a timing hold.
  • The description's sample lines end in "the menus at 75%, their sides moved 0". This head's line stops at "the menus at 75%".
  • Errors: none in all six runs. fatal, crash, device lost and kernel/NVRM lines are all 0. The PR's logs have no warning or error that main's lack; the only new lines are the wide ui lines.
  • Frame rate (from the logs' fps lines, one ordinary run each, with the desktop on the same card, so not a measurement): within 1 fps of main in play at 1920x1080 and 1440x1080. At 3440x1440 the PR's run read 29-33 fps for the first 700 presents after the load (presents 1560-2280), against main's 37-40. From then on both read 43-45. I didn't repeat it.

Not checked: Windows, AMD, Intel, a window or a real monitor of these shapes (headless only), the loading fade from mooki's comment, text boxes, the map page, other HUDs, other shapes, HOS_WIDE_UI=0, and timing.

3440x1440, play: main, then this PR

main 91a1ae8b, 3440x1440, play
this PR 607a186c, 3440x1440, play

3440x1440, pause menu: main, then this PR

main 91a1ae8b, 3440x1440, pause menu
this PR 607a186c, 3440x1440, pause menu

1440x1080, play: main, then this PR

main 91a1ae8b, 1440x1080, play
this PR 607a186c, 1440x1080, play

1440x1080, pause menu: main, then this PR

main 91a1ae8b, 1440x1080, pause menu
this PR 607a186c, 1440x1080, pause menu

Linux test (NVIDIA; not T-QSAV, which asks for AMD or Intel): pass Hardware: RTX 3070 Ti 8 GB, NVIDIA 595.104.02, Pop!_OS 24.04 (Linux 7.2), i7-12700K. Built 607a186c (the PR head) with clang from the Linux toolchain, against main 91a1ae8b. The three commits from 91a1ae8b to the PR's base b9cdc432 change only CI, forge scripts and docs, nothing in `host/`, `gpu/` or `nx2/`. What I checked: - **The Linux build:** a full source build of the head (clang 23.1.5 with the toolchain's sysroot). It finished with no errors (`widescreen: 17 of 17 sites`; main has 11). The build compiles host code with `-Wno-everything`, so I also compiled `host/hos_wide_ui.c` and `host/rx_backend.c` with the same flags plus `-Wall -Wextra`. `hos_wide_ui.c` gives no warnings. `rx_backend.c` gives the same 6 `-Wmissing-field-initializers` that main's gives, none of them in this PR's lines. - **The checks:** `tests/test_hud_layout.py` and `tests/test_wide_ui.py` pass with gcc 13.3 and with clang 23.1.5. The full `tests/run_all.py` gives 499 passed, 0 failed, 33 skipped. Every skip is a Windows-only check or a tool missing here (spirv-val, glslang, dav1d, a Vulkan device for the checks). - **In the game:** headless and muted, with the same options file in both folders. Each run loads a save in Kokiri Forest's village with scripted input, stands in play, then presses + for the pause menu (Items page). I took the game's own screenshots at half the output size, at the same presents in every run. Main and the PR each ran at `HOS_OUTPUT_RES` 3440x1440, 1440x1080 and 1920x1080, with `HOS_WIDE_UI_LOG=1` on the PR's runs. Result: | Shape | main 91a1ae8b | this PR 607a186c | |---|---|---| | 21:9 3440x1440, play | The HUD at its 16:9 size in the middle 16:9, not stretched | Hearts top left, item button top right, minimap bottom right, at the same distance from the corners as at 16:9. Each part is the same size as on main, moved out 220 px in the half-size shot (440 px of the output, the logged 660 canvas units). Not stretched | | 21:9, pause menu | The menu in the middle 16:9 | The same as main: the areas beside the menu are pixel-identical, and almost every pixel that differs is in the right half, where the 3D item model turns | | 4:3 1440x1080, play | The HUD over the whole width, squeezed sideways: the item box is about 47x62 px in the 540-high shot, the hearts narrow and tall | The HUD in the corners at 89%, shapes kept: the item box is about 55x55 px (62x62 at 16:9), the hearts round | | 4:3, pause menu | The 16:9 menu squeezed into the width at full height | Fitted to the width, centred, black above and below (rows 63-475 of 540), not stretched | | 16:9 1920x1080 (control) | - | The HUD and the pause menu in the same places as on main | The "Kokiri Forest" place-name card follows the HUD too: at 21:9 it moves to the left edge, and at 4:3 it is no longer squeezed. The `wide ui` lines from the PR's runs: ``` wide ui: picture 2.3889: the HUD at 100% of its 16:9 size, its sides moved 660 and its top and bottom 0 canvas units; the menus at 100% wide ui: picture 1.3333: the HUD at 89% of its 16:9 size, its sides moved -300 and its top and bottom 135 canvas units; the menus at 75% wide ui: picture 0.0000: the HUD at 100% of its 16:9 size, its sides moved 0 and its top and bottom 0 canvas units; the menus at 100% wide ui: the plan took 0.147 ms a present over the last 600 wide ui: the plan took 0.512 ms a present over the last 600 ``` - **The plan's time** on this i7-12700K: 0.142-0.150 ms a present in play at both shapes. With the pause menu open it is 0.47-0.56 ms a present at both shapes. These come from the log of ordinary runs, not from a timing hold. - **The description's sample lines** end in "the menus at 75%, their sides moved 0". This head's line stops at "the menus at 75%". - **Errors:** none in all six runs. fatal, crash, device lost and kernel/NVRM lines are all 0. The PR's logs have no warning or error that main's lack; the only new lines are the `wide ui` lines. - **Frame rate** (from the logs' fps lines, one ordinary run each, with the desktop on the same card, so not a measurement): within 1 fps of main in play at 1920x1080 and 1440x1080. At 3440x1440 the PR's run read 29-33 fps for the first 700 presents after the load (presents 1560-2280), against main's 37-40. From then on both read 43-45. I didn't repeat it. Not checked: Windows, AMD, Intel, a window or a real monitor of these shapes (headless only), the loading fade from mooki's comment, text boxes, the map page, other HUDs, other shapes, `HOS_WIDE_UI=0`, and timing. **3440x1440, play:** main, then this PR ![main 91a1ae8b, 3440x1440, play](https://git.magicbean.dev/attachments/caa8438b-7f55-4cf0-9792-b79dcfd465b4) ![this PR 607a186c, 3440x1440, play](https://git.magicbean.dev/attachments/5c1715d0-e6d4-4412-8fc3-020324713147) **3440x1440, pause menu:** main, then this PR ![main 91a1ae8b, 3440x1440, pause menu](https://git.magicbean.dev/attachments/2d0d0106-716c-4f55-9817-af827a84b7ee) ![this PR 607a186c, 3440x1440, pause menu](https://git.magicbean.dev/attachments/4b1ee1dd-a77a-4399-b688-f92b64f17de4) **1440x1080, play:** main, then this PR ![main 91a1ae8b, 1440x1080, play](https://git.magicbean.dev/attachments/5c884257-4694-45d3-8c5e-d91394a201ec) ![this PR 607a186c, 1440x1080, play](https://git.magicbean.dev/attachments/c58b9089-169e-4b66-bd99-131cb3954e6a) **1440x1080, pause menu:** main, then this PR ![main 91a1ae8b, 1440x1080, pause menu](https://git.magicbean.dev/attachments/e2fa0f09-7962-477f-860c-8bbd92ba9a3f) ![this PR 607a186c, 1440x1080, pause menu](https://git.magicbean.dev/attachments/3f0c7936-d508-4549-8af3-dfd7c77846ab)
First-time contributor

Tested on a real ultrawide: 5120x2160 (RTX 5090, Windows 11)

Build: feat/hud-layout @ 607a186c, tested as is (the head still conflicts with main). Built for Windows with nx2/make_game.py and shaders prepared, using llvm-mingw 20261104 and the Hookshot Builder's cpm / wgpu-native / third_party / work. The build took 23 min.

Hardware:

  • RTX 5090 32 GB, NVIDIA 621.14; Core i7-13700K, 98 GB; Windows 11 Pro.
  • LG UltraGear ultrawide, native 5120x2160 (Windows shows it as 4096x1728 at 125% scaling).

Method:

  • Windowed, the game's own screenshots at the picture's full size (RX_VK_SHOT_W/H).
  • Scripted input from a Kokiri Forest autosave: Continue, standing, talk to Saria (Talk_00), the pause menu.
  • Aspect ratio at Monitor's shape (ultrawide=1), Output resolution 5120x2160.
  • One build, two runs back to back with warm caches: the default (this PR) and HOS_WIDE_UI=0 (the old layout).
  • The other shapes come from the Output resolution row (output_res): 32:9 5120x1440, 16:10 2560x1600, 4:3 1920x1440. 16:9 1920x1080 ran with Aspect ratio 16:9.
  • HOS_WIDE_UI_LOG=1 in every run, plus one run with HOS_WIDE_UI_LOG=20000 for the pane list.

The screenshots are attached in pairs: this PR on top, HOS_WIDE_UI=0 below.

Check Result Notes
5120x2160 gameplay (1-gameplay-location) ✅ pass The log reads "picture 2.3704: the HUD at 100% of its 16:9 size, its sides moved 640". The hearts sit in the top-left corner and the minimap in the bottom-right corner, at the screen's edges. With HOS_WIDE_UI=0 both sit at the middle 16:9's corners. Nothing is clipped, off-screen or overlapping, and the scale is right (the same size in both)
Location name (Location_00) ✅ pass "KOKIRI FOREST" moves to the bottom-left edge (N_SmallDisplay_00 left −640)
Dialogue box (Talk_00 / MessageDialog_00, 2-dialogue-Talk) ✅ pass The box and Saria's name stay centred and unstretched, the same in both layouts
Dialogue's button guide ("Hide" / "Conversation Log") ⚠️ stays in the 16:9 position See finding 2 below. It's the same in both layouts, so not a regression, but it now sits apart from the rest of the HUD, which is at the edges
The action prompt (DoCommand_00, "A Talk" with X/Y/B) ✅ as designed It stays beside Link in the middle in both layouts; it's classed as a menu's and none of its 15 panes move
Pause menu (3-pause-menu) ✅ pass It's the same in both layouts at 5120x2160: centred 16:9, not stretched, the scene dimmed at the sides. That matches "menus unchanged on wider screens"
Loading fade (4-loading-fade) ❌ still present on 607a186c See finding 1 below
32:9 5120x1440 (5-shape-5120x1440) ✅ pass It stops at 12:5's sides as described (sides moved 672). The hearts and minimap sit inward of the screen corners, wider than HOS_WIDE_UI=0's middle 16:9
16:10 2560x1600 (5-shape-2560x1600) ✅ pass The HUD stays at 100% (sides moved −192), with the hearts and minimap in the corners. Old: the HUD at 90%
4:3 1920x1440 (5-shape-1920x1440) ✅ pass The HUD at 89% with its top and bottom moved 135, so the hearts sit higher and the minimap lower. Old: 75%
16:9 1920x1080 (5-shape-1920x1080) ✅ unchanged "picture 0.0000: the HUD at 100%, sides moved 0" in both, and the pictures match apart from the scene's animation
Flicker ✅ none seen No flicker was seen in the screenshots, which were taken every 60 presents. I didn't check frame by frame
Plan time (HOS_WIDE_UI_LOG=1) ✅ 0.08–0.13 ms a present At 5120x2160: 0.105–0.133 ms. At the other shapes: 0.08–0.12 ms
Frame rate, 5120x2160 warm ✅ no change ~57–62 fps in both layouts (vi: lines). This is a reference reading, not a measurement
Log ✅ clean No crash, device-lost, VK_ERROR or validation lines in 15 runs

Finding 1: the loading fade still leaves the ultrawide sides lit (mooki's report)

  • What happens: coming out of a load at 5120x2160, the fade covers only the middle 16:9. The sides keep showing the scene at full brightness, so the fade is a dark 16:9 block between two bright strips (4-loading-fade).
  • On this head: it's still present on 607a186c, with the PR and with HOS_WIDE_UI=0.
  • The plan log: it classes LoadingFade_00 as a menu's ("over the whole picture" as a root layout), but none of its 11 panes move. Its fade, P_Fade_00, stays at the 16:9 canvas's width, x [−1920 1920] with width ×1.000. The loading icon (N_LoadIcon_00, x 1778) also stays at the 16:9 bottom-right, not the screen's.
  • Fix: this is the bug llun said he'll fold in.

Finding 2: the dialogue's button guide stays at the 16:9 edge

  • What happens: during dialogue the "Hide (L) / Conversation Log (Y)" guide sits at the 16:9 picture's right edge, not the screen's. In play, the HUD it replaces (the minimap) is at the screen's right edge, so the guide is ~640 canvas units further in.
  • Panes: the guide is in MessageLog_01, classed as "a menu's": A_GuideAll_00 at x 1840, Pa_HideGuide_00 at x 1193.5 and Pa_Guide_00 at x 1795, all rule 0, stays.
  • Same layout: MessageDialog_00's own choice buttons (N_BtnNum_00 x 1460 and N_BtnIn_00–04 x ~1520–1532) are also unmoved.
  • Fix: they would need to be in HUD_LAYOUTS (MessageLog_01 at least), or have a rule for those panes.
  • Corner buttons: this scene shows no top-right button cluster in play, so I couldn't check one.

Not tested

  • The item quick select, the map page, shops, the horse and boss HUDs, and movies.
  • A window smaller than the output.
  • DLSS and frame generation (off in these runs).
  • 21:9 at 3440x1440 (the user's monitor is 2.37, close to 21:9).

Screenshots (each: this PR on top, HOS_WIDE_UI=0 below)

1-gameplay-location-5120x2160.jpg

2-dialogue-Talk-5120x2160.jpg

3-pause-menu-5120x2160.jpg

4-loading-fade-5120x2160.jpg

5-shape-1920x1080.jpg

5-shape-1920x1440.jpg

5-shape-2560x1600.jpg

5-shape-5120x1440.jpg

## Tested on a real ultrawide: 5120x2160 (RTX 5090, Windows 11) **Build:** feat/hud-layout @ **607a186c**, tested as is (the head still conflicts with main). Built for Windows with `nx2/make_game.py` and shaders prepared, using llvm-mingw 20261104 and the Hookshot Builder's cpm / wgpu-native / third_party / work. The build took 23 min. **Hardware:** - RTX 5090 32 GB, NVIDIA 621.14; Core i7-13700K, 98 GB; Windows 11 Pro. - LG UltraGear ultrawide, native **5120x2160** (Windows shows it as 4096x1728 at 125% scaling). **Method:** - Windowed, the game's own screenshots at the picture's full size (`RX_VK_SHOT_W/H`). - Scripted input from a Kokiri Forest autosave: Continue, standing, talk to Saria (`Talk_00`), the pause menu. - Aspect ratio at Monitor's shape (`ultrawide=1`), Output resolution 5120x2160. - One build, two runs back to back with warm caches: the default (this PR) and `HOS_WIDE_UI=0` (the old layout). - The other shapes come from the Output resolution row (`output_res`): 32:9 5120x1440, 16:10 2560x1600, 4:3 1920x1440. 16:9 1920x1080 ran with Aspect ratio 16:9. - `HOS_WIDE_UI_LOG=1` in every run, plus one run with `HOS_WIDE_UI_LOG=20000` for the pane list. The screenshots are attached in pairs: this PR on top, `HOS_WIDE_UI=0` below. | Check | Result | Notes | |---|---|---| | **5120x2160 gameplay** (`1-gameplay-location`) | ✅ pass | The log reads "picture 2.3704: the HUD at 100% of its 16:9 size, its sides moved 640". The hearts sit in the top-left corner and the minimap in the bottom-right corner, at the screen's edges. With `HOS_WIDE_UI=0` both sit at the middle 16:9's corners. Nothing is clipped, off-screen or overlapping, and the scale is right (the same size in both) | | **Location name** (`Location_00`) | ✅ pass | "KOKIRI FOREST" moves to the bottom-left edge (`N_SmallDisplay_00` left −640) | | **Dialogue box** (`Talk_00` / `MessageDialog_00`, `2-dialogue-Talk`) | ✅ pass | The box and Saria's name stay centred and unstretched, the same in both layouts | | **Dialogue's button guide** ("Hide" / "Conversation Log") | ⚠️ **stays in the 16:9 position** | See finding 2 below. It's the same in both layouts, so not a regression, but it now sits apart from the rest of the HUD, which is at the edges | | **The action prompt** (`DoCommand_00`, "A Talk" with X/Y/B) | ✅ as designed | It stays beside Link in the middle in both layouts; it's classed as a menu's and none of its 15 panes move | | **Pause menu** (`3-pause-menu`) | ✅ pass | It's the same in both layouts at 5120x2160: centred 16:9, not stretched, the scene dimmed at the sides. That matches "menus unchanged on wider screens" | | **Loading fade** (`4-loading-fade`) | ❌ **still present on 607a186c** | See finding 1 below | | **32:9 5120x1440** (`5-shape-5120x1440`) | ✅ pass | It stops at 12:5's sides as described (sides moved 672). The hearts and minimap sit inward of the screen corners, wider than `HOS_WIDE_UI=0`'s middle 16:9 | | **16:10 2560x1600** (`5-shape-2560x1600`) | ✅ pass | The HUD stays at 100% (sides moved −192), with the hearts and minimap in the corners. Old: the HUD at 90% | | **4:3 1920x1440** (`5-shape-1920x1440`) | ✅ pass | The HUD at 89% with its top and bottom moved 135, so the hearts sit higher and the minimap lower. Old: 75% | | **16:9 1920x1080** (`5-shape-1920x1080`) | ✅ unchanged | "picture 0.0000: the HUD at 100%, sides moved 0" in both, and the pictures match apart from the scene's animation | | **Flicker** | ✅ none seen | No flicker was seen in the screenshots, which were taken every 60 presents. I didn't check frame by frame | | **Plan time** (`HOS_WIDE_UI_LOG=1`) | ✅ 0.08–0.13 ms a present | At 5120x2160: 0.105–0.133 ms. At the other shapes: 0.08–0.12 ms | | **Frame rate, 5120x2160 warm** | ✅ no change | ~57–62 fps in both layouts (`vi:` lines). This is a reference reading, not a measurement | | **Log** | ✅ clean | No crash, device-lost, `VK_ERROR` or validation lines in 15 runs | ### Finding 1: the loading fade still leaves the ultrawide sides lit (mooki's report) - **What happens:** coming out of a load at 5120x2160, the fade covers only the middle 16:9. The sides keep showing the scene at full brightness, so the fade is a dark 16:9 block between two bright strips (`4-loading-fade`). - **On this head:** it's still present on 607a186c, with the PR and with `HOS_WIDE_UI=0`. - **The plan log:** it classes `LoadingFade_00` as a menu's ("over the whole picture" as a root layout), but none of its 11 panes move. Its fade, `P_Fade_00`, stays at the 16:9 canvas's width, x [−1920 1920] with width ×1.000. The loading icon (`N_LoadIcon_00`, x 1778) also stays at the 16:9 bottom-right, not the screen's. - **Fix:** this is the bug llun said he'll fold in. ### Finding 2: the dialogue's button guide stays at the 16:9 edge - **What happens:** during dialogue the "Hide (L) / Conversation Log (Y)" guide sits at the 16:9 picture's right edge, not the screen's. In play, the HUD it replaces (the minimap) is at the screen's right edge, so the guide is ~640 canvas units further in. - **Panes:** the guide is in **`MessageLog_01`**, classed as "a menu's": `A_GuideAll_00` at x 1840, `Pa_HideGuide_00` at x 1193.5 and `Pa_Guide_00` at x 1795, all rule 0, stays. - **Same layout:** `MessageDialog_00`'s own choice buttons (`N_BtnNum_00` x 1460 and `N_BtnIn_00`–`04` x ~1520–1532) are also unmoved. - **Fix:** they would need to be in `HUD_LAYOUTS` (`MessageLog_01` at least), or have a rule for those panes. - **Corner buttons:** this scene shows no top-right button cluster in play, so I couldn't check one. ### Not tested - The item quick select, the map page, shops, the horse and boss HUDs, and movies. - A window smaller than the output. - DLSS and frame generation (off in these runs). - 21:9 at 3440x1440 (the user's monitor is 2.37, close to 21:9). ### Screenshots (each: this PR on top, `HOS_WIDE_UI=0` below) ![1-gameplay-location-5120x2160.jpg](https://git.magicbean.dev/attachments/03925e45-56f8-4cde-8be2-572321bb5164) ![2-dialogue-Talk-5120x2160.jpg](https://git.magicbean.dev/attachments/e5630cdc-ee70-4f6b-8ce3-b6c145f0dab7) ![3-pause-menu-5120x2160.jpg](https://git.magicbean.dev/attachments/c580976a-d1bb-4a14-a280-365fff739eb2) ![4-loading-fade-5120x2160.jpg](https://git.magicbean.dev/attachments/43cfca7e-84aa-4ba1-ae08-1dff4e14881e) ![5-shape-1920x1080.jpg](https://git.magicbean.dev/attachments/6daa3d8a-4e45-4fff-bb97-1ba8364a1a2b) ![5-shape-1920x1440.jpg](https://git.magicbean.dev/attachments/2b49af67-7bf7-4f4e-9aaa-7a0637b8153e) ![5-shape-2560x1600.jpg](https://git.magicbean.dev/attachments/9d8e96d8-40c5-411a-ad9b-d48ad6add1ee) ![5-shape-5120x1440.jpg](https://git.magicbean.dev/attachments/05239ad2-a44e-431f-a938-7a054fb9f8ce)
HumanBean left a comment

Code review of 607a186c (code only: I didn't build it or run it).

The planner and the per-shape math look right. At 16:9, hos_wide_ratio and hos_wide_squeeze are 0, so the projection site is skipped and ui_on stays 0. The only thing that happens is the boot-time forced remake for 3 presents, and it gives the same transforms, so 16:9 is the game's own. The numbers for 21:9 to 48:9 and for 16:10 to 5:4 match the tests and dukenheimer's logs (660, 672, -192, -300/135). The game gets no mouse or touch input (display.c, nn_hid.c), so no hit-testing has to follow the moved panes. ozwaldorf's credit is in the file header, the commit message and a Co-Authored-By trailer.

Before this can merge:

  1. The loading fade, still open. mooki's point from the comments isn't fixed on this head. dukenheimer saw it again on 607a186c at 5120x2160. llun said the fix would be folded in here, and nothing has been pushed since. The fade check in tests/test_hud_layout.py still reads KEEP_VTABLES[2].
  2. The loading fade on narrower screens is new in this PR. With the default Ultrawide menus row, bit 3 is cleared (kMenus = 0xa), so UILoadingFadeScreen goes through the projection site. On main a narrower screen gives _q = hos_wide_squeeze = 1, a no-op. Here it gets hos_ui_qy = k: 0.889 at 4:3 and 0.833 at 5:4, or r / (16/9) at any narrower shape with HOS_WIDE_UI=0. Being in FULL_LAYOUTS only stops the menu fit, not the projection scale. So the fade stops short of the top and bottom (inline on rx_backend.c).
  3. It conflicts with main in docs/tested-configurations.md (both add rows at the top of the table), and it is 50 commits behind. The "Rebased on current main" box is ticked, but that's not true now.

Questions, each needing a run at a narrower shape:

  • Dialogue. Talk_00 is drawn at the HUD's size. MessageDialog_00 and MessageLog_01 are classed as menus (dukenheimer's log) and fitted. On a narrower screen, the text box and its choices and button guide come out at two scales: 100% and 90% at 16:10, 89% and 75% at 4:3. This ties in with dukenheimer's finding 2.
  • The map page. MapMenu_00 is in HUD_LAYOUTS, and tests/test_wide_ui.py models the map page as its own root. If the game does the same, the map page is planned and never fitted, while the rest of the pause menu is fitted. Then its side panes go to the screen's edges with the pause menu's frame still in the middle, and on a narrower screen it is drawn wider than the screen (1.185 of the width at 4:3). The map page is listed as untested.

A nit: with the pause menu open, the plan took 0.47 to 0.56 ms a present (tonesi's i7-12700K run), up from 0.10 to 0.15 ms in play. The plan builds the tree, checks alive() and runs rest_of for every pane it has noted, menus included, though only the HUD's are planned. The Measurements section only gives the in-play figure. Also, the sample wide ui line in the description still ends in "their sides moved 0", which this head no longer prints.

Code review of 607a186c (code only: I didn't build it or run it). The planner and the per-shape math look right. At 16:9, `hos_wide_ratio` and `hos_wide_squeeze` are 0, so the projection site is skipped and `ui_on` stays 0. The only thing that happens is the boot-time forced remake for 3 presents, and it gives the same transforms, so 16:9 is the game's own. The numbers for 21:9 to 48:9 and for 16:10 to 5:4 match the tests and dukenheimer's logs (660, 672, -192, -300/135). The game gets no mouse or touch input (`display.c`, `nn_hid.c`), so no hit-testing has to follow the moved panes. ozwaldorf's credit is in the file header, the commit message and a Co-Authored-By trailer. Before this can merge: 1. **The loading fade, still open.** mooki's point from the comments isn't fixed on this head. dukenheimer saw it again on 607a186c at 5120x2160. llun said the fix would be folded in here, and nothing has been pushed since. The fade check in `tests/test_hud_layout.py` still reads `KEEP_VTABLES[2]`. 2. **The loading fade on narrower screens is new in this PR.** With the default Ultrawide menus row, bit 3 is cleared (`kMenus = 0xa`), so `UILoadingFadeScreen` goes through the projection site. On main a narrower screen gives `_q = hos_wide_squeeze = 1`, a no-op. Here it gets `hos_ui_qy = k`: 0.889 at 4:3 and 0.833 at 5:4, or `r / (16/9)` at any narrower shape with `HOS_WIDE_UI=0`. Being in `FULL_LAYOUTS` only stops the menu fit, not the projection scale. So the fade stops short of the top and bottom (inline on `rx_backend.c`). 3. **It conflicts with main** in `docs/tested-configurations.md` (both add rows at the top of the table), and it is 50 commits behind. The "Rebased on current main" box is ticked, but that's not true now. Questions, each needing a run at a narrower shape: - **Dialogue.** `Talk_00` is drawn at the HUD's size. `MessageDialog_00` and `MessageLog_01` are classed as menus (dukenheimer's log) and fitted. On a narrower screen, the text box and its choices and button guide come out at two scales: 100% and 90% at 16:10, 89% and 75% at 4:3. This ties in with dukenheimer's finding 2. - **The map page.** `MapMenu_00` is in `HUD_LAYOUTS`, and `tests/test_wide_ui.py` models the map page as its own root. If the game does the same, the map page is planned and never fitted, while the rest of the pause menu is fitted. Then its side panes go to the screen's edges with the pause menu's frame still in the middle, and on a narrower screen it is drawn wider than the screen (1.185 of the width at 4:3). The map page is listed as untested. A nit: with the pause menu open, the plan took 0.47 to 0.56 ms a present (tonesi's i7-12700K run), up from 0.10 to 0.15 ms in play. The plan builds the tree, checks `alive()` and runs `rest_of` for every pane it has noted, menus included, though only the HUD's are planned. The Measurements section only gives the in-play figure. Also, the sample `wide ui` line in the description still ends in "their sides moved 0", which this head no longer prints.
@ -0,0 +152,4 @@
/* (the HUD's root layouts: the hearts, magic, rupees and buttons; the place's name; the minimap (the pause menu's map page is
the same layout); the text box; and the ones placed from the world, never moved. Kept the size of a picture over the
whole screen: the fades and the system overlay, kept whole by their screens) */
static const char *const HUD_LAYOUTS[] = {"PlayerStatus_00", "Location_00", "MapMenu_00", "Talk_00", "PlayerStatus3D_00", "Pointer_00",
Owner

On a narrower screen, Talk_00 is drawn at the HUD's size (k: 100% at 16:10, 89% at 4:3). dukenheimer's log classes MessageDialog_00 and MessageLog_01 as menus, so they're fitted (r / (16/9): 90% and 75%). So the text box and its choices and button guide come out at different scales and no longer line up. Could you check a dialogue with choices at 16:10 and 4:3? Adding those two to HUD_LAYOUTS would probably fix this and dukenheimer's finding 2 together.

On a narrower screen, `Talk_00` is drawn at the HUD's size (k: 100% at 16:10, 89% at 4:3). dukenheimer's log classes `MessageDialog_00` and `MessageLog_01` as menus, so they're fitted (r / (16/9): 90% and 75%). So the text box and its choices and button guide come out at different scales and no longer line up. Could you check a dialogue with choices at 16:10 and 4:3? Adding those two to `HUD_LAYOUTS` would probably fix this and dukenheimer's finding 2 together.
@ -0,0 +154,4 @@
whole screen: the fades and the system overlay, kept whole by their screens) */
static const char *const HUD_LAYOUTS[] = {"PlayerStatus_00", "Location_00", "MapMenu_00", "Talk_00", "PlayerStatus3D_00", "Pointer_00",
"BalloonMessage_00"};
static const char *const FULL_LAYOUTS[] = {"LoadingFade_00", "SystemOverlay_00"};
Owner

LoadingFade_00 being in FULL_LAYOUTS ("kept whole by their screens") only skips the menu fit. With the default menus row, its screen isn't kept whole, on wider or on narrower screens, so the projection still scales it. This is the fade-pane widening llun said would be folded in here (mooki's comment). dukenheimer still sees it on 607a186c.

`LoadingFade_00` being in `FULL_LAYOUTS` ("kept whole by their screens") only skips the menu fit. With the default menus row, its screen isn't kept whole, on wider or on narrower screens, so the projection still scales it. This is the fade-pane widening llun said would be folded in here (mooki's comment). dukenheimer still sees it on 607a186c.
@ -0,0 +168,4 @@
/* (MapMenu_00 is also the HUD's minimap: there the icons' group sits over the minimap and moves out with it; kept only at
the canvas's origin, where the map page puts it. While it does, the area title and the world map card stay on the map,
which stays 16:9. The icons' part itself is never looked into: its icons move as one with their group) */
{"MapMenu_00", "N_MapIcon_00", RULE_KEEP_ORIGIN}, {"MapMenu_00", "Pa_MapIcon_00", RULE_KEEP},
Owner

These rules only matter if the plan walks MapMenu_00 while it shows the map page. tests/test_wide_ui.py models it that way, as a root of its own (place(mroot2, 0, 0)). If the game does the same, the map page is HUD here: its side panes move to the screen's edges while the pause menu's frame stays in the middle 16:9, and on a narrower screen it isn't fitted with the rest of the pause menu (1.185 of the width at 4:3, wider than the screen). The map page is listed as untested. Could you run it at 21:9 and 4:3? If it's as above, a MapMenu_00 showing its page (in_tab) could be treated as a menu.

These rules only matter if the plan walks `MapMenu_00` while it shows the map page. `tests/test_wide_ui.py` models it that way, as a root of its own (`place(mroot2, 0, 0)`). If the game does the same, the map page is HUD here: its side panes move to the screen's edges while the pause menu's frame stays in the middle 16:9, and on a narrower screen it isn't fitted with the rest of the pause menu (1.185 of the width at 4:3, wider than the screen). The map page is listed as untested. Could you run it at 21:9 and 4:3? If it's as above, a `MapMenu_00` showing its page (`in_tab`) could be treated as a menu.
@ -0,0 +434,4 @@
ext_done[i] = rest_done[i] = ext_ydone[i] = 0;
rest[i] = plan_new[i] = plany_new[i] = 0.0f, wide_new[i] = 1.0f;
}
if (on) {
Owner

Nit: from here on, every noted pane is linked, checked with alive() and given rest_of, menus included, but only the HUD's roots are visited. tonesi measured 0.47 to 0.56 ms a present with the pause menu open, against 0.10 to 0.15 ms in play. Could the menu-open figure go in the Measurements? Skipping panes whose root isn't the HUD's would also help.

Nit: from here on, every noted pane is linked, checked with `alive()` and given `rest_of`, menus included, but only the HUD's roots are visited. tonesi measured 0.47 to 0.56 ms a present with the pause menu open, against 0.10 to 0.15 ms in play. Could the menu-open figure go in the Measurements? Skipping panes whose root isn't the HUD's would also help.
@ -164,1 +164,4 @@
hos_wide_keep_mask = forced ? (uint32_t)strtoul(forced, NULL, 16) : keep_mask;
extern volatile float hos_wide_ratio; /* (narrower than 16:9: the menus fit its width whatever the Ultrawide menus row says,
never stretched up and down; the row is about wider screens) */
if (!forced && hos_wide_ratio > 0.0f && hos_wide_ratio < 16.0f / 9.0f * 0.999f) hos_wide_keep_mask &= ~2u;
Owner

On a narrower screen this drops only bit 1. With the default Ultrawide menus row, bit 3 (UILoadingFadeScreen) is already cleared (kMenus = 0xa in vk_framegen.cpp), so the loading fade now goes through the projection site with hos_ui_qy = k: 0.889 at 4:3 and 0.833 at 5:4, or r / (16/9) at any narrower shape with HOS_WIDE_UI=0. On main the site multiplies by hos_wide_squeeze, which is 1 on a narrower screen, so the fade covered everything. Now it leaves the scene lit above and below. Keeping bit 3 here as well (hos_wide_keep_mask = (hos_wide_keep_mask & ~2u) | 8u) would put it back the way main has it. The wider case is mooki's point, which is still open.

On a narrower screen this drops only bit 1. With the default Ultrawide menus row, bit 3 (`UILoadingFadeScreen`) is already cleared (`kMenus = 0xa` in `vk_framegen.cpp`), so the loading fade now goes through the projection site with `hos_ui_qy = k`: 0.889 at 4:3 and 0.833 at 5:4, or `r / (16/9)` at any narrower shape with `HOS_WIDE_UI=0`. On main the site multiplies by `hos_wide_squeeze`, which is 1 on a narrower screen, so the fade covered everything. Now it leaves the scene lit above and below. Keeping bit 3 here as well (`hos_wide_keep_mask = (hos_wide_keep_mask & ~2u) | 8u`) would put it back the way main has it. The wider case is mooki's point, which is still open.
@ -0,0 +211,4 @@
build_run(a.cc, FIXTURE_HEAD + body + FIXTURE_MAIN, "plan")
head = FIXTURE_HEAD.replace("volatile float hos_wide_squeeze, hos_wide_ratio;", "volatile float hos_wide_squeeze;")
site = SITE_MAIN.replace("SITE", oot_wide.HUD).replace("FADES", "0x%xULL" % oot_wide.KEEP_VTABLES[2])
Owner

Still open from mooki's comment: KEEP_VTABLES[2] is the event fade. The loading fade is KEEP_VTABLES[3], and with hos_wide_keep_mask = 0x5 (the default row) this site would scale it. A check on it, at a narrower hos_ui_qy too, would catch both fade issues.

Still open from mooki's comment: `KEEP_VTABLES[2]` is the event fade. The loading fade is `KEEP_VTABLES[3]`, and with `hos_wide_keep_mask = 0x5` (the default row) this site would scale it. A check on it, at a narrower `hos_ui_qy` too, would catch both fade issues.
llun force-pushed feat/hud-layout from 607a186c44
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 2s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
to 7cedec2d56
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 5s
CI / Workflow lint (actionlint) (pull_request) Successful in 8s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Failing after 5s
2026-11-13 19:46:08 +00:00
Compare
Author
Member

Review round pushed: 7cedec2d, one commit on main 514591a3 (credit and trailers kept). The description is updated with the numbers and pictures below.

@HumanBean, thanks for the careful read. Point by point:

  1. The loading fade on wider screens: fixed. The fade's pane across the canvas (P_Fade_00) is drawn the whole picture's width when its layout sits in the middle 16:9, and its icon stays where it is. The check now reads the loading fade's own screen, KEEP_VTABLES[3], at 21:9 and at 4:3, and fails without the fix. In game at 2560x1080, during the load main and 607a186c leave the sides lit (mean 229 to 230 of 255) while the middle is black. This head has 0 there, and the sides lift with the middle.
  2. The loading fade on narrower screens (your rx_backend.c comment): fixed, but not by keeping bit 3. I draw the same pane taller (1 / hos_ui_qy) instead. Kept whole, the fade's 16:9 layout would be squeezed across a 4:3 screen, its icon and text with it, as on main. This way they keep their shape and the fade still covers the full height. In game at 1440x1080, 607a186c lit the bands above and below at 104 while the middle was black; this head has 0. Main is 0 there too (it keeps the screen whole).
  3. Rebased onto 514591a3, and the tested-configurations.md conflict is gone.

Your questions:

  • The map page: you were right. The map (View button, in play) is the minimap's own MapMenu_00 layout as a root of its own: the log shows the same pane address going from "HUD" to "menu" and back. While it shows its page, it's now drawn as a menu's (as_menu, counted in the plan's change check). It isn't walked or moved, and on a narrower screen it's fitted to the width. At 4:3 it's fitted with the other menus now (main stretches it), and at 21:9 it's in the middle as on main. The pause menu itself has no map page (Collection, Equipment, Items, System).
  • Dialogue: MessageDialog_00 is now a layout of its own kind: held where the game puts it at the HUD's size, the box's own scale, never moved or fitted. MessageLog_01 is in HUD_LAYOUTS, so its guide follows the minimap to the edge. Both are checked game-free (each check fails without its change). I couldn't get a text box in game: Navi had nothing to say on any D-pad direction in my route.
  • The nit, the plan with a menu open: only the HUD's trees and the loading fade's are noted now (a layout's root pane, then panes made from a noted one). A menu's panes and a HUD part inside a menu (the pause bar's hearts) are never noted. On this PC (Ryzen 9 9900X3D), 607a186c then this head, ms a present: play 0.10 to 0.11 then 0.05 to 0.06; the map open 0.09 to 0.12 then 0.05; the pause menu open 0.13 to 0.19 then 0.05 to 0.06. They're in the Measurements, and the sample wide ui line is fixed.

@mooki: your fade-pane widening is folded in, as you proposed in comment 12471, and the check reads KEEP_VTABLES[3]. #57 can shrink to whatever else it covers.

@dukenheimer: thank you for the 5120x2160 run. Finding 1 (the fade) and finding 2 (the "Hide / Conversation Log" guide) should both be fixed on 7cedec2d. The loading icon staying at the middle 16:9's bottom right is by design: only the black pane is widened. Could you re-check a dialogue with your Saria save, at 5120x2160 and at one narrower shape (16:10 or 4:3)? I'd like to see the guide at the screen's edge, and any choices at the same scale as the box.

@tonesi: thanks for the Linux run and the pause-menu figure. That's what led to this round's change in what the plan notes.

@TrashPanda: your WIP #287 brings over Hookshot's wide_cover for the same loading fade. The two are compatible, but they conflict in the text of the HUD site in nx2/oot_wide.py, so whichever lands second has to merge it.

python -I -B tests/run_all.py: 490 passed, 6 failed. The same 6 fail on main 514591a3 on this PC.

Review round pushed: **7cedec2d**, one commit on main 514591a3 (credit and trailers kept). The description is updated with the numbers and pictures below. @HumanBean, thanks for the careful read. Point by point: 1. **The loading fade on wider screens:** fixed. The fade's pane across the canvas (`P_Fade_00`) is drawn the whole picture's width when its layout sits in the middle 16:9, and its icon stays where it is. The check now reads the loading fade's own screen, `KEEP_VTABLES[3]`, at 21:9 and at 4:3, and fails without the fix. In game at 2560x1080, during the load main and 607a186c leave the sides lit (mean 229 to 230 of 255) while the middle is black. This head has 0 there, and the sides lift with the middle. 2. **The loading fade on narrower screens** (your `rx_backend.c` comment): fixed, but not by keeping bit 3. I draw the same pane taller (`1 / hos_ui_qy`) instead. Kept whole, the fade's 16:9 layout would be squeezed across a 4:3 screen, its icon and text with it, as on main. This way they keep their shape and the fade still covers the full height. In game at 1440x1080, 607a186c lit the bands above and below at 104 while the middle was black; this head has 0. Main is 0 there too (it keeps the screen whole). 3. **Rebased** onto 514591a3, and the `tested-configurations.md` conflict is gone. Your questions: - **The map page:** you were right. The map (View button, in play) is the minimap's own `MapMenu_00` layout as a root of its own: the log shows the same pane address going from "HUD" to "menu" and back. While it shows its page, it's now drawn as a menu's (`as_menu`, counted in the plan's change check). It isn't walked or moved, and on a narrower screen it's fitted to the width. At 4:3 it's fitted with the other menus now (main stretches it), and at 21:9 it's in the middle as on main. The pause menu itself has no map page (Collection, Equipment, Items, System). - **Dialogue:** `MessageDialog_00` is now a layout of its own kind: held where the game puts it at the HUD's size, the box's own scale, never moved or fitted. `MessageLog_01` is in `HUD_LAYOUTS`, so its guide follows the minimap to the edge. Both are checked game-free (each check fails without its change). I couldn't get a text box in game: Navi had nothing to say on any D-pad direction in my route. - **The nit, the plan with a menu open:** only the HUD's trees and the loading fade's are noted now (a layout's root pane, then panes made from a noted one). A menu's panes and a HUD part inside a menu (the pause bar's hearts) are never noted. On this PC (Ryzen 9 9900X3D), 607a186c then this head, ms a present: play 0.10 to 0.11 then 0.05 to 0.06; the map open 0.09 to 0.12 then 0.05; the pause menu open 0.13 to 0.19 then 0.05 to 0.06. They're in the Measurements, and the sample `wide ui` line is fixed. @mooki: your fade-pane widening is folded in, as you proposed in comment 12471, and the check reads `KEEP_VTABLES[3]`. #57 can shrink to whatever else it covers. @dukenheimer: thank you for the 5120x2160 run. Finding 1 (the fade) and finding 2 (the "Hide / Conversation Log" guide) should both be fixed on 7cedec2d. The loading icon staying at the middle 16:9's bottom right is by design: only the black pane is widened. Could you re-check a dialogue with your Saria save, at 5120x2160 and at one narrower shape (16:10 or 4:3)? I'd like to see the guide at the screen's edge, and any choices at the same scale as the box. @tonesi: thanks for the Linux run and the pause-menu figure. That's what led to this round's change in what the plan notes. @TrashPanda: your WIP #287 brings over Hookshot's `wide_cover` for the same loading fade. The two are compatible, but they conflict in the text of the `HUD` site in `nx2/oot_wide.py`, so whichever lands second has to merge it. `python -I -B tests/run_all.py`: 490 passed, 6 failed. The same 6 fail on main 514591a3 on this PC.
Author
Member

Description: the dialogue check is test request T-3E3B (any PC with a save next to someone who asks a question). The new box "Host code builds on both Windows and Linux" stays unticked until 7cedec2d is built on Linux: 607a186c was (tonesi), and this round changes plain C in hos_wide_ui.c only. @tonesi, if you have a moment, a Linux build of 7cedec2d would settle it.

Description: the dialogue check is test request T-3E3B (any PC with a save next to someone who asks a question). The new box "Host code builds on both Windows and Linux" stays unticked until 7cedec2d is built on Linux: 607a186c was (tonesi), and this round changes plain C in hos_wide_ui.c only. @tonesi, if you have a moment, a Linux build of 7cedec2d would settle it.
Author
Member

Pictures for the review round (7cedec2d, run as 5487b3f1, the same code), from the runs in the Measurements. All are the game's own shots, headless on the RTX 5080, Ultrawide menus Centred.

The loading fade during a load, before it lifts. At 21:9, main's sides stay lit while the middle is black; this PR covers the whole picture. At 4:3, the head before this round (607a186c) lit thin bands above and below; this PR doesn't. The loading icon keeps its place in the middle 16:9.

the loading fade at 2560x1080: main above, this PR below

the loading fade at 1440x1080: 607a186c above, this PR below

The map (View button), drawn as a menu while it shows. At 21:9 it's in the middle, as on main. At 4:3 it's fitted to the width, where main stretches it to the full height.

the map at 2560x1080: main above, this PR below

the map at 1440x1080: main above, this PR below

Play and the pause menu after the round. The HUD sits at the screen's edges and the menus are unchanged at 21:9. At 4:3 the HUD is in the corners at 89%, and the pause menu is fitted to the width, where main stretches it.

play and the pause menu at 2560x1080: main above, this PR below

play and the pause menu at 1440x1080: main above, this PR below

Pictures for the review round (7cedec2d, run as 5487b3f1, the same code), from the runs in the Measurements. All are the game's own shots, headless on the RTX 5080, Ultrawide menus Centred. **The loading fade during a load, before it lifts.** At 21:9, main's sides stay lit while the middle is black; this PR covers the whole picture. At 4:3, the head before this round (607a186c) lit thin bands above and below; this PR doesn't. The loading icon keeps its place in the middle 16:9. ![the loading fade at 2560x1080: main above, this PR below](https://git.magicbean.dev/attachments/8df898b9-48d8-41a0-a26e-d6a1c65b855f) ![the loading fade at 1440x1080: 607a186c above, this PR below](https://git.magicbean.dev/attachments/4e947a68-34e7-4032-9a64-dad290913b77) **The map (View button), drawn as a menu while it shows.** At 21:9 it's in the middle, as on main. At 4:3 it's fitted to the width, where main stretches it to the full height. ![the map at 2560x1080: main above, this PR below](https://git.magicbean.dev/attachments/0f9aa221-3c7e-4adf-b83f-813fa7de1547) ![the map at 1440x1080: main above, this PR below](https://git.magicbean.dev/attachments/5c5324ec-7975-4e9a-a021-ebaaa6a342a2) **Play and the pause menu after the round.** The HUD sits at the screen's edges and the menus are unchanged at 21:9. At 4:3 the HUD is in the corners at 89%, and the pause menu is fitted to the width, where main stretches it. ![play and the pause menu at 2560x1080: main above, this PR below](https://git.magicbean.dev/attachments/f71110e5-5a61-4d1a-9430-88d00e1f1f92) ![play and the pause menu at 1440x1080: main above, this PR below](https://git.magicbean.dev/attachments/e4496f3d-292d-4e58-ba5c-f0dfbf54a899)
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 5s
CI / Workflow lint (actionlint) (pull_request) Successful in 8s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Failing after 5s
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/hud-layout:llun-feat/hud-layout'
git switch 'llun-feat/hud-layout'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
5 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!95
No description provided.