Skip to content

Ocarina of Time draws its 3D above its Switch 2 resolution: Render scale 1.333 gives native 2560x1440 and 2.0 native 3840x2160 docked, where the console draws 1920x1080 and upscales - #2532

Open
Know-It-All-Brother wants to merge 2 commits into
seedds:mainfrom
Know-It-All-Brother:oot-render-scale

Conversation

@Know-It-All-Brother

@Know-It-All-Brother Know-It-All-Brother commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

What and why

This lets Ocarina draw its 3D at a higher resolution than it does on a Switch 2, up to native 3840x2160. Docked, the game never draws its 3D views above 1920x1080: that's level 0, the top of its dynamic resolution, at 0.75 of its 2560x1440 output, and its upscaler fills the rest. On main the launcher's Render scale doesn't reach this game at all, because the dynamic-resolution stub never reads DM_RENDER_SCALE and the profile doesn't declare RENDER_SCALE.

With this PR the Render scale sets the 3D views at 1080 x the scale lines, docked:

Render scale Output 3D views
1.0 (default, as main) 2560x1440 1920x1080, upscaled
1.333 2560x1440 2560x1440, native
1.5 3840x2160 2880x1620, upscaled
2.0 3840x2160 3840x2160, native

Up to 1.333 it's one change: when Dynamic resolution is off, the dynamic-resolution stub's level 0 also writes level 0's scale as the game's own scale times the render scale, capped at the output's size. 1.0 writes the game's own scale back, so nothing changes unless you raise the setting.

Above 1.333 the game outputs its own 3840x2160 instead, which took four more patches. Each site is found by its function's fingerprint (output_4k in patches.py):

  • The output size goes up 1.5x at the game's init and on a mode change. The game already has 3840x2160 in its own output list.
  • The window textures' size goes up 1.5x at the graphics init. Without it the frame buffer is 4K and every present crops a 2560x1440 window texture.
  • The render-target memory goes up 2.25x. The game sizes its per-frame render targets from a 305 MB budget in 64 MB blocks, and a 3840x2160 frame doesn't fit, so the allocator hands back a null block and the game crashes at the first frame of play.
  • Level 0 is written as 1.5 x 0.75 x the render scale, because the dynamic resolution's update and the view-size update each scale it by 1440 / the output's height.

I found these with probes of the view-size update in play, the host's write watch (DM_PROBE_WATCH) on the view's size fields, and a decompile of the game's render-target allocator. The game note's Render scale section has the addresses.

The game only reads these sizes at start and on a level change, so the setting takes effect at start, which is how the launcher sets it. The stub declares RENDER_SCALE 2, so the launcher's slider goes up to 2.0.

Checked

On an M5 Max (40-core GPU, 64 GB), docked, in a window, on main at c9d57d2 plus this change:

  • Pass dumps in play show the main passes at 1920x1080 with 1.0, 2560x1440 with 1.333, 2880x1620 with 1.5 and 3840x2160 with 2.0, with no red lines. At 1.5 the game's own upscaler fills the 4K output cleanly.
  • bench.py on kokiri-deck from the deck save, two runs of each:
Render scale 3D views fps GPU p50
1.0 (as main) 1920x1080 60.0 / 60.0 13.41 / 13.85 ms
1.333 2560x1440 60.0 / 60.0 13.90 / 13.88 ms
2.0 3840x2160 60.0 / 59.0 14.54 / 14.71 ms

On this Mac native 4K costs about 0.7 ms of GPU time a frame over native 1440p, and the game holds 60 fps.

  • Checks that pass: docs_patches.py --check, test_docs, test_patches, test_settings and test_launcher_settings.

Not checked: handheld (the output stubs scale the handheld size too, but I haven't run it), a slower Mac, or the iPhone.

Screenshots

The same spot on the Kokiri Forest deck at render scale 1.0, 1.333 and 2.0. The 1.0 and 1.333 crops are shown at 3x and the 2.0 crop at 2x, so all three cover the same area at the same size:

Render scale 1.0 on the left and 1.333 on the right (crops shown at 2x):
closeup_link
closeup_mushrooms
closeup_village

Full frames at 1.0, 1.333 and 2.0:
scale_1 0
scale_1 333

scale_2 0

Docs

  • The findings are in the game note (the Render scale section), and python3 dm/tools/docs_symptoms.py was run (the symptom index, unchanged)
  • docs/agents/game-patches.md is regenerated for the new sites
  • No game files, keys or data extracted from a game in the change or the attachments

Related

Both only lower the size. This PR raises it, up to native 4K, and leaves the lower range to those PRs. If #1903 lands first, its stubs and this one would need one combined RENDER_SCALE.

…own size: 1.333 draws the 3D views at 2560x1440 instead of 1920x1080 docked

With Dynamic resolution off, the dynamic-resolution stub's level 0 now also writes level 0's scale (the first entry of the game's level list) as the game's own scale times DM_RENDER_SCALE (state +0x10), at most 1.0 of the output. The game's own scale is kept in the state page (+0x2a0) on the first call, so 1.0, the default, writes it back and nothing changes. The stub declares RENDER_SCALE 2, so the launcher offers the setting; above 1.333 the 3D views stay at the output's size.

The game reads the scale when it sets its views' sizes, at start and when the level changes, so the setting takes effect at start, as the launcher sets it.

Checked on an M5 Max (40-core GPU), docked, a window, kokiri-deck from the deck save: pass dumps in play show the main passes at 1920x1080 with 1.0 and 2560x1440 with 1.333; three alternating runs of each gave 56.2 fps / GPU p50 13.46 ms with 1.0 and 57.5 fps / 13.34 ms with 1.333. docs_patches.py --check, test_docs, test_patches, test_settings and test_launcher_settings pass.

The game note's new Render scale section also records why 3840x2160 is not done: the frame buffer can be raised to it, but the game's upscaler library has no variant above 2560x1440 and the window's textures stay that size.
@github-actions github-actions Bot added rewriter The NSO rewriter and per-build patches docs Documentation game: 0400486024DEC000 A game labels Nov 12, 2026
@Know-It-All-Brother Know-It-All-Brother changed the title Ocarina of Time takes the launcher's Render scale up to its output's own size: 1.333 draws the 3D views at 2560x1440 instead of 1920x1080 docked Ocarina of Time draws its 3D above its Switch 2 resolution: Render scale 1.333 gives native 2560x1440 docked, where the console draws 1920x1080 and upscales Nov 12, 2026
…raws its 3D views native at it

Above 1.333 the output is the game's own 3840x2160 docked instead of 2560x1440, and the 3D views are drawn at 1080 x the render scale lines of it: 2.0 at 3840x2160, 1.5 at 2880x1620 with the game's upscaler filling the rest. 1.333 or less keeps every size as before.

Four sites, each found by its function's fingerprint (output_4k in patches.py):
- the output size x 1.5 at the game's init (main+0x1087b70, v131072) and on a mode change (main+0x6502e0)
- the window textures' size x 1.5 at the graphics init (main+0x346be50); without it every present crops a 2560x1440 window texture
- the render-target memory x 2.25: the app object's budget (+0x5b8, 305 MB) and block size (+0x5c0, 64 MB), which main+0x36ae9d0 copies into the allocator; without it the per-frame render targets of a 3840x2160 view do not fit, the allocator hands out a null block and the game crashes at the first frame of play (main+0x404fc)
The level-0 stub writes 1.5 x 0.75 x the render scale above 1.333, since the dynamic resolution's update and the view-size update each scale the level by 1440 / the output's height.

Checked on an M5 Max (docked, a window, kokiri-deck from the deck save, two runs each): main passes at 1920x1080 / 2560x1440 / 2880x1620 / 3840x2160 with 1.0 / 1.333 / 1.5 / 2.0 (pass dumps in play), no red line; GPU p50 13.41-13.85 ms with 1.0, 13.88-13.90 with 1.333, 14.54-14.71 with 2.0, 59-60 fps. docs_patches.py --check, test_docs, test_patches, test_settings, test_launcher_settings. Not run: handheld, a slower Mac, the iPhone.
@Know-It-All-Brother Know-It-All-Brother changed the title Ocarina of Time draws its 3D above its Switch 2 resolution: Render scale 1.333 gives native 2560x1440 docked, where the console draws 1920x1080 and upscales Ocarina of Time draws its 3D above its Switch 2 resolution: Render scale 1.333 gives native 2560x1440 and 2.0 native 3840x2160 docked, where the console draws 1920x1080 and upscales Nov 13, 2026
@Amost-

Amost- commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Tested on a slower Mac, which the description lists as not checked. The PR works as described on an M2 Pro, but native 3840x2160 costs about 31 ms of GPU time a frame there, not 1 ms.

Setup: MacBook Pro M2 Pro (19-core GPU, 32 GB), macOS 27.2, built-in screen, a window, SDR, docked. Host: main 034e91a8 plus local picks that don't touch the game's patches, with this PR's two commits cherry-picked (one conflict, a table row in the game's README). scripts/bench.py, route field-run with one more A press at 58 s, from my own save in Kokiri Forest, two runs of each. The 3D size is the most frequent attachment size in DM_NVN_PASSDUMP=every:1500.

Render scale Dynamic resolution 3D views Output fps GPU p50
1.0 off 1920x1080 2560x1440 30.7 / 30.8 23.74 / 23.98 ms
1.333 off 2560x1440 2560x1440 25.1 / 26.4 29.67 / 29.74 ms
1.5 off 2880x1620 3840x2160 20.6 / 20.6 38.93 / 37.99 ms
2.0 off 3840x2160 3840x2160 17.5 / 17.4 54.50 / 54.67 ms
2.0 on 1280x720 3840x2160 27.8 / 30.5 24.74 / 25.62 ms
1.0 on 854x480 2560x1440 31.9 (one run) 18.85 ms
  • The sizes match the description at every scale, with no red lines and no crash at the first frame of play at 1.5 or 2.0.
  • Cost: from 1920x1080 to 3840x2160 the GPU time goes from 24 to 55 ms, about 5 ms per million pixels. On this Mac 1.0 holds 30 fps and every higher scale falls below it. At 2.0 the game also runs slow: the bench's game speed is 0.87 (0.98 at 1.5, 1.00 at 1.333 and below).
  • Dynamic resolution on: the 3D falls to a third of the output's width at both scales, and the GPU time stays above 16.7 ms even there (18.85 ms at 854x480). At 2.0 that gives a 3840x2160 output with 1280x720 3D, fewer 3D pixels than 1.0 with it off. The description says the scale applies with Dynamic resolution off; a line in the Render scale row's description would save slower Macs from that combination.

Not checked: handheld, HDR, other areas, a long session.

@eremitewang-netizen

Copy link
Copy Markdown

Tested on an M2 Max (38-core GPU, 64 GB, macOS 26.6), windowed, docked, SDR, from a save in Kokiri Forest and Hyrule Castle.

Engine: NX2Apple 1.5.0-rc1 (pin 0c078130, chain a5e7fb6e4fa4). This PR's patches.py applied on top; DM_RENDER_SCALE in ~/Library/Application Support/nxnative/0400486024DEC000/env.txt. Metal HUD on, numbers from the host log (timing lines, 300-frame windows).

Render scale Output 3D views GPU p50 Frame p50 fps
1.0 2560x1440 1920x1080 16.6 ms 16.67 ms 60
1.2 2560x1440 2328x1310 21.0 ms 22.8 ms ~44
1.333 2560x1440 2560x1440 23.2 ms 25.0 ms ~40
1.5 3840x2160 2880x1620 30.5 ms 35.3 ms ~28
2.0 3840x2160 3840x2160 38.2 ms 40.1 ms ~25

The scale works as described, and 1.333 is visibly sharper (1:1 with the window). But:

At 1.333 the dark areas flicker: black shapes appear for single frames at night — outdoors after dusk and in unlit interiors. It is not there at 1.0 (same save, same spot, two runs each), and it comes back at 1.333 and 1.5. It looks like the same family as the profile's "models black for one frame when a streaming step replaces their textures" (below), but the save stands still, so no streaming step should run.

Reproduced with DM_RENDER_SCALE=1.333 alone (no other switch set), 3 of 3 runs.

Two questions:

  1. Is this known, or does the higher 3D size interact with something in the point of the stub this PR patches (a shadow map or a light's render target whose size now differs from the one the game computes)?
  2. Do you want a log from a run at 1.333 in the night area, and is a save at that spot useful?

Either way, 1.0 is what I am running for now. Thanks for the patch: the picture at 1.333 is the best this Mac has looked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation game: 0400486024DEC000 A game rewriter The NSO rewriter and per-build patches

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants