Skip to content

Show the middle 16:9 in the save picture on ultrawide - #1183

Merged
hckrman101 merged 3 commits into
NotRexed:mainfrom
janvitos:fix/ultrawide-save-picture
Nov 9, 2026
Merged

hckrman101 merged 3 commits into
NotRexed:mainfrom
janvitos:fix/ultrawide-save-picture

Conversation

@janvitos

@janvitos janvitos commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #952.

What

The pause menu draws the whole picture into the save's 543×306 JPEG through the game's viewport helper. When the window is wider than 16:9, the whole wide picture was squeezed into that texture.

nx2/oot_wide.py gets one more site, main+0x284824. That is the helper's SetViewport call, right after its address is loaded and before the blr. At that site the viewport is widened by the picture's ratio over 16:9 and centred, so the middle 16:9 fills the texture, the same view a 16:9 window would save. The scissor still clips to the texture.

  • Only one viewport changes: a 0, 0, 543, 306 viewport, and only past 16:9.
  • 16:9 is untouched: there hos_wide_squeeze is 0, so the game runs as shipped.
  • Other game versions: the site's instruction word is checked like the others, so a different version leaves the site out.

How it was tested

  • Machine: Linux (CachyOS), RTX 4070 SUPER, NVIDIA 620.70.06.
  • Build: a full game build with this change on 8b2e1d3.
  • Ultrawide: at 21:9 (3440×1440, HOS_ASPECT=2.3889), saving from the pause menu now gives a picture that matches a 16:9 save from the same spot. Before, it was the whole 21:9 picture squeezed into 16:9.
  • 16:9: saves are unchanged.
  • New test: tests/test_oot_wide_save_picture.py compiles the site's C exactly as the lifter places it. It checks 21:9, 32:9 and 2560×1080 against the expected width (within 1 px) and centring. It also checks that a 16:9 window and every other viewport stay as the game set them.
  • Suite: on this branch (rebased on 1f6fbdb), python -I -B tests/run_all.py gives 132 passed, 0 failed, 17 skipped on Linux.

🤖 Generated with Claude Code

…otRexed#952)

The pause menu draws the whole picture into the save's 543x306 JPEG through the game's viewport helper. Wider than
16:9, the whole wide picture was squeezed into that texture. nx2/oot_wide.py now widens that one SetViewport (main+
0x284824, before its blr) by the picture's ratio over 16:9, centred, so the middle 16:9 fills the texture as a 16:9
window would show it. Only a 0,0,543,306 viewport is changed, and only past 16:9 (hos_wide_squeeze is 0 otherwise).

tests/test_oot_wide_save_picture.py compiles the site's C and checks 21:9, 32:9 and 2560x1080 against the expected
width and centring, and that 16:9 and every other viewport are left as the game set them.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…all.py (review of NotRexed#1183)

The test defaulted to `cc` and had no ARGS entry, so run_all.py --strict
would call `cc` on the windows-2022 job, where there normally is none.
It is now listed with ["--cc", "gcc"] like the other C fixture tests and
defaults to gcc itself, so the runner skips it (or fails it under
--strict) when gcc is missing. Also rewraps a long docstring line in
nx2/oot_wide.py.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@janvitos

janvitos commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Pushed in ef362de:

  • tests/run_all.py now lists tests/test_oot_wide_save_picture.py with ["--cc", "gcc"], and the test's own default is gcc, like test_text_speed.py. Without gcc on PATH the runner now skips it ("not found here: gcc"), or fails it under --strict.
  • Rewrapped the long docstring line in nx2/oot_wide.py.

…upstream's new one (NotRexed#1183)

Upstream main added tests/test_motion.py right after
test_audio_voice_biquad.py in run_all.py's ARGS, where this branch had
put its entry, and the PR stopped merging. The entry now follows
test_input_background.py, a place no other change touches.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@janvitos

janvitos commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

Moved the run_all.py entry in 5a03e8b: upstream's new test_motion.py line landed where it was, so this PR had a conflict. It merges cleanly with main again.

@hckrman101
hckrman101 merged commit ac3bd44 into NotRexed:main Nov 9, 2026
8 of 10 checks passed
nesrodzair pushed a commit to nesrodzair/Hookshot that referenced this pull request Nov 9, 2026
…ddle band, comments

From the review of NotRexed#1224:
- host/display.c and host/posix/display_sdl.c open the default window at
  the picture's shape whenever Monitor's shape set one (960 x 720 for
  4:3): a 1280 x 720 window stretched the 4:3 picture 33% too wide on the
  Vulkan path (preserve_stretch) until fullscreen.
- nx2/oot_wide.py's save picture (NotRexed#1183) reads hos_wide_ratio: past 16:9
  its viewport is widened as before, narrower than 16:9 it is made taller
  so the picture's middle band fills the 543 x 306 texture at its own
  shape. tests/test_oot_wide_save_picture.py covers 4:3, 16:10 and 5:4.
- hos_main.c's block comment, oot_wide.py's docstring and the menu hint
  describe narrower screens too.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKvkjzHAcf259i979SBbeS
github-actions Bot pushed a commit to notquiteog/Hookshot that referenced this pull request Nov 9, 2026
## [2.0.12](v2.0.11...v2.0.12) (2026-11-09)

### Features

* a timed span per FSR pass, so the cost has a number per pass ([1d7f262](1d7f262))
* add a Quit option to the gameplay menu ([0b02149](0b02149))
* **audio:** Audio output in the Insert menu: Auto, Stereo or 5.1 ([4856258](4856258)), closes [NotRexed#1182](https://github.com/notquiteog/Hookshot/issues/1182)
* census the render target sets, to settle whether a G-buffer exists ([8f2fda3](8f2fda3))
* compile AMD's FSR 3.1 upscaler passes from the FidelityFX SDK ([37afa2e](37afa2e)), closes [NotRexed#577](https://github.com/notquiteog/Hookshot/issues/577)
* compose the scene's view projection, and transpose it for the shader ([df82d90](df82d90))
* count FSR evaluations, because nine passes a call is cheap or ruinous ([85a3ea2](85a3ea2))
* create FSR 3.1's pipelines and internal images ([6b51433](6b51433))
* derive camera motion from the scene's depth, and keep that depth ([29c7cbe](29c7cbe))
* DLSS at the seam, and a stage switch for the FSR cost ([6aa9f48](6aa9f48))
* DLSS Super Resolution through NGX, behind a build-time SDK gate ([aff6abb](aff6abb))
* frame generation asks for its own extensions and the whole input set ([ad291d1](ad291d1))
* FSR 3 frame interpolation, and a jitter for the upscalers to undo ([384c41b](384c41b))
* give FSR the assembled depth and camera motion ([481b957](481b957))
* HOS_DISPLAY_VRR, which is what actually holds the frame rate at 60 ([c65c68e](c65c68e))
* HOS_FPS_FORCE, because raising the limiter alone does not hold ([2008ac7](2008ac7))
* HOS_VSYNC_HZ, the hardcoded 60 that actually paces the game ([5eb9f8b](5eb9f8b))
* link NGX properly, frame generation at the seam, AMD's interpolation sources ([43e3360](43e3360))
* Monitor's shape for screens narrower than 16:9 (4:3, 16:10, 5:4) ([b44c9cd](b44c9cd))
* name each network binding's role in the upscaler input contract ([d9566a1](d9566a1))
* publish Ocarina's camera, and make a backend reachable without the stand-in ([8bd7c8d](8bd7c8d))
* record FSR 3.1's nine dispatches ([8769c80](8769c80))
* route the game's upscale dispatch through one backend seam ([1a036c8](1a036c8))
* RX_VK_VP_HUNT, to find the view projection from a value only it holds ([3c02213](3c02213))
* the hunt found the projection, so widen it to find the view ([2946a32](2946a32))
* the parallel lock says which site took it, and the answer is two sites ([b8543c9](b8543c9))
* the two backends have rows in the Insert menu ([cf8b3b1](cf8b3b1))
* the two lock sites say why they had to take it ([614f552](614f552))
* the upscaler and frame generation follow the menu while you play ([61d7d4a](61d7d4a))
* the upscalers and frame generation are the native settings, not new rows ([a0ce7de](a0ce7de))
* the vsync event follows the display, and 0 turns it off ([a67e471](a67e471))
* **vk:** AMD FSR 4 upscaling and frame generation through AMD's runtime ([65003cc](65003cc))
* **vk:** choose the frame generation method in the Insert menu ([c4f7d84](c4f7d84))
* **vk:** DLSS 5 neural rendering through NGX directly, beside Streamline ([7784399](7784399))
* **vk:** DLSS 5 neural rendering, with the model's own settings in the menu ([2851bc6](2851bc6))
* **vk:** frame generation Auto: a frame for each of the display's refreshes ([a5a7440](a5a7440))
* **vk:** say why DLSS 5 neural rendering is refused, and the RTX 20-40 route ([60f1d2e](60f1d2e))
* **vk:** time a capture's GPU wait for its late pages apart in the log ([e3b622f](e3b622f))

### Bug Fixes

* a fork releases its own work, not upstream's ([47dd097](47dd097))
* a Windows build could ask for DLSS and then fail to link ([d72441a](d72441a))
* **audio:** 5.1 surround when the default output has it ([NotRexed#1124](https://github.com/notquiteog/Hookshot/issues/1124)) ([b0abeca](b0abeca))
* **audio:** a finished wave buffer reaches the game at the renderer update, not mid-pass ([28e4a11](28e4a11))
* **build:** a Windows build linked the ARM64 NGX library, not the x64 one ([70b7d25](70b7d25))
* **builder:** copies from a read-only install stay writable ([70ffa5e](70ffa5e))
* **builder:** stop when generated source cleanup fails ([d254e6f](d254e6f))
* **builder:** the window icon is copied writable too ([4b2d871](4b2d871))
* **build:** NGX asks for Microsoft's C++ runtime, which llvm-mingw has not ([114e917](114e917))
* **build:** the feature announcement swallowed the whole Vulkan build ([1004f37](1004f37))
* **build:** the twelve MSVC runtime symbols NGX asks of an llvm-mingw link ([d00159a](d00159a))
* clear the SPD counter between the pyramid passes (did not fix the cost) ([66e8c3a](66e8c3a))
* contain pipeline cache save allocation failures ([4518a20](4518a20)), closes [NotRexed#1022](https://github.com/notquiteog/Hookshot/issues/1022)
* HOS_FPS_FORCE must not touch the nominal rate: it is slow motion on screen ([8681f2e](8681f2e))
* **lift:** clear an earlier lift's hot pieces before the patches; count every made and raw output-size place ([816a472](816a472)), closes [NotRexed#912](https://github.com/notquiteog/Hookshot/issues/912)
* **lift:** edit_c in chunks.py, so its tests import without the lift's tools ([1c5258f](1c5258f))
* **lift:** group page marks after the patches; a missing output-size edit stops the lift ([3fa6300](3fa6300))
* **linux:** the AppImage's parts list names the repository it is built in ([2228816](2228816))
* narrow screens: window at the picture's shape, save picture's middle band, comments ([3060261](3060261)), closes [NotRexed#1183](https://github.com/notquiteog/Hookshot/issues/1183)
* preserve NUP member names during implicit reads ([1fdfeff](1fdfeff))
* publish the camera from the inlined projection copies too ([c630d59](c630d59))
* read the upscaler contract in descriptor order, not from draw_sampled ([492bcd0](492bcd0))
* **release:** the fork's releases follow upstream's v2.0.x line ([d495994](d495994))
* **release:** the setup, the Builder and the game update from the repository they were built in ([6fd4364](6fd4364))
* **release:** the Windows release installs the renderer CI built, with DLSS, NR and FSR 4 ([0fa18bd](0fa18bd))
* scan for the view projection once a target set, not once a draw ([12d4d83](12d4d83))
* snapshot the projection's fields, and name what caps the frame rate ([3790556](3790556))
* the conventionalcommits preset has to match the changelog writer ([ca6595f](ca6595f))
* the DLSS contract test links again ([86b6988](86b6988))
* the game's network takes one texture, so it is not a temporal upscaler ([853cfe3](853cfe3))
* the history reset the document specifies, which nothing implemented ([9858599](9858599))
* the host camera symbols must be weak, or the renderer does not load at all ([70ecdf4](70ecdf4))
* the release's commit-analyzer preset is a package, and it was not declared ([3ff9fbd](3ff9fbd))
* the renderer would not load, and the build now says so ([c871a3c](c871a3c))
* the shader check compared glslang's version, and ran where glslang is not ([f48483c](f48483c))
* the view projection scan ran per draw, which stuttered hard ([2d9b4e8](2d9b4e8))
* **vk:** a draw that waits for its pipeline runs the build at once if it is still queued ([aafbaf5](aafbaf5))
* **vk:** DLSS frame generation sets NGX's parameters through its C functions ([2c19b63](2c19b63))
* **vk:** frame generation Auto: the snap distance is not named near (windows.h defines it empty) ([d156a6d](d156a6d))
* **vk:** keep the last sRGB UNORM surface format, so drivers that list only sRGB keep theirs ([3408437](3408437))
* **vk:** Linux/Wayland: take an sRGB surface format for the swapchain ([7416487](7416487))
* **vk:** no mirror is made inside a parallel replay's segment (an earlier segment read it unfilled) ([8f0ed68](8f0ed68))
* **vk:** tag the uplift colour pair as whole resources, as the shipping integration does ([dd3e61e](dd3e61e))
* **vulkan:** bind only bytes accessed by buffer-image copies ([b0a9034](b0a9034))
* **vulkan:** clean up failed mesh argument initialization ([b2f5692](b2f5692))
* **vulkan:** preserve guest mip levels in partial clears ([d7a1207](d7a1207))
* **vulkan:** release modules from rejected cache records ([2838e89](2838e89))
* **widescreen:** the save picture shows the middle 16:9 on ultrawide ([NotRexed#952](https://github.com/notquiteog/Hookshot/issues/952)) ([e2e155e](e2e155e))

### Performance Improvements

* **fpx:** keep the hardware result for FMUL/FMLA S when the flags are already set ([6de3bf1](6de3bf1))
* **lift:** one page mark per straight-line group of stores through a base register ([825861b](825861b))
* **lift:** the interrupt poll before each backward goto, calling a64_poll_cur() with no argument, so a loop's guest registers stay in host registers ([0d6ed03](0d6ed03))
* stream sampled profiles instead of loading every line ([8cf6d1e](8cf6d1e))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Save state screenshots look squished on ultra-wide.

1 participant