feat(options): a Shadow resolution setting keeps thin shadows near Link smooth #773
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!773
Loading…
Reference in a new issue
No description provided.
Delete branch "Jiro113/Longshot:feat/sun-shadow-resolution"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: feat · Risk: low · Tested on: Windows 11, RTX 4090; Linux build and checks (Ubuntu 22.04) · Needs: an 8 GB card (Balanced preset), any maker
Why
The shadows the sun casts from thin things near Link, such as the drawbridge's chains, look like stair steps. The game draws the sun's shadow maps 1600 pixels a side, sized for the console's 720p and 1080p;
at a PC's resolutions each shadow-map pixel covers several screen pixels, so the edges of thin shadows break up. The game
already has larger sizes in its own table (2200 and 3072) that it never uses at the console's settings. This PR lets the
player choose them, and makes the presets for cards with 8 GB or more choose the largest.
Searched the tracker for shadow issues: none matches the stair-stepped shadows of thin things. Two other shadow problems
are open and this PR does not touch them: shadows on Link that break with the camera angle (#601) and blocky artifacts
around Link in Zora River's water on AMD/Linux (#603).
What changed
A Shadow resolution row in Graphics: Game's (1600), High (2200), Highest (3072). The lifter adds one hook where the
game rounds the size of the sun's shadow cascades; the host raises that size when the row asks for more, never lowers it,
and reads the row every frame, so a change applies at once. The Quality presets set it: Game's on Minimum and Low,
Highest on Balanced, High and Ultra.
nx2/oot_shadow.py, installed fromnx2/lift.pyin standalone builds. It checks the build id and the exactinstruction words at the site (
and w23, w8, #0xfffffff8after MAIN+0x4729f0) and installs nothing when they differ.host/oot_shadow.c,hos_oot_shadow(). Only the object whose vtable is the sun shadow's is changed, and onlyw23. Game's, or a value out of range, reads no game memory and leaves every register as it was.
HOS_OPT_SHADOW_RES = 56ingpu/vk/vk_api.h, answered ingpu/vk/vk_replay.cpp. Ingpu/vk/vk_menu.cpp: the row,apply_preset()'s values, and the reading of an options file from before the row inmenu_init()andmenu_auto_for_gpu()(shadow_res_unsaved). The bug report counts the row among the defaults thatdepend on the PC (
report_default_depends_on_pc()).tier hints, which now name the shadows (
gpu/vk/menu_i18n.h).shadow_resolutionline indocs/development.md; the measured run indocs/tested/.tests/test_oot_shadow.pychecks the install against the build id and each instruction word, and compiles thereal host helper against an authored shadow object. The checks cover the sun's object only, raise-only, live changes,
only x23 changing, the menu wiring, and each preset's value. The fixtures that cut
menu_init()'s load block(
test_menu_keys.py,test_upscaler_round_trip.py) declare the new flag.test_bug_report_defaults.pycounts therow with the preset-set rows.
test_menu_sections.pycounts the new row.Measurements
Verdict: High costs about 0.02 ms of GPU time a frame and Highest about 0.05 ms on an RTX 4090 (medians; 0.03 and 0.07 ms by the means). That is under 1 %
of the frame, and the frame rate did not move: the run is CPU-bound there. In video memory, by the maps' size, Highest
takes about 113 MB against the game's 31 MB.
Hardware: Windows 11 Home 10.0.26300, RTX 4090 24 GB, NVIDIA 621.24, Core i9-13900K, 64 GB RAM.
Scene: Hyrule Field at the castle's drawbridge as a child, standing still, camera untouched. Output 2560x1440 with
ultrawide on (presented 5120x1440 SDR); DLSS 2560x720 to 5120x1440, preset K; frame generation Off; V-Sync Auto
(mailbox); Quality preset Auto (this PC), on its Ultra tier (memory budget 5).
Method: an in-session A/B with
RX_VK_PASS_TIMING=1. Shadow resolution was switched live through the control pipe in 3rounds of Game's, High, Highest. Each setting kept 2 blocks of 600 presents, after leaving out the block that straddles
the switch, so there are 6 blocks for each. The table gives the median, with the mean and the standard deviation beside it.
Baseline: Game's is this PR's row at 0, which leaves the game's size as main does: the hook reads no game memory and
changes no register there (
tests/test_oot_shadow.py). An in-session A/B on the same spot was steadier than separateruns of main and this PR.
How to reproduce: build a pack of this branch over a base, stand at the drawbridge, set
RX_VK_PASS_TIMING=1, and switchthe row (or
longshot_ctl.py option set shadow_resolution <0|1|2>), reading theGPU pass timingblocks.Two earlier runs with the periodic
GPU timeline showed no difference in fps either: Hyrule Field 88.0, 89.4 and 89.7fps, and the drawbridge 74.7, 74.6 and 74.1 fps for Game's, High and Highest.
e257da53 is this branch's second commit before the rebase onto
aa95d35f(now463c23ff). The commits after it change onlythe menu, so the hook and what it draws are the same: the presets set the row, and an old options file is read.
Screenshots
The same spot, by day, switched live in one session: Game's, High, Highest, then Game's again, each shot 12 frames after
the switch. A 400x560 crop of the 5120x1440 frame, enlarged 2x without smoothing: the gate pillar's shadow on the
left-hand wall. Its edge's stair steps are coarse on Game's, finer on High, finest on Highest, and coarse again back on
Game's. The whole view shifts by a few pixels between shots, so compare the edges rather than where the shadow lies.
A second run with
RX_VK_PASS_TIMING=1checked the sizes at each switch: 1600x1600 shadow passes after Game's, 2200x2200after High, and the 3072x3072 map used every frame on Highest only
(the comment). The chains' shadow on the
planks, and the whole view, are in the first screenshots' comment.
Flags and settings
One menu row and its options-file key; no switch.
shadow_resolutioninlongshot_options.ini, 0 to 2)Testing
Tested:
69ff5bebwith no change to the commits (git range-diff):608cd6ab, named below where it was tested,is now
ab099100.python -I -B tests/run_all.pyon Windows 11 (llvm-mingw) atab099100: 501 passed, 20 failed, 74skipped, none in a file this PR touches: the 18 below, and fpx_sticky and spawn_emit, which can't find the C++
standard headers to build a64emit on this PC (
'algorithm' file not found).python -I -B tests/run_all.pyon Windows 11 (llvm-mingw), at df342aab (608cd6ab, the head, only corrects thesettings in the
docs/tested/file;test_tested_configurationsandtest_oot_shadowran again there): 539 passed,18 failed, 37 skipped. All 18
also fail on main (
a00c485c) on this PC; they are Windows environment failures in files this PR doesn't touch:appimage_player_build, bug_report, cpm_discovery, cpp_preflight, crash_dialog, env_on, lift_cleanup_error,
linux_preflight, make_game_cache, make_game_prepare, missing_nup, oot_state_tap, options_budget_once, readback_late,
tested_configurations (CRLF in one pipe check), vk_pgo, walk_key, work_lock (the venv's Python launcher keeps the lock
after a kill; it passes with the base interpreter).
tests/test_oot_shadow.py,test_preset_exempt,test_bug_report_defaults,test_menu_keys,test_upscaler_round_trip,test_menu_i18n,test_menu_sectionsandtest_tested_configurations'sformat checks pass.
608cd6ab):tests/run_all.pywith the system Python 3.10: 487 passed, 22 failed,85 skipped; main (
aa95d35f) in the same place: 484 passed, 24 failed. Every failure on the branch also fails on mainthere, and none is in a file this PR touches; several are the same as on Windows (env_on, options_budget_once).
test_oot_shadowpasses with gcc 11 and with tools.lock'sclang 23.
RX_VK_ONLY=1 nx2/build.py --standalone, tools.lock's clang and sysroot) builds and linkslongshot_vk.sowith the new option.host/oot_shadow.ccompiles with the host's Linux flags and-Wall -Wextra -Werror, with clang 23 and gcc 11, against the generated runtime headers. A whole Linux player buildneeds the game, so it was not run.
608cd6ab), one start each with its own userfolder, reading the row through the control pipe once the game presents:
RX_VK_AUTO_PRESET_VRAM_MB=4096(tier 1): Game's;memory_budget, on Auto for this PC: Highest; the same file with the card takenas 6 GB (
RX_VK_AUTO_PRESET_VRAM_MB=6144, "the hardware changed", tier 2): Game's;Highest, Highest, Highest, Highest, Game's, Game's (Custom keeps what was there). Balanced, then Shadow resolution
set to High by hand: High, and the preset turns to Custom.
ivy near Link:
Measured as above.
Not tested:
and while switching the row.
nothing in it depends on the maker, but the video memory and the GPU time were measured on an RTX 4090 only.
Must be tested:
switch Shadow resolution between Game's and Highest. Expect smooth chain shadows on Highest, no new stutter, and no
texture-budget warning in
longshot_log.txt; report the GPU time line for each.as on NVIDIA, and the same row values after picking each Quality preset.
Risks
What could break:
so the row then does nothing.
the renderer's list of large textures after the switch to Game's, but a second one (54 MB), idle since before the
switch, stayed listed through Game's and High for over 6000 presents. Starting the game on Game's never makes them.
Turning it off: set Shadow resolution to Game's, which makes the hook a no-op.
Compatibility:
longshot_options.inigainsshadow_resolution. A file from before it takes the value of the preset it came from:Game's on Minimum and Low, Highest otherwise, and Auto's tier once the GPU is known. A player who had picked Balanced
or above sees sharper shadows after updating.
MKWVK_API_VERSION:MkwVkHostandMkwVkRendererareunchanged; only a new option id is added.
Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named).third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.docs/tested/for each new measured run (format indocs/tested-configurations.md).tests/that reads changed source updated in this PR, not weakened.🤖 Generated with Claude Code
Screenshots of the same spot: Hyrule Field at the castle's drawbridge as a child, by day, standing still with the camera reset (ZL). Windows 11, RTX 4090, NVIDIA 621.24, presented at 5120x1440 SDR (DLSS 2560x720 to 5120x1440, preset K). Shadow resolution was switched live through the control pipe in one session, each shot taken 12 frames after the switch.
The crops below are 400x360 pixels of the 5120x1440 frame (from x 2600, y 740), enlarged 2x without smoothing. They show the right-hand chain's shadow on the planks beside Link. The shadow sits a little to one side or the other from shot to shot because the idle camera sways: the chain and the gate pillar move by the same amount, and a second Game's shot taken last is as far from the first as High is.
Before: Game's 1600, this PR @
608cd6abAfter: High 2200, this PR @
608cd6abAfter: Highest 3072, this PR @
608cd6abThe whole view, After: Highest 3072, this PR @
608cd6ab(scaled down to 2560x720)🤖 Generated with Claude Code
Settled the last two boxes and tested the presets in the game, at
608cd6ab(no new commit):host/oot_shadow.ccompiles with the host's Linux flags under clang 23 and gcc 11 with-Wall -Wextra -Werror; run_all fails nothing that main doesn't fail in the same place.Must be tested now names its test requests: T-TUR5 (an 8 GB card on Balanced) and T-WSWW (AMD or Intel). Both wait for the testing wanted label.
A clearer view of the same four shots (same run as the earlier screenshots, this PR @
608cd6ab): the gate pillar's shadow on the left-hand wall, a 400x560 crop of the 5120x1440 frame (from x 560, y 200), enlarged 2x without smoothing. The setting went Game's, High, Highest, then Game's again, each shot 12 frames after the switch.The edge's stair steps are coarse on Game's, finer on High, finest on Highest, and coarse again when it goes back to Game's.
The cascade sizes at each setting were checked in a second run at the same save's start in Hyrule Field, with
RX_VK_PASS_TIMING=1and the row switched through the control pipe, 1300 presents per setting:🤖 Generated with Claude Code
Screenshots: the description now shows the gate pillar's shadow edge for Game's, High, Highest and Game's again, where the difference is plainest; the chain crops stay in the earlier comment. The cascade sizes at each setting are checked in a second run (comment 24829). The note on the view shifting between shots is reworded as what was seen.
A correction to the size check in the comment above (#773 (comment)): the "last used 1241 presents ago" I quoted belongs to the wrong map. The run has two 3072x3072 maps, and that one (0x10004579a00) was already idle before any switch (last used 41, then 641 presents ago, on Highest).
The evidence is the other one, 0x10004c2e400: it is listed as "last used 1 presents ago" in every listing taken on Highest (before the first switch and after the switch back to Highest), and it is absent from every listing taken on Game's or High. The shadow passes are 1600x1600 after Game's and 2200x2200 after High, as said.
The idle map stays listed through Game's and High (54 MB), so switching down during play doesn't give all the video memory back at once; the description's Risks now say so.
🤖 Generated with Claude Code
WIP: feat(options): a Shadow resolution setting keeps thin shadows near Link smoothto feat(options): a Shadow resolution setting keeps thin shadows near Link smoothTesting needed. This PR is ready for review, but it was run in the game on an RTX 4090 (Windows 11) only. Two runs by hand are needed before merge, posted as test requests:
Steps for both: stand still at Hyrule Field's drawbridge as a child, by day, and switch Graphics > Shadow resolution between Game's and Highest. Expect the shadows' edges (the gate pillar's on the left wall, the chains' on the planks) to turn smooth on Highest and coarse again on Game's, with no new stutter. On the 8 GB card, also look for a texture-budget warning in
longshot_log.txtand give theGPU timeline for each setting. On AMD or Intel, also pick each Quality preset and check that the row reads Game's on Minimum and Low, and Highest on Balanced, High and Ultra.A maintainer: the requests are waiting for the
testing wantedlabel before testers can see them.🤖 Generated with Claude Code
608cd6abd5ab09910038Rebased on main
69ff5beb, with no change to the commits (git range-diff):608cd6abis nowab099100.run_allrerun at the new head, added to Testing; the measurements and in-game runs stand, the code they ran is unchanged. #836, stacked on this PR, was rebased with it.🤖 Generated with Claude Code
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.