feat(video): a Window choice that fills the actual window, Hor+ and Vert+ #142

Open
simonwjackson wants to merge 2 commits from simonwjackson/Longshot:feat/window-aspect into main
Contributor

Why

Monitor's shape follows the monitor, not a resized window. Window fills the actual window and keeps the 16:9 horizontal view on taller shapes, showing more above and below rather than cropping the sides.

Also proposed on Hookshot: https://github.com/simonwjackson/Hookshot/tree/feat-window-aspect.

What changed

  • Existing Aspect ratio row: adds Window, saved as ultrawide=2, using a stable -1 UI sentinel. Default monitor behavior stays unchanged.
  • Host: hos_wide.c / .h, Windows/SDL display startup and resize, hos_main.c, rx_backend.c and nx2/oot_wide.py keep the camera/culling matched to the actual window. Render targets keep their startup size.
  • UI: oot_ui_squeeze.h, vk_draw.cpp, vk_internal.h and vk_main.cpp preserve centred 16:9 UI proportions as the window resizes; no HUD docking/size controls.
  • Tests cover geometry, q4/q5 sentinels, FOV interaction, unknown→known monitor discovery, real saving and swapchain retries. README and launcher description retain the original changes.

Flags and settings

Flag What it does Default now Recommendation Helps on Doesn't help on Untested on
Graphics → Aspect ratio → Window; ultrawide=2 Hor+ on wide windows, Vert+ on tall windows; restart to select, then live resizing Monitor's shape (1), unchanged Keep the current default; opt-in accessibility/presentation choice Windows whose shape differs from the monitor Reallocate render targets during resizing; no HUD anchoring controls Windows gameplay, other GPUs, real handhelds, HDR, DLSS/FSR/FG, pause/inventory, movies, resized thumbnails
HOS_ASPECT (existing override) Keeps main’s explicit aspect override; disables Window mode while set Unset Keep unset for Window Explicit-aspect diagnostic runs Does not follow resizes Same platform gaps as Window

Measurements

main: 316f9319c8 (2026-11-13 UTC)
this PR: feat/window-aspect @ 1c66d19bab
Hardware: AMD RX 7900 XT 20 GB, RADV Mesa 26.2.3; Ryzen 5 7600X, 32 GB RAM; NixOS 26.11, Linux 7.2.7. Private headless Sway 1.12/Xwayland; udmabuf enabled; RX_VK_FORCE_MESH_FALLBACK=1; speakers disabled.
Scene: Hyrule Castle Town (Market_Child), stationary at the copied save's wooden door. 1920×1080 drawable and output, 100% render resolution, game's own upscaler, frame generation Off, game frame-rate cap Uncapped, VSync Auto, HDR Off. Effective menu values were read through the control bridge. No overlays, injectors or external shader-cache tools.
Method: One short session, main → PR → main → PR, with fresh copies of the same save. Each process warms at the stationary scene for at least 10 s, then samples for 20 s; the last five vk: N presents, X fps log lines per run are retained. The per-run median and median of the two run medians are below. These are native-present measurements at the settings below, not a claim about other scenes or 1% lows.
How to reproduce: Load a disposable copy of a Castle Town save and stay by the same wooden door, without movement/camera input. Use the settings above, with Aspect ratio Window on this PR; baseline main has stock behavior. Alternate the two binaries twice, let each settle, and take the last five presents-log readings per run. The additional feature proof below is separate from these stationary FPS samples.

Observation main @ 316f9319 this PR @ 1c66d19b
Run medians, native FPS (first / second) 53.40 / 54.30 53.40 / 52.80
Median of run medians, native FPS 53.85 53.10
Samples retained per run 5 / 5 5 / 5

Window proof: both PR runs started with persisted ultrawide=2; the bridge reported the UI's stable value=-1, text=Window. In the last run the same live game was resized from 1920×1080 to 2448×1848, 2560×1080, 1280×1024, then back to 1920×1080. Inspected captures show the full window filled, wider side visibility in ultrawide, more vertical visibility in the tall/5:4 shapes, and preserved character/HUD proportions. This does not add HUD docking at the new edges.

Live drawable Control-bridge frame State Aspect setting
1920×1080, initial 24890 play Window
2448×1848 25271 play Window
2560×1080 25582 play Window
1280×1024 25881 play Window
1920×1080, restored 26179 play Window

After the resizes, the bridge still reported value=-1, text=Window. Changing Sharpness to 10 and back to 0 kept ultrawide=2 in the saved options file. There was no separate restart-after-that-save run; the game-free real-save regression covers that persistence path. The entire session took 389.8 s. No Monitor-setting FPS sample on the feature binary is claimed; the comparison here is stock main against the running Window mode. Windows and #95's loading-fade integration remain requested, not tested.

Exact presents-log samples (five per run)
Run 1: main
[hos Presentation Thread] vk: 27120 presents, 53.2 fps; a frame: 1603 draws (0 skipped), 107 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 27240 presents, 53.4 fps; a frame: 1591 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 27360 presents, 54.3 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 27480 presents, 54.0 fps; a frame: 1604 draws (0 skipped), 107 dispatches, 165 passes, 158 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1430 textures (3409 MB)
[hos Presentation Thread] vk: 27600 presents, 53.2 fps; a frame: 1597 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1421 textures (3326 MB)
Run 2: this PR
[hos Presentation Thread] vk: 20880 presents, 53.0 fps; a frame: 1587 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 21000 presents, 53.6 fps; a frame: 1591 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1392 textures (3169 MB)
[hos Presentation Thread] vk: 21120 presents, 53.7 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1432 textures (3539 MB)
[hos Presentation Thread] vk: 21240 presents, 53.4 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1443 textures (3641 MB)
[hos Presentation Thread] vk: 21360 presents, 52.7 fps; a frame: 1599 draws (0 skipped), 107 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1439 textures (3492 MB)
Run 3: main
[hos Presentation Thread] vk: 25440 presents, 53.5 fps; a frame: 1603 draws (0 skipped), 106 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1392 textures (3169 MB)
[hos Presentation Thread] vk: 25560 presents, 54.3 fps; a frame: 1594 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1392 textures (3169 MB)
[hos Presentation Thread] vk: 25680 presents, 54.7 fps; a frame: 1595 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1395 textures (3197 MB)
[hos Presentation Thread] vk: 25800 presents, 54.6 fps; a frame: 1588 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1420 textures (3428 MB)
[hos Presentation Thread] vk: 25920 presents, 54.0 fps; a frame: 1594 draws (0 skipped), 108 dispatches, 165 passes, 158 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1431 textures (3418 MB)
Run 4: this PR
[hos Presentation Thread] vk: 24360 presents, 53.0 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 24480 presents, 52.6 fps; a frame: 1595 draws (0 skipped), 106 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1393 textures (3178 MB)
[hos Presentation Thread] vk: 24600 presents, 52.8 fps; a frame: 1592 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1414 textures (3373 MB)
[hos Presentation Thread] vk: 24720 presents, 52.8 fps; a frame: 1590 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1432 textures (3427 MB)
[hos Presentation Thread] vk: 24840 presents, 52.5 fps; a frame: 1603 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1429 textures (3400 MB)

The binaries are the previously built Linux release copies at these exact commits, with all tracked source hashes rechecked against their Git trees before this session. Native shader preparation ran for each copy before measurement. No newer or unbuilt binary is substituted.

Current-main merge: current head 1adca03782bdb6ae7a526fa184bc44800f1b2e02 merges the Longshot main snapshot fetched for this run, dd02c6cca9582f5bb1b992d5c43268bf5d846c3f, into the existing PR branch with a merge commit and a normal push; no history rewrite. The earlier measurements and screenshots remain evidence only at their named commits, not a new performance comparison on this main. The merge was rebuilt and smoke-tested as described below. Main advanced to fdc2840b during validation; a three-way merge check against that newer head is clean, but it is not included in the tested binary.

Testing

Current merge validation (1adca03782bd, 2026-11-13): README keeps main’s current rows with the Window choice; test_output_resolution.py retains main’s new default-output cases and the Window/save cases. Focused feature/option-ID/README/menu checks: 11 passed, 0 failed. Full suite on zao, with /tmp/longshot-carry/env.sh and the shared suite lock: 506 passed, 3 failed, 16 skipped in 508 s (linux, 8 at once). All three failures reproduce on untouched main dd02c6cc: test_display_pointer.py (missing win::rx_option fixture definition), test_texture_staging_publication.py (missing log_debug fixture declaration), and the known Nix-path test_linux_tools.py failure. No new failures. git diff --check dd02c6cc passes for the feature diff; main itself contains an unrelated font-license trailing space. No standalone formatter/typechecker is configured; the suite compiles native fixtures and byte-compiles tracked Python. The pinned source was hash-audited before and after a successful Linux build on aka. The new build loaded a copied Castle Town save (Market_Child, state play) in a private headless Sway/Xwayland session on the RX 7900 XT/RADV; its runtime version and live options were read through the control bridge. Audio was disabled, the GPU lock held throughout, and both private units stopped. No owner game, live save or korri* unit was touched. This was a short startup/gameplay smoke check, not a repeat of the earlier feature/performance measurements. Windows and other GPUs were not retested. The older test counts below describe the historical measured/follow-up heads, not this merge.

Tested: fresh Linux main/PR gameplay and feature proof above. All screenshots linked here were opened and checked. Only isolated copies and copied test saves were used; no installed game, live save, owner game session or korri* unit was changed. No GPU hang/device-lost/crash marker appeared in the completed measured runs.

Focused checks after the clean local rebase: 11 passed, 0 failed, 0 skipped in 1 s (linux, 8 at once). These include the feature test, tests touched by the change and tests/test_option_ids.py; the command and individual results are retained locally. git diff --check passes. There is no standalone formatter/typechecker in this project. Native fixtures compile through the test runner. Author and both contiguous AI trailers are preserved.

The earlier complete Linux suite at the measured commits is recorded in this PR's history. Its unresolved test_linux_tools.py failure also occurs on untouched main: that fixture fixes PATH to /usr/bin:/bin, which omits Nix's readlink/dirname. The separate earlier OpenSSL failure was resolved by adding OpenSSL to PATH; all 18 release tests then passed unchanged on main and each feature. The full suite was not repeated for this documentation/measurement pass; only the focused checks above were rerun.

Not tested: Windows native builds/gameplay and non-ASCII Windows paths, physical controllers, other GPU families, HDR, full playthroughs and every feature combination. No Windows machine is available. Any combination specifically requested below remains unverified; a request is not a passing result.

Needs other testers:

  • Windows, llvm-mingw, resizable display: Build main and the PR with llvm-mingw and a non-ASCII game path. Compare Monitor's shape and Window at 1920x1080, 2448x1848, 2560x1080 and 1280x1024, then resize live. Window must fill the drawable, preserve proportions, show Hor+/Vert+ and remain selected after an unrelated save and restart. Report commits, GPU, driver and captures. (T-ADMJ).
  • Linux or Windows, a build combining #142 and #95: Compare #142 alone with a build combining #142 and #95 at named commits. In Window mode at 16:9, tall, ultrawide and 5:4 shapes, load/teleport between areas and resize live. Expect the loading fade to cover the entire drawable without exposed edges, unchanged HUD projections and correct Hor+/Vert+ after loading; report screenshots and commits. (T-6SJP).

These requests are open; if the service marks them waiting for a maintainer, they need the testing wanted label before testers can claim them.

Screenshots

main @ 316f9319, 1920×1080, monitor shape

this PR @ 1c66d19b, Window, live resize 2448x1848

this PR @ 1c66d19b, Window, live resize 2560x1080

this PR @ 1c66d19b, Window, live resize 1280x1024

this PR @ 1c66d19b, Window, live resize 1920x1080

Risk and rollback

Tall windows render more pixels at startup and can run slower. Projection or UI cases not visited may render incorrectly. Select a monitor/explicit aspect and restart, or revert. Longshot #95 changes related HUD projection sites; combining it needs a separate integration check.

Reviewer notes

One feature only. The manual merge resolutions change documentation and retain both sets of output-resolution tests. Linux evidence does not certify Windows; that gap is assigned to open test requests rather than marked passed. No public MkwVkHost/MkwVkRenderer structure changed, so no ABI-version bump is needed. The shared tested-configurations file is unchanged; the measured runs are folded below. Authored glslang SPIR-V headers, where present, are source artifacts, not game-dump output.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main. Merged the main snapshot fetched for this run, dd02c6cca958 as 1adca03782bd, as requested by the author; latest main fdc2840b still merges cleanly. No rebase or force-push.
  • 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.
  • Screenshots for a change that shows on screen.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Linux host build verified; Windows build/runtime verification assigned to the open request above, not claimed passed.
  • A row in the folded tested-configurations table below for each measured run.
  • Flags noted, and the switch reference updated (README).
  • Docs updated.
Tested configurations: fresh Longshot runs, 2026-11-13 UTC
Run Commit/build Hardware and OS Settings and scene Result
1 main @ 316f9319 RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock Last five presents readings: 53.2, 53.4, 54.3, 54.0, 53.2; median 53.40 FPS. Same copied save, separate warm-up.
2 this PR @ 1c66d19b RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Aspect ratio Window Last five presents readings: 53.0, 53.6, 53.7, 53.4, 52.7; median 53.40 FPS. Same copied save, separate warm-up.
3 main @ 316f9319 RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock Last five presents readings: 53.5, 54.3, 54.7, 54.6, 54.0; median 54.30 FPS. Same copied save, separate warm-up.
4 this PR @ 1c66d19b RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Aspect ratio Window Last five presents readings: 53.0, 52.6, 52.8, 52.8, 52.5; median 52.80 FPS. Same copied save, separate warm-up.

Additional same-session feature/off-setting observations and their proof are described above.

## Why Monitor's shape follows the monitor, not a resized window. Window fills the actual window and keeps the 16:9 horizontal view on taller shapes, showing more above and below rather than cropping the sides. Also proposed on Hookshot: https://github.com/simonwjackson/Hookshot/tree/feat-window-aspect. ## What changed - Existing Aspect ratio row: adds Window, saved as `ultrawide=2`, using a stable -1 UI sentinel. Default monitor behavior stays unchanged. - Host: `hos_wide.c` / `.h`, Windows/SDL display startup and resize, `hos_main.c`, `rx_backend.c` and `nx2/oot_wide.py` keep the camera/culling matched to the actual window. Render targets keep their startup size. - UI: `oot_ui_squeeze.h`, `vk_draw.cpp`, `vk_internal.h` and `vk_main.cpp` preserve centred 16:9 UI proportions as the window resizes; no HUD docking/size controls. - Tests cover geometry, q4/q5 sentinels, FOV interaction, unknown→known monitor discovery, real saving and swapchain retries. README and launcher description retain the original changes. ## Flags and settings | Flag | What it does | Default now | Recommendation | Helps on | Doesn't help on | Untested on | |---|---|---|---|---|---|---| | Graphics → Aspect ratio → Window; `ultrawide=2` | Hor+ on wide windows, Vert+ on tall windows; restart to select, then live resizing | Monitor's shape (1), unchanged | Keep the current default; opt-in accessibility/presentation choice | Windows whose shape differs from the monitor | Reallocate render targets during resizing; no HUD anchoring controls | Windows gameplay, other GPUs, real handhelds, HDR, DLSS/FSR/FG, pause/inventory, movies, resized thumbnails | | `HOS_ASPECT` (existing override) | Keeps main’s explicit aspect override; disables Window mode while set | Unset | Keep unset for Window | Explicit-aspect diagnostic runs | Does not follow resizes | Same platform gaps as Window | ## Measurements main: 316f9319c87b48b8a077a52bb1673440cf071a1e (2026-11-13 UTC) this PR: feat/window-aspect @ 1c66d19babfb9151a307ebe2de240ed1eee314d2 Hardware: AMD RX 7900 XT 20 GB, RADV Mesa 26.2.3; Ryzen 5 7600X, 32 GB RAM; NixOS 26.11, Linux 7.2.7. Private headless Sway 1.12/Xwayland; udmabuf enabled; `RX_VK_FORCE_MESH_FALLBACK=1`; speakers disabled. Scene: Hyrule Castle Town (`Market_Child`), stationary at the copied save's wooden door. 1920×1080 drawable and output, 100% render resolution, game's own upscaler, frame generation Off, game frame-rate cap Uncapped, VSync Auto, HDR Off. Effective menu values were read through the control bridge. No overlays, injectors or external shader-cache tools. Method: One short session, main → PR → main → PR, with fresh copies of the same save. Each process warms at the stationary scene for at least 10 s, then samples for 20 s; the last five `vk: N presents, X fps` log lines per run are retained. The per-run median and median of the two run medians are below. These are native-present measurements at the settings below, not a claim about other scenes or 1% lows. How to reproduce: Load a disposable copy of a Castle Town save and stay by the same wooden door, without movement/camera input. Use the settings above, with Aspect ratio Window on this PR; baseline main has stock behavior. Alternate the two binaries twice, let each settle, and take the last five presents-log readings per run. The additional feature proof below is separate from these stationary FPS samples. | Observation | main @ 316f9319 | this PR @ 1c66d19b | |---|---|---| | Run medians, native FPS (first / second) | 53.40 / 54.30 | 53.40 / 52.80 | | Median of run medians, native FPS | 53.85 | 53.10 | | Samples retained per run | 5 / 5 | 5 / 5 | **Window proof:** both PR runs started with persisted `ultrawide=2`; the bridge reported the UI's stable `value=-1`, `text=Window`. In the last run the same live game was resized from 1920×1080 to 2448×1848, 2560×1080, 1280×1024, then back to 1920×1080. Inspected captures show the full window filled, wider side visibility in ultrawide, more vertical visibility in the tall/5:4 shapes, and preserved character/HUD proportions. This does not add HUD docking at the new edges. | Live drawable | Control-bridge frame | State | Aspect setting | |---|---:|---|---| | 1920×1080, initial | 24890 | play | Window | | 2448×1848 | 25271 | play | Window | | 2560×1080 | 25582 | play | Window | | 1280×1024 | 25881 | play | Window | | 1920×1080, restored | 26179 | play | Window | After the resizes, the bridge still reported `value=-1`, `text=Window`. Changing Sharpness to 10 and back to 0 kept `ultrawide=2` in the saved options file. There was no separate restart-after-that-save run; the game-free real-save regression covers that persistence path. The entire session took 389.8 s. No Monitor-setting FPS sample on the feature binary is claimed; the comparison here is stock main against the running Window mode. Windows and #95's loading-fade integration remain requested, not tested. <details> <summary>Exact presents-log samples (five per run)</summary> ```text Run 1: main [hos Presentation Thread] vk: 27120 presents, 53.2 fps; a frame: 1603 draws (0 skipped), 107 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 27240 presents, 53.4 fps; a frame: 1591 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 27360 presents, 54.3 fps; a frame: 1594 draws (0 skipped), 106 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 27480 presents, 54.0 fps; a frame: 1604 draws (0 skipped), 107 dispatches, 165 passes, 158 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1430 textures (3409 MB) [hos Presentation Thread] vk: 27600 presents, 53.2 fps; a frame: 1597 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (931 ms), 0 programs (10 failed), 1421 textures (3326 MB) Run 2: this PR [hos Presentation Thread] vk: 20880 presents, 53.0 fps; a frame: 1587 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 21000 presents, 53.6 fps; a frame: 1591 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1392 textures (3169 MB) [hos Presentation Thread] vk: 21120 presents, 53.7 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1432 textures (3539 MB) [hos Presentation Thread] vk: 21240 presents, 53.4 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1443 textures (3641 MB) [hos Presentation Thread] vk: 21360 presents, 52.7 fps; a frame: 1599 draws (0 skipped), 107 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (993 ms), 0 programs (10 failed), 1439 textures (3492 MB) Run 3: main [hos Presentation Thread] vk: 25440 presents, 53.5 fps; a frame: 1603 draws (0 skipped), 106 dispatches, 165 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1392 textures (3169 MB) [hos Presentation Thread] vk: 25560 presents, 54.3 fps; a frame: 1594 draws (0 skipped), 107 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1392 textures (3169 MB) [hos Presentation Thread] vk: 25680 presents, 54.7 fps; a frame: 1595 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1395 textures (3197 MB) [hos Presentation Thread] vk: 25800 presents, 54.6 fps; a frame: 1588 draws (0 skipped), 106 dispatches, 164 passes, 158 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1420 textures (3428 MB) [hos Presentation Thread] vk: 25920 presents, 54.0 fps; a frame: 1594 draws (0 skipped), 108 dispatches, 165 passes, 158 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (913 ms), 0 programs (10 failed), 1431 textures (3418 MB) Run 4: this PR [hos Presentation Thread] vk: 24360 presents, 53.0 fps; a frame: 1591 draws (0 skipped), 107 dispatches, 165 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 24480 presents, 52.6 fps; a frame: 1595 draws (0 skipped), 106 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1393 textures (3178 MB) [hos Presentation Thread] vk: 24600 presents, 52.8 fps; a frame: 1592 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1414 textures (3373 MB) [hos Presentation Thread] vk: 24720 presents, 52.8 fps; a frame: 1590 draws (0 skipped), 106 dispatches, 165 passes, 159 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1432 textures (3427 MB) [hos Presentation Thread] vk: 24840 presents, 52.5 fps; a frame: 1603 draws (0 skipped), 107 dispatches, 164 passes, 159 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 25369 pipelines (952 ms), 0 programs (10 failed), 1429 textures (3400 MB) ``` </details> The binaries are the previously built Linux release copies at these exact commits, with all tracked source hashes rechecked against their Git trees before this session. Native shader preparation ran for each copy before measurement. No newer or unbuilt binary is substituted. **Current-main merge:** current head `1adca03782bdb6ae7a526fa184bc44800f1b2e02` merges the Longshot main snapshot fetched for this run, `dd02c6cca9582f5bb1b992d5c43268bf5d846c3f`, into the existing PR branch with a merge commit and a normal push; no history rewrite. The earlier measurements and screenshots remain evidence only at their named commits, not a new performance comparison on this main. The merge was rebuilt and smoke-tested as described below. Main advanced to `fdc2840b` during validation; a three-way merge check against that newer head is clean, but it is not included in the tested binary. ## Testing **Current merge validation (`1adca03782bd`, 2026-11-13):** README keeps main’s current rows with the Window choice; test_output_resolution.py retains main’s new default-output cases and the Window/save cases. Focused feature/option-ID/README/menu checks: **11 passed, 0 failed**. Full suite on zao, with `/tmp/longshot-carry/env.sh` and the shared suite lock: **506 passed, 3 failed, 16 skipped in 508 s (linux, 8 at once)**. All three failures reproduce on untouched main `dd02c6cc`: `test_display_pointer.py` (missing `win::rx_option` fixture definition), `test_texture_staging_publication.py` (missing `log_debug` fixture declaration), and the known Nix-path `test_linux_tools.py` failure. No new failures. `git diff --check dd02c6cc` passes for the feature diff; main itself contains an unrelated font-license trailing space. No standalone formatter/typechecker is configured; the suite compiles native fixtures and byte-compiles tracked Python. The pinned source was hash-audited before and after a successful Linux build on aka. The new build loaded a copied Castle Town save (`Market_Child`, state `play`) in a private headless Sway/Xwayland session on the RX 7900 XT/RADV; its runtime version and live options were read through the control bridge. Audio was disabled, the GPU lock held throughout, and both private units stopped. No owner game, live save or `korri*` unit was touched. This was a short startup/gameplay smoke check, not a repeat of the earlier feature/performance measurements. Windows and other GPUs were not retested. The older test counts below describe the historical measured/follow-up heads, not this merge. **Tested:** fresh Linux main/PR gameplay and feature proof above. All screenshots linked here were opened and checked. Only isolated copies and copied test saves were used; no installed game, live save, owner game session or `korri*` unit was changed. No GPU hang/device-lost/crash marker appeared in the completed measured runs. Focused checks after the clean local rebase: `11 passed, 0 failed, 0 skipped in 1 s (linux, 8 at once)`. These include the feature test, tests touched by the change and `tests/test_option_ids.py`; the command and individual results are retained locally. `git diff --check` passes. There is no standalone formatter/typechecker in this project. Native fixtures compile through the test runner. Author and both contiguous AI trailers are preserved. The earlier complete Linux suite at the measured commits is recorded in this PR's history. Its unresolved `test_linux_tools.py` failure also occurs on untouched main: that fixture fixes PATH to `/usr/bin:/bin`, which omits Nix's readlink/dirname. The separate earlier OpenSSL failure was resolved by adding OpenSSL to PATH; all 18 release tests then passed unchanged on main and each feature. The full suite was not repeated for this documentation/measurement pass; only the focused checks above were rerun. **Not tested:** Windows native builds/gameplay and non-ASCII Windows paths, physical controllers, other GPU families, HDR, full playthroughs and every feature combination. No Windows machine is available. Any combination specifically requested below remains unverified; a request is not a passing result. **Needs other testers:** - Windows, llvm-mingw, resizable display: Build main and the PR with llvm-mingw and a non-ASCII game path. Compare Monitor's shape and Window at 1920x1080, 2448x1848, 2560x1080 and 1280x1024, then resize live. Window must fill the drawable, preserve proportions, show Hor+/Vert+ and remain selected after an unrelated save and restart. Report commits, GPU, driver and captures. (T-ADMJ). - Linux or Windows, a build combining #142 and #95: Compare #142 alone with a build combining #142 and #95 at named commits. In Window mode at 16:9, tall, ultrawide and 5:4 shapes, load/teleport between areas and resize live. Expect the loading fade to cover the entire drawable without exposed edges, unchanged HUD projections and correct Hor+/Vert+ after loading; report screenshots and commits. (T-6SJP). These requests are open; if the service marks them waiting for a maintainer, they need the `testing wanted` label before testers can claim them. ## Screenshots ![main @ 316f9319, 1920×1080, monitor shape](https://git.magicbean.dev/simonwjackson/Longshot/raw/commit/02f8cb125c248eb407bd4ac487a538b61b6b06a5/window-1-steady.png) ![this PR @ 1c66d19b, Window, live resize 2448x1848](https://git.magicbean.dev/simonwjackson/Longshot/raw/commit/02f8cb125c248eb407bd4ac487a538b61b6b06a5/window-4-live-resize-2448x1848.png) ![this PR @ 1c66d19b, Window, live resize 2560x1080](https://git.magicbean.dev/simonwjackson/Longshot/raw/commit/02f8cb125c248eb407bd4ac487a538b61b6b06a5/window-4-live-resize-2560x1080.png) ![this PR @ 1c66d19b, Window, live resize 1280x1024](https://git.magicbean.dev/simonwjackson/Longshot/raw/commit/02f8cb125c248eb407bd4ac487a538b61b6b06a5/window-4-live-resize-1280x1024.png) ![this PR @ 1c66d19b, Window, live resize 1920x1080](https://git.magicbean.dev/simonwjackson/Longshot/raw/commit/02f8cb125c248eb407bd4ac487a538b61b6b06a5/window-4-live-resize-1920x1080.png) ## Risk and rollback Tall windows render more pixels at startup and can run slower. Projection or UI cases not visited may render incorrectly. Select a monitor/explicit aspect and restart, or revert. Longshot #95 changes related HUD projection sites; combining it needs a separate integration check. ## Reviewer notes One feature only. The manual merge resolutions change documentation and retain both sets of output-resolution tests. Linux evidence does not certify Windows; that gap is assigned to open test requests rather than marked passed. No public MkwVkHost/MkwVkRenderer structure changed, so no ABI-version bump is needed. The shared tested-configurations file is unchanged; the measured runs are folded below. Authored glslang SPIR-V headers, where present, are source artifacts, not game-dump output. ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. Merged the main snapshot fetched for this run, `dd02c6cca958` as `1adca03782bd`, as requested by the author; latest main `fdc2840b` still merges cleanly. No rebase or force-push. - [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] Screenshots for a change that shows on screen. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Linux host build verified; Windows build/runtime verification assigned to the open request above, not claimed passed. - [x] A row in the folded tested-configurations table below for each measured run. - [x] Flags noted, and the switch reference updated (README). - [x] Docs updated. <details> <summary>Tested configurations: fresh Longshot runs, 2026-11-13 UTC</summary> | Run | Commit/build | Hardware and OS | Settings and scene | Result | |---|---|---|---|---| | 1 | main @ `316f9319` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock | Last five presents readings: 53.2, 53.4, 54.3, 54.0, 53.2; median 53.40 FPS. Same copied save, separate warm-up. | | 2 | this PR @ `1c66d19b` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Aspect ratio Window | Last five presents readings: 53.0, 53.6, 53.7, 53.4, 52.7; median 53.40 FPS. Same copied save, separate warm-up. | | 3 | main @ `316f9319` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; stock | Last five presents readings: 53.5, 54.3, 54.7, 54.6, 54.0; median 54.30 FPS. Same copied save, separate warm-up. | | 4 | this PR @ `1c66d19b` | RX 7900 XT, RADV 26.2.3; Ryzen 7600X, 32 GB; NixOS 26.11 / Linux 7.2.7 | Castle Town wooden door; 1920×1080, 100% render resolution, FG/HDR Off, Uncapped, VSync Auto; Aspect ratio Window | Last five presents readings: 53.0, 52.6, 52.8, 52.8, 52.5; median 52.80 FPS. Same copied save, separate warm-up. | Additional same-session feature/off-setting observations and their proof are described above. </details>
feat(video): a Window choice that fills the actual window, Hor+ and Vert+
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
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
1c66d19bab
Also proposed on Hookshot: simonwjackson/Hookshot feat-window-aspect.
Cherry-picked source: e7481e2, 4813a27, 92b3eb5, a71778d, d1d9cdc.
Resolve rename/context conflicts without changing feature behaviour.
Keep measured configurations in the PR description, not the shared table.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Author
Contributor

Added a fresh interleaved main/PR Linux measurement session at the named commits, the last-five-sample FPS medians, feature proof and inspected screenshots. Windows testing requests are open; the description names the remaining gaps. The branch also rebases cleanly onto main 1be180bb with an identical stable patch ID and passing focused tests; the force-with-lease push is still gated, so the measured remote head stays pinned. No new baseline FPS is claimed for the local rebase. The four drawable shapes and live resize were checked; #95 loading-fade integration has its own open request.

Added a fresh interleaved main/PR Linux measurement session at the named commits, the last-five-sample FPS medians, feature proof and inspected screenshots. Windows testing requests are open; the description names the remaining gaps. The branch also rebases cleanly onto main 1be180bb with an identical stable patch ID and passing focused tests; the force-with-lease push is still gated, so the measured remote head stays pinned. No new baseline FPS is claimed for the local rebase. The four drawable shapes and live resize were checked; #95 loading-fade integration has its own open request.
simonwjackson changed title from WIP: feat(video): a Window choice that fills the actual window, Hor+ and Vert+ to feat(video): a Window choice that fills the actual window, Hor+ and Vert+ 2026-11-13 07:49:58 +00:00
Merge current Longshot main into feat/window-aspect
Some checks are pending
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 / 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
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 7s
1adca03782
Keep current menu documentation with the Window aspect choice, and retain both default-output and Window cases in the output-resolution test.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Author
Contributor

Merged main snapshot dd02c6cca9 with a merge commit and normal push; resolved README using main’s current rows plus Window, and retained both main’s new default-output cases and our Window/save cases in test_output_resolution.py. Eleven focused checks pass; full suite: 506 passed, 3 failures reproduced on untouched main, 16 skipped; Linux build and isolated Castle Town smoke pass, and newer main fdc2840b still merges cleanly (details in Testing).

Merged main snapshot dd02c6cca9582f5bb1b992d5c43268bf5d846c3f with a merge commit and normal push; resolved README using main’s current rows plus Window, and retained both main’s new default-output cases and our Window/save cases in test_output_resolution.py. Eleven focused checks pass; full suite: 506 passed, 3 failures reproduced on untouched main, 16 skipped; Linux build and isolated Castle Town smoke pass, and newer main fdc2840b still merges cleanly (details in Testing).
HumanBean added this to the v3.0.0 milestone 2026-11-13 17:53:56 +00:00
HumanBean left a comment

Thanks. This is careful work. The Hor+/Vert+ projection is correct: the tangent is multiplied by 16:9 over the window's shape only when the window is narrower, so the horizontal view stays the 16:9 one. Keeping each projection's original tangent stops the FOV growing on repeated updates, and that also holds with the FOV slider's hook (the new block in tests/test_oot_fov.py). A minimised window (0x0) is ignored, both in rx_resize and in hos_wide_resize. A live resize changes no render target, so no upscaler history or guest heap has to be rebuilt. The swapchain is still remade the way main does it (swap_resize folds a burst of resizes into one remake at the next present). The default doesn't change: ultrawide=0/1 and a missing line read and save as before, and hos_wide_window is set only by ultrawide=2. HOS_ASPECT still overrides. No MkwVkHost/MkwVkRenderer change, third_party/ untouched, no generated files.

I read the code only. I didn't build it or run any checks.

Must change

  1. It doesn't merge with current main. Main is now at fbd42024. README.md conflicts with the bullet that #55 added in the same place (f647a072, "Changing the Upscaler keeps your chosen render scale..."). Keep both bullets. Every other file merges cleanly (I checked with a three-way merge). The "Rebased on current main" box is false until then.

  2. After a live resize, movies, the menus' sides and the save picture still use the startup shape. hos_wide_resize updates only the camera/HUD aspect. hos_wide_margin is set only in hos_wide_start, and two things still read it:

    • the MOVIE site, so pre-rendered movies are no longer in the middle 16:9;
    • host_wide_menus → vk_framegen.cpp wide_sides(), which places the blurred or black sides of the pause and map menus.

    The THUMB site likewise reads the startup hos_wide_ratio. Two examples:

    • Start full screen on a 21:9 screen, then go back to a 16:9 window: the menus are drawn across the full 16:9, but the blur/bars still cover about 12% on each side.
    • Start at 1280x720 and widen to 21:9: movies are stretched and the menus get no side treatment.

    Recompute hos_wide_squeeze/hos_wide_margin from the live aspect in hos_wide_resize. Give THUMB a live ratio of its own. hos_wide_ratio has to stay at the startup value, because the DLSS jitter (rx_backend.c dlss_jitter_clip) needs the shape the render targets actually have. These are the paths the description lists as untested; "Follows window resizes" isn't true for them yet.

  3. The checklist doesn't match the template.

    • The template's "Host code builds on both Windows (llvm-mingw) and Linux" box was reworded and ticked. No Windows build was made, so it should stay unticked, with the reason, until someone builds it. T-ADMJ can cover this.
    • "A row in docs/tested-configurations.md" was changed to "the folded table below". Add the rows to the file, as AGENTS.md asks.
  4. The screenshots return 404. git.magicbean.dev/simonwjackson/Longshot/raw/commit/02f8cb12.../*.png doesn't resolve: the fork or that commit isn't reachable. Attach them to the PR so a reviewer can see Hor+/Vert+ and the centred HUD. If you can, add a main @ hash shot of the same spot at one of the resized sizes.

Should change

  • Clamp the shape.
    • hos_wide_picture_size takes any aspect: main's hos_wide_clamp (5:4 to 48:9) isn't applied. Its pixel cap can also break the "do not shrink below 1920x1080" comment. Below about 1:3, the width drops under 1920 (540x2160 → 1656x6648). Above about 10:1, the height drops under 1080, and from about 28:1 the width goes past 16384. You can only get there at startup with HOS_WINDOW_SIZE or full screen.
    • The live aspect isn't clamped at all. A 1600x40 or 40x1600 window gives a view of about 175° across or up and down. With the 300% FOV of #199 it goes further still.
    • Clamp both, the startup shape and the live one, to a range you've checked.
  • The picture keeps the window's startup shape, and that is usually 16:9. In Window mode the window opens at 1280x720 unless it starts full screen or HOS_WINDOW_SIZE is set. So a player who picks Window and maximises on a 21:9 screen gets a 2560x1440 picture stretched across 3440, which is softer than Monitor's shape. Say so in the hint and the README ("the picture's size is set by the window's shape at start"), or open the window at the size it last had.
    • On SDL, is hos_sdl_pixel_size right after set_fullscreen already the full-screen size? On X11/Wayland, going full screen can be asynchronous. I haven't checked this.
  • window_fit_active() calls getenv("HOS_ASPECT") on every draw and every 0x90 uniform block in Window mode. Read it once.
  • Clean up the hos_wide_projection table.
    • It only grows: one entry per projection address ever seen, kept sorted with a memmove.
    • A recycled address keeps valid. In a window whose aspect equals the stored patched_aspect (for example a square window), a new square shadow view at that address is then taken for a patched camera.
    • Drop an entry once its aspect is neither 16:9 nor the patched value, and don't take that path for a view with no entry.
  • Window + HOS_HEADLESS: the picture keeps the monitor's shape from hos_main.c, but hos_wide_aspect() is 0, so the cameras stay at 16:9 and the picture is stretched. The live _ratio doesn't fall back to _ratio_fixed. This is a minor dev-only case.

The other layout work

  • #95 rewrites the same HUD string in nx2/oot_wide.py, so they will conflict as text, and whichever lands second has to port. As written, Window mode replaces #95's HUD outright:

    • #95's hos_ui_qx/qy and edge-anchored panes don't apply in Window mode;
    • #95's hos_wide_ui_tick reads the startup hos_wide_squeeze/hos_wide_ratio, which a live resize doesn't change.

    Please agree with #95's author which one owns the HUD projection. I'd suggest #95's layout taking hos_wide_aspect() as its shape, with the draw-time refit (oot_ui_squeeze.h) staying as the live correction.

  • #57 only changes which keep-mask bits the menus clear. Window's HUD branch still honours hos_wide_keep_mask, so the two are compatible. Its fades still depend on the side margin from point 2 above.

  • #199 and this PR edit different parts of tests/test_oot_fov.py.

  • The Output resolution row: in Window mode only the chosen size's height is used, and its shape is ignored. That matches picture_for; the hint could say so.

Nits

  • The measurement compares main with this PR in Window mode. This PR's own Monitor's-shape path, which every player runs, wasn't measured. On a 16:9 window the run medians are 53.85 vs 53.10. Since hos_display_start now waits for the window for everyone, one interleaved pair on the default would settle it.
  • aspect_started starts at -1, which is now Window's value until outputs_sync runs.
Thanks. This is careful work. The Hor+/Vert+ projection is correct: the tangent is multiplied by 16:9 over the window's shape only when the window is narrower, so the horizontal view stays the 16:9 one. Keeping each projection's original tangent stops the FOV growing on repeated updates, and that also holds with the FOV slider's hook (the new block in `tests/test_oot_fov.py`). A minimised window (0x0) is ignored, both in `rx_resize` and in `hos_wide_resize`. A live resize changes no render target, so no upscaler history or guest heap has to be rebuilt. The swapchain is still remade the way main does it (`swap_resize` folds a burst of resizes into one remake at the next present). The default doesn't change: `ultrawide=0/1` and a missing line read and save as before, and `hos_wide_window` is set only by `ultrawide=2`. `HOS_ASPECT` still overrides. No `MkwVkHost`/`MkwVkRenderer` change, `third_party/` untouched, no generated files. I read the code only. I didn't build it or run any checks. ## Must change 1. **It doesn't merge with current main.** Main is now at `fbd42024`. `README.md` conflicts with the bullet that https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/55 added in the same place (f647a072, "Changing the Upscaler keeps your chosen render scale..."). Keep both bullets. Every other file merges cleanly (I checked with a three-way merge). The "Rebased on current `main`" box is false until then. 2. **After a live resize, movies, the menus' sides and the save picture still use the startup shape.** `hos_wide_resize` updates only the camera/HUD aspect. `hos_wide_margin` is set only in `hos_wide_start`, and two things still read it: - the MOVIE site, so pre-rendered movies are no longer in the middle 16:9; - `host_wide_menus` → `vk_framegen.cpp wide_sides()`, which places the blurred or black sides of the pause and map menus. The THUMB site likewise reads the startup `hos_wide_ratio`. Two examples: - Start full screen on a 21:9 screen, then go back to a 16:9 window: the menus are drawn across the full 16:9, but the blur/bars still cover about 12% on each side. - Start at 1280x720 and widen to 21:9: movies are stretched and the menus get no side treatment. Recompute `hos_wide_squeeze`/`hos_wide_margin` from the live aspect in `hos_wide_resize`. Give THUMB a live ratio of its own. `hos_wide_ratio` has to stay at the startup value, because the DLSS jitter (`rx_backend.c dlss_jitter_clip`) needs the shape the render targets actually have. These are the paths the description lists as untested; "Follows window resizes" isn't true for them yet. 3. **The checklist doesn't match the template.** - The template's "Host code builds on both Windows (llvm-mingw) and Linux" box was reworded and ticked. No Windows build was made, so it should stay unticked, with the reason, until someone builds it. T-ADMJ can cover this. - "A row in `docs/tested-configurations.md`" was changed to "the folded table below". Add the rows to the file, as AGENTS.md asks. 4. **The screenshots return 404.** `git.magicbean.dev/simonwjackson/Longshot/raw/commit/02f8cb12.../*.png` doesn't resolve: the fork or that commit isn't reachable. Attach them to the PR so a reviewer can see Hor+/Vert+ and the centred HUD. If you can, add a main @ hash shot of the same spot at one of the resized sizes. ## Should change - **Clamp the shape.** - `hos_wide_picture_size` takes any aspect: main's `hos_wide_clamp` (5:4 to 48:9) isn't applied. Its pixel cap can also break the "do not shrink below 1920x1080" comment. Below about 1:3, the width drops under 1920 (540x2160 → 1656x6648). Above about 10:1, the height drops under 1080, and from about 28:1 the width goes past 16384. You can only get there at startup with `HOS_WINDOW_SIZE` or full screen. - The live aspect isn't clamped at all. A 1600x40 or 40x1600 window gives a view of about 175° across or up and down. With the 300% FOV of https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/199 it goes further still. - Clamp both, the startup shape and the live one, to a range you've checked. - **The picture keeps the window's startup shape, and that is usually 16:9.** In Window mode the window opens at 1280x720 unless it starts full screen or `HOS_WINDOW_SIZE` is set. So a player who picks Window and maximises on a 21:9 screen gets a 2560x1440 picture stretched across 3440, which is softer than Monitor's shape. Say so in the hint and the README ("the picture's size is set by the window's shape at start"), or open the window at the size it last had. - On SDL, is `hos_sdl_pixel_size` right after `set_fullscreen` already the full-screen size? On X11/Wayland, going full screen can be asynchronous. I haven't checked this. - **`window_fit_active()` calls `getenv("HOS_ASPECT")` on every draw and every 0x90 uniform block in Window mode.** Read it once. - **Clean up the `hos_wide_projection` table.** - It only grows: one entry per projection address ever seen, kept sorted with a `memmove`. - A recycled address keeps `valid`. In a window whose aspect equals the stored `patched_aspect` (for example a square window), a new square shadow view at that address is then taken for a patched camera. - Drop an entry once its aspect is neither 16:9 nor the patched value, and don't take that path for a view with no entry. - **Window + `HOS_HEADLESS`:** the picture keeps the monitor's shape from `hos_main.c`, but `hos_wide_aspect()` is 0, so the cameras stay at 16:9 and the picture is stretched. The live `_ratio` doesn't fall back to `_ratio_fixed`. This is a minor dev-only case. ## The other layout work - **https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/95** rewrites the same `HUD` string in `nx2/oot_wide.py`, so they will conflict as text, and whichever lands second has to port. As written, Window mode replaces #95's HUD outright: - #95's `hos_ui_qx/qy` and edge-anchored panes don't apply in Window mode; - #95's `hos_wide_ui_tick` reads the startup `hos_wide_squeeze`/`hos_wide_ratio`, which a live resize doesn't change. Please agree with #95's author which one owns the HUD projection. I'd suggest #95's layout taking `hos_wide_aspect()` as its shape, with the draw-time refit (`oot_ui_squeeze.h`) staying as the live correction. - **https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/57** only changes which keep-mask bits the menus clear. Window's HUD branch still honours `hos_wide_keep_mask`, so the two are compatible. Its fades still depend on the side margin from point 2 above. - **https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/199** and this PR edit different parts of `tests/test_oot_fov.py`. - **The Output resolution row:** in Window mode only the chosen size's height is used, and its shape is ignored. That matches `picture_for`; the hint could say so. ## Nits - The measurement compares main with this PR in Window mode. This PR's own Monitor's-shape path, which every player runs, wasn't measured. On a 16:9 window the run medians are 53.85 vs 53.10. Since `hos_display_start` now waits for the window for everyone, one interleaved pair on the default would settle it. - `aspect_started` starts at -1, which is now Window's value until `outputs_sync` runs.
@ -497,6 +497,12 @@ HDR, Detail, and GPU and memory (last: the rows to change when something is wron
and **Sharpness** sharpens it, by the picture's pixels: when the window is much larger than the picture (the Handheld
Profile's 1536x864 on a 4K screen), a Sharpness kept from an older version is stronger than it was, so a lower one may
suit.
- **Window aspect ratio** (opt-in): fills the actual window. Wider than 16:9 shows more at the sides (Hor+);
Owner

This conflicts with current main (fbd42024): #55 added the 'Changing the Upscaler keeps your chosen render scale' bullet in the same place. Keep both.

This conflicts with current main (`fbd42024`): https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/55 added the 'Changing the Upscaler keeps your chosen render scale' bullet in the same place. Keep both.
@ -3989,6 +4006,8 @@ void draw_startup(ui::Ctx& c) {
} // namespace
bool window_fit_active() { return ultrawide_read == 2 && !getenv("HOS_ASPECT"); }
Owner

This is called per draw (draw()) and per 0x90 uniform block (uniform_range) in Window mode, so it calls getenv on the hot path. Cache HOS_ASPECT once.

This is called per draw (`draw()`) and per 0x90 uniform block (`uniform_range`) in Window mode, so it calls `getenv` on the hot path. Cache `HOS_ASPECT` once.
@ -0,0 +16,4 @@
float hos_wide_aspect(void) { return hos_wide_window ? atomic_load_explicit(&window_aspect, memory_order_relaxed) : hos_aspect_ratio; }
void hos_wide_resize(unsigned width, unsigned height) {
Owner

Only the camera/HUD aspect follows a resize here. hos_wide_margin/hos_wide_squeeze, set once in hos_wide_start, still drive the MOVIE site and the menus' blurred/black sides (host_wide_menus -> vk_framegen.cpp wide_sides), and THUMB reads the startup hos_wide_ratio. After a resize, movies and menu sides keep the startup shape. Recompute squeeze/margin here from the live aspect, and give THUMB its own live ratio. Keep hos_wide_ratio as it is for the DLSS jitter, since the targets keep their startup size. The live aspect is also unclamped: a 1600x40 or 40x1600 window gives a view of about 175 degrees.

Only the camera/HUD aspect follows a resize here. `hos_wide_margin`/`hos_wide_squeeze`, set once in `hos_wide_start`, still drive the MOVIE site and the menus' blurred/black sides (`host_wide_menus` -> `vk_framegen.cpp wide_sides`), and THUMB reads the startup `hos_wide_ratio`. After a resize, movies and menu sides keep the startup shape. Recompute squeeze/margin here from the live aspect, and give THUMB its own live ratio. Keep `hos_wide_ratio` as it is for the DLSS jitter, since the targets keep their startup size. The live aspect is also unclamped: a 1600x40 or 40x1600 window gives a view of about 175 degrees.
@ -0,0 +21,4 @@
if (aspect < 16.0 / 9.0) oh = (unsigned)(ow / aspect / 8 + 0.5) * 8;
else ow = (unsigned)(oh * aspect / 8 + 0.5) * 8;
/* Upstream's render-target pool guard (issue 1031): cap pixels, not shape. */
if ((double)ow * oh > hos_wide_max_px()) {
Owner

There's no hos_wide_clamp here, unlike main's path, and this cap can break the comment above. Below about 1:3 the width drops under 1920 (540x2160 -> 1656x6648). Above about 10:1 the height drops under 1080, and from about 28:1 the width passes 16384. Clamp the aspect first (main's 5:4 to 48:9, or a portrait floor you've checked).

There's no `hos_wide_clamp` here, unlike main's path, and this cap can break the comment above. Below about 1:3 the width drops under 1920 (540x2160 -> 1656x6648). Above about 10:1 the height drops under 1080, and from about 28:1 the width passes 16384. Clamp the aspect first (main's 5:4 to 48:9, or a portrait floor you've checked).
@ -300,1 +312,4 @@
if (getenv("HOS_FULLSCREEN") && atoi(getenv("HOS_FULLSCREEN"))) set_fullscreen(1);
int pixel_w = 0, pixel_h = 0;
hos_sdl_pixel_size(window, &pixel_w, &pixel_h);
hos_wide_start(pixel_w, pixel_h);
Owner

Is the pixel size already the full-screen size right after set_fullscreen(1)? On X11/Wayland the switch can be asynchronous, and the picture is sized only from this value. Otherwise, a windowed start opens at 1280x720 in Window mode, so the picture is nearly always 16:9 and a maximised ultrawide window stretches 2560 pixels across 3440. Worth saying in the hint/README.

Is the pixel size already the full-screen size right after `set_fullscreen(1)`? On X11/Wayland the switch can be asynchronous, and the picture is sized only from this value. Otherwise, a windowed start opens at 1280x720 in Window mode, so the picture is nearly always 16:9 and a maximised ultrawide window stretches 2560 pixels across 3440. Worth saying in the hint/README.
@ -51,1 +87,4 @@
HUD = "{ extern int hos_wide_window; if (hos_wide_window) " + HUD + " else " + HUD_FIXED + " }"
MOVIE = ("{ extern volatile float hos_wide_margin; const float _m = hos_wide_margin; if (_m > 0.0f) { "
Owner

MOVIE still reads hos_wide_margin, which is only set at startup in Window mode, so movies leave the middle 16:9 after a live resize (see host/hos_wide.c).

MOVIE still reads `hos_wide_margin`, which is only set at startup in Window mode, so movies leave the middle 16:9 after a live resize (see host/hos_wide.c).
@ -48,6 +72,21 @@ HUD = ("{ extern volatile float hos_wide_squeeze; const float _q = hos_wide_sque
"memcpy(c->vreg[1], _row, 16); } } } } /* (nx2/oot_wide.py: HUD in the middle 16:9) */"
% (UI_M00, UI_M11, " : ".join("_vt == 0x%xULL ? %du" % (v, 1 << k) for k, v in enumerate(KEEP_VTABLES)) + " : 0u"))
HUD = ("{ extern float hos_wide_aspect(void); const float _w = hos_wide_aspect(); if (_w > 0.0f) { float _m00, _m11; "
Owner

#95 rewrites this same HUD string (hos_ui_qx/qy, edge-anchored panes). In Window mode this branch replaces it outright, and #95's hos_wide_ui_tick reads the startup squeeze/ratio. Please agree with #95 which one owns the HUD projection before either lands.

https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/95 rewrites this same HUD string (`hos_ui_qx/qy`, edge-anchored panes). In Window mode this branch replaces it outright, and #95's `hos_wide_ui_tick` reads the startup squeeze/ratio. Please agree with #95 which one owns the HUD projection before either lands.
Some checks are pending
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 / 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
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 7s
This pull request has changes conflicting with the target branch.
  • README.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/window-aspect:simonwjackson-feat/window-aspect'
git switch 'simonwjackson-feat/window-aspect'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 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!142
No description provided.