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
Contributor

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 Hookshot #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 three 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, and the world-placed ones, which stay unmoved).
    • 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 are #2843's.
  • 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, and the projection site. tests/test_wide_ui.py is #2843's, run with the menus' moves on.
  • 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 New 0: the HUD as before (wider: middle 16:9; narrower: fitted to the width, no longer stretched). 1: also moves the menus' side panes out to a wider screen's edges (#2843's Extended) 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. The commits since then up to f41db065 touch no HUD, menu or render code: forge tooling, the release scripts, the DLSS preset row (#39), and the game's own update check taken out.
this PR: feat/hud-layout @ 9c5a39c1 (the same change before its rebase onto f41db065, plus the tested-configurations row)
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%, their sides moved 0
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%, their sides moved 0
wide ui: the plan took 0.102 ms a present over the last 600

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.

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. python -I -B tests/run_all.py on the PR's commit: 484 passed, 6 failed, 40 skipped. All 6 also fail on main f41db065 on this PC: test_bake_portability, test_finish_output, test_lift_cache, test_setup_runtime, test_check_third_party, 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:

  • Text boxes with an NPC, the item quick select, the map page, 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.

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)
  • 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)

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

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 #2843's planner with the per-class extras.
  • 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.
  • mooki's #57 (wide loading fades) edits the comment beside KEEP_VTABLES and the menus' keep bits. Whichever lands second has a small text conflict there; they are compatible.

Issues

See #64

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • 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

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Flags table filled in; README / docs/development.md switch reference updated.
  • Screenshots for a change that shows on screen.
  • A row in docs/tested-configurations.md for each measured run.
  • MKWVK_API_VERSION bumped (gpu/vk/vk_api.h's MkwVkHost or MkwVkRenderer changed). n/a
  • The SPIR-V headers regenerated with glslang (a shader changed). n/a
  • 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.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Built on Windows only; the host code is plain C11 with __builtin_ctzll, as host/hos_thread.c already uses.
  • 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 Hookshot [#2843](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 three 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`, and the world-placed ones, which stay unmoved). - **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 are #2843's. - **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, and the projection site. `tests/test_wide_ui.py` is #2843's, run with the menus' moves on. - **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` | New | `0`: the HUD as before (wider: middle 16:9; narrower: fitted to the width, no longer stretched). `1`: also moves the menus' side panes out to a wider screen's edges (#2843's Extended) | 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. The commits since then up to f41db065 touch no HUD, menu or render code: forge tooling, the release scripts, the DLSS preset row (#39), and the game's own update check taken out. this PR: feat/hud-layout @ 9c5a39c1 (the same change before its rebase onto f41db065, plus the tested-configurations row) 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%, their sides moved 0 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%, their sides moved 0 wide ui: the plan took 0.102 ms a present over the last 600 ``` 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. ## 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. `python -I -B tests/run_all.py` on the PR's commit: 484 passed, 6 failed, 40 skipped. All 6 also fail on main f41db065 on this PC: test_bake_portability, test_finish_output, test_lift_cache, test_setup_runtime, test_check_third_party, 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:** - Text boxes with an NPC, the item quick select, the map page, 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. **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) - 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) ## 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) | **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 #2843's planner with the per-class extras. - **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. - mooki's #57 (wide loading fades) edits the comment beside `KEEP_VTABLES` and the menus' keep bits. Whichever lands second has a small text conflict there; they are compatible. ## Issues See #64 ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [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 - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Flags table filled in; README / docs/development.md switch reference updated. - [x] Screenshots for a change that shows on screen. - [x] A row in `docs/tested-configurations.md` for each measured run. - [ ] `MKWVK_API_VERSION` bumped (`gpu/vk/vk_api.h`'s `MkwVkHost` or `MkwVkRenderer` changed). n/a - [ ] The SPIR-V headers regenerated with glslang (a shader changed). n/a - [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. - [ ] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Built on Windows only; the host code is plain C11 with `__builtin_ctzll`, as `host/hos_thread.c` already uses. - [x] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(ui): the HUD laid out for every screen shape at its 16:9 size, anchored to the screen's edges
Some checks are pending
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
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / Python source checks (windows-2022, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (windows-2022, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 2s
c7c93f1e13
A screen narrower than 16:9 (5:4, 4:3, 3:2, 16:10) gave the HUD and menus the picture's whole width at its full
height, stretched taller; a wider one kept the HUD in the middle 16:9, far from the corners. Now, with nothing to
choose, the HUD keeps the size it has at 16:9 and what sits at a side or corner of its canvas moves out (wider) or in
(narrower) to the screen's, as a layout made for that shape would place it:

- host/hos_wide_ui.c: the pane planner of Hookshot #2843 (ozwaldorf's Extended ultrawide menus), here for the HUD's
  layouts (by their root layout's name), on wider and narrower screens and, when the HUD is drawn smaller, up and down.
  Wider than 12:5 (32:9) the moves stop at 12:5's sides; at 48:9 the HUD stays on the middle screen. Narrower than 3:2
  the HUD is drawn smaller by the ratio over 3:2 (4:3: 89%). Every other layout is a menu's: unchanged on a wider
  screen, drawn smaller from its root pane to fit a narrower one's width, never stretched.
- nx2/oot_wide.py: the UI projection's site scales across and up (hos_ui_qx, hos_ui_qy) for every screen not kept
  whole; the pane sites of #2843.
- host/rx_backend.c: on a narrower screen the menus' screen is never kept whole (stretched), whatever the Ultrawide
  menus row says (it is about wider screens).
- HOS_WIDE_UI=0 keeps the HUD as before (narrower: fitted to the width), 1 also moves the menus' side panes as #2843's
  Extended; HOS_HUD_MAX_RATIO, HOS_HUD_FULL_RATIO stand over the two limits; HOS_WIDE_UI_LOG logs the plan.
- 16:9 is the game's own, as before.

Tests: tests/test_hud_layout.py (new), tests/test_wide_ui.py (#2843's, with the menus' moves on).

Co-Authored-By: ozwaldorf <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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?
Some checks are pending
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
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / Python source checks (windows-2022, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (windows-2022, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 2s
This pull request has changes conflicting with the target branch.
  • docs/tested-configurations.md
Some workflows are waiting to be reviewed.
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

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 description provided.