fix(vk): keep the textures the GPU drew that the game samples again after finalizing them (a cached shadow map) #2368
Open
Joey-Magnus wants to merge 6 commits from
Joey-Magnus/fix/keep-resampled-drawn into main
pull from: Joey-Magnus/fix/keep-resampled-drawn
merge into: MagicBeanDev:main
MagicBeanDev:main
MagicBeanDev:github/pr/2893
MagicBeanDev:github/pr/2890
MagicBeanDev:github/pr/2887
MagicBeanDev:github/pr/2878
MagicBeanDev:github/pr/2875
MagicBeanDev:github/pr/2874
MagicBeanDev:github/pr/2873
MagicBeanDev:github/pr/2871
MagicBeanDev:github/pr/2756
MagicBeanDev:github/pr/2897
MagicBeanDev:github/pr/2896
MagicBeanDev:github/pr/2718
MagicBeanDev:github/pr/2842
MagicBeanDev:github/pr/2691
MagicBeanDev:github/pr/2895
MagicBeanDev:github/pr/2743
MagicBeanDev:github/pr/2765
MagicBeanDev:github/pr/2708
MagicBeanDev:github/pr/2732
MagicBeanDev:github/pr/2755
MagicBeanDev:github/pr/2817
MagicBeanDev:github/pr/2876
MagicBeanDev:release-please--branches--main
MagicBeanDev:release-please--branches--main--release-notes
MagicBeanDev:github/pr/2778
MagicBeanDev:github/pr/2888
MagicBeanDev:github/pr/2866
MagicBeanDev:github/pr/2877
MagicBeanDev:github/pr/2800
MagicBeanDev:github/pr/2829
MagicBeanDev:github/pr/2850
MagicBeanDev:github/pr/2889
MagicBeanDev:github/pr/2882
MagicBeanDev:github/pr/2860
MagicBeanDev:github/pr/2849
MagicBeanDev:github/pr/2886
MagicBeanDev:github/pr/2794
MagicBeanDev:github/pr/2840
MagicBeanDev:github/pr/2830
MagicBeanDev:github/pr/2834
MagicBeanDev:github/pr/2719
MagicBeanDev:github/pr/2742
MagicBeanDev:github/pr/2741
MagicBeanDev:github/pr/2820
MagicBeanDev:github/pr/2771
MagicBeanDev:github/pr/2859
MagicBeanDev:github/pr/2858
MagicBeanDev:github/pr/2823
MagicBeanDev:github/pr/2757
MagicBeanDev:github/pr/2769
MagicBeanDev:github/pr/2844
MagicBeanDev:github/pr/2843
MagicBeanDev:github/pr/2673
MagicBeanDev:github/pr/2855
MagicBeanDev:github/pr/2739
MagicBeanDev:github/pr/2807
MagicBeanDev:github/pr/2851
MagicBeanDev:github/pr/2789
MagicBeanDev:github/pr/2814
MagicBeanDev:github/pr/2818
MagicBeanDev:github/pr/2774
MagicBeanDev:github/pr/2772
MagicBeanDev:github/pr/2750
MagicBeanDev:github/pr/2786
MagicBeanDev:github/pr/2785
MagicBeanDev:github/pr/2782
MagicBeanDev:github/pr/2779
MagicBeanDev:github/pr/2775
MagicBeanDev:github/pr/2706
MagicBeanDev:github/pr/2766
MagicBeanDev:github/pr/2747
MagicBeanDev:github/pr/2759
MagicBeanDev:github/pr/2745
MagicBeanDev:github/pr/2740
MagicBeanDev:github/pr/2733
MagicBeanDev:github/pr/2725
MagicBeanDev:github/pr/2727
MagicBeanDev:github/pr/2728
MagicBeanDev:github/pr/2729
MagicBeanDev:github/pr/2721
MagicBeanDev:github/pr/2722
MagicBeanDev:github/pr/2715
MagicBeanDev:github/pr/2714
MagicBeanDev:github/pr/2707
MagicBeanDev:github/pr/2703
MagicBeanDev:github/pr/2705
MagicBeanDev:github/pr/2697
MagicBeanDev:github/pr/2679
MagicBeanDev:github/pr/2676
MagicBeanDev:github/pr/2669
MagicBeanDev:docs/what-hookshot-connects-to
MagicBeanDev:fix/hdr-encode-test
MagicBeanDev:fix/main-tests
MagicBeanDev:fix/play-cache-files
MagicBeanDev:perf/game-barrier-order
MagicBeanDev:feat/heap-lazy
MagicBeanDev:feat/texture-detail
MagicBeanDev:perf/astc-opaque-bc1
MagicBeanDev:fix/fs-dir-lock
MagicBeanDev:fix/framegen-native-kept
MagicBeanDev:fix/update-no-downgrade
MagicBeanDev:port/k10-gcn-f16-mirrors
MagicBeanDev:fix/framegen-mult-cap
MagicBeanDev:chore/release-as-2.1.1
MagicBeanDev:perf/pcache-load-switch
MagicBeanDev:fix/prepare-progress-log
MagicBeanDev:fix/update-closes-stale-game
MagicBeanDev:fix/hdr-scaled-sampled
MagicBeanDev:perf/first-start-preset
MagicBeanDev:perf/zero-upload
MagicBeanDev:fix/vcore-escape-2s
MagicBeanDev:test/fake-gh-atomic
MagicBeanDev:perf/batch-idle-flush
MagicBeanDev:test/finish-stub-a
MagicBeanDev:chore/record-2.1.0
MagicBeanDev:fix/main-red-tests
MagicBeanDev:fix/test-display-startup-driver-cache
MagicBeanDev:feat/mesh-shader-mode
MagicBeanDev:fix/menu-probes-at-startup
MagicBeanDev:fix/fps-counter-classic
MagicBeanDev:fix/test-log-batch-flake
MagicBeanDev:fix/narrow-screen-hud
MagicBeanDev:ci/prereleases
MagicBeanDev:release/v2.0.15
MagicBeanDev:hotfix/v2.0.15
MagicBeanDev:fix/present-ahead-replay
MagicBeanDev:release/v2.0.13
MagicBeanDev:fix/bracket-paths-2013
MagicBeanDev:fix/hdr-overlays-2013
MagicBeanDev:fix/fps-counter-scale-2013
MagicBeanDev:fix/stick-seen-at-rest
MagicBeanDev:hotfix/menu-fits-screen
MagicBeanDev:fix/f1-always-opens-menu
MagicBeanDev:fix/ini-keep-lines
MagicBeanDev:claude/cranky-pike-7cf247
MagicBeanDev:perf/texture-answer-flat
MagicBeanDev:fix/stamp-test-modules
MagicBeanDev:fix/appimage-fonts
MagicBeanDev:fix/display-awake
MagicBeanDev:release/v2.0.12
MagicBeanDev:fix/discord-windows
MagicBeanDev:rc/v2.0.12-tests
MagicBeanDev:fix/dlss-keep-feature
MagicBeanDev:rc/v2.0.12
MagicBeanDev:button-icons
No reviewers
Labels
Clear labels
accessibility
Barrier affecting people with disabilities
autorelease: pending
autorelease: tagged
bug
Something isn't working
dependencies
Pull requests that update a dependency file
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
github_actions
Pull requests that update GitHub Actions code
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
testing wanted
wontfix
This will not be worked on
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
2 participants
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/Hookshot!2368
Loading…
Reference in a new issue
No description provided.
Delete branch "Joey-Magnus/fix/keep-resampled-drawn"
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?
When VRAM is tight, the eviction pass throws out the game's cached shadow map, and shadows go missing or come out wrong until the game draws it again. This keeps such textures through evictions forced by the texture budget.
Refs #2209 (dark, moving shadows over Hyrule Field) and #993. Tracking: #995.
What goes wrong
keep_finalizedonly keeps textures of 4 MB or less.From the log (
RX_VK_LOG_REMADE=1, main + this branch's base, Dodongo's Cavern,RX_VK_VRAM_MB=512):The logs on #2209 (GTX 1660 Ti 6 GB), #2297 (RTX 4060 8 GB) and #2238 (RTX 4060 Laptop 8 GB) show the same 12288x12288 map created again two to four times in one session.
The change
Texture::resampledis set where a texture the GPU drew is sampled after the game finalized it, which is how a cache the game keeps behaves.keep_resampled::kMaxUnused, the same line after which #2198's reserve lets a long-unused texture go). One from an area left behind goes as before.RX_VK_KEEP_RESAMPLED_MB(512 by default;0evicts as before). The candidates' total is counted before the pass, and the pass drops the least recently used beyond the cap, askeep_finalizeddoes, so the cap goes to the most recently used. The rule is ingpu/vk/keep_resampled.h.resampledis cleared whereverreleasedgoes back to false (the texture is drawn again).keep_finalizeddoesn't count a texturekeep_resampledalready covers, so none is in both totals. WithRX_VK_KEEP_RESAMPLED_MB=0it keeps them as before.Numbers
Five-minute headless scripted walks in Dodongo's Cavern (
HOS_HEADLESS=1). The texture budget is forced down to 512 MB so the eviction runs constantly, withRX_VK_LOG_REMADE=1. Same save, same build except for this change.kept, not evictedlines from this ruleTested on: Linux (Ubuntu 26.04.1, KDE Plasma 6.6 Wayland), RTX 4070 Ti 12 GB with driver 595.99.02, Ryzen 7 9800X3D, 2560x1440 DLAA. There is a row in
docs/tested-configurations.md.Not tested:
RX_VK_VRAM_MB=512)The cost: up to 512 MB of VRAM kept for textures that would otherwise have been evicted under pressure. In these runs that was about 320 MB, the two shadow maps.
Test
python -I -B tests/test_keep_resampled.py:=0vk_texture.cppstill asks the rule, sets the flag and reads the switchThe full
tests/run_all.pypasses: 361 passed, 0 failed, 14 skipped.🤖 Generated with Claude Code
Thanks, the diagnosis is convincing: depth, so not flushable; finalized, so neither
keep_finalized(4 MB cap) nor the unflushable rule (!released) covers it, and the remade-over-GPU-storage path then samples it empty. TheRX_VK_LOG_REMADElines and the before/after table make the case well. The rule itself is small, has a switch, and the test covers its cases.What I'd like changed before this goes in is how the 1 GB is spent, because it matters most on the 6-8 GB cards this is for:
orderfrom least recently used, andkeep_resampled::keephands out the cap first come, first served. So the textures that take the 1 GB are the stalest resampled ones, and once the cap is used up, a younger one (the current area's shadow map) is the one that goes. Nothing ever lets a kept one go except being drawn over, so a shadow map from an area left behind stays for the rest of the session.keep_finalizeddoes this the other way round on purpose (sum first, drop the least recently used beyond the cap), and elsewhere in this loop a course unused for 3600 presents is allowed to go. I'd suggest the same here: count the candidates' total up front and let the oldest go beyond the cap, and/or don't keep one unused for more than N presents (3600 would still cover the 400-frame gap in your log).RX_VK_VRAM_MBaround 4000 in Hyrule Field for a few minutes, would help here, since the 512 MB run with a 12 GB card doesn't show what the driver does near its limit.const bool overwritten = shifted || drawn_over(tex);, so whichever merges second has a small textual conflict. The semantic one matters more: #2198's reserve line exists to let go of long-unused textures, and a 400-frames-unused 288 MB shadow map is exactly that. With this rule placed first, the hold can't take it and will take live textures instead to meet its line. Fine if that's the intent, but it should be decided when the two are merged together, and the rule's VRAM should probably be counted by the hold.Smaller things:
resampledis never cleared.releasedgoes back to false when the GPU writes the texture again (lines 2613/3132), butresampledstays, so a reused target that was once sampled after finalizing (e.g. last frame's shadow map read before it's drawn) keeps the flag for good. Clearing it alongsidereleased = falsewould make the flag mean what the comment says.finalized_kept, so thekeep_finalizedtotal stays inflated and its LRU drops a few more small ones than it needs to. Minor; skipping resampled ones when summingfinalized_keptwould fix it.CI: the failing
appimageandSPIR-V fixturesjobs fail in the apt/toolchain install step (DNS to archive.ubuntu.com), not in anything this touches; Windows build and the Windows Python checks pass, the Ubuntu ones were still pending.@ -2472,6 +2473,10 @@ Texture* texture_touch(Texture* tex, bool for_sampling) {// GPU's texels, and a write before (another variable in the same page) re-uploaded garbage over a drawn LUT:// the rainbow sky)const uint32_t since = tex->gpu_written && tex->released ? std::max(tex->upload_gen, tex->release_gen) : tex->upload_gen;// (hookshot) one the game finalized and samples after that: a cache it keeps (the shadow map it draws, finalizes and samplesNit: nothing clears
resampledwhen the GPU draws the texture again (released = falseat 2613/3132). Resetting it there would keep the flag to "sampled after its latest finalize".1024 MB default: on an 8 GB card that's about a fifth of the texture budget pinned. Your runs needed ~320 MB; a default around 384-512 would cover the shadow maps with less pressure on everything else.
orderis oldest first, so this gives the cap to the least recently used resampled textures and evicts the newest ones once it's full; and a kept one never ages out unless drawn over. Could this followkeep_finalized(total first, drop the oldest beyond the cap) and/or skip ones unused for more than ~3600 presents?Thanks for the review. Rebased on current
main(7759a6f3), and the points are addressed in837ee609and2c3692cd.keep_finalized: the candidates' bytes are summed before the pass, and walking least recently used first, the oldest go until the rest fit, so the cap goes to the most recently used. There's also an age limit. Only a texture used in the last 1,800 presents is a candidate, the same line after which #2198's reserve lets a long-unused texture go, so one from an area left behind is evicted as before. 1,800 still covers the ~400-present gap in the log.RX_VK_VRAM_MB~4000 in Hyrule Field. I'll add a row todocs/tested-configurations.mdwhen I have it.Smaller ones:
resampledis now cleared at both placesreleasedgoes back to false. The test checks that the two counts match.keep_finalizedno longer counts whatkeep_resampledcovers.2c3692cdfixes a gap in that: withRX_VK_KEEP_RESAMPLED_MB=0the exclusion still applied, so a small resampled texture lost the protection it had before. It now applies only while the rule is on.tests/run_all.pyon Linux passes.vk_texture.cppcompiles withnx2/build.py's flags.Thanks for the rework. Looked at the rebased head (
2c3692cd) against what I reviewed at f34b900.Fixed:
keep_finalizeddoes it. Only textures used in the last 1,800 presents count, so a shadow map from an area you've left goes as before. The test covers the cap order and the clamp.resampledis now cleared at both placesreleased = false, and the Python check makes sure the two counts stay equal (nice).keep_finalizednow leaves resampled textures to this rule, but only while the rule is on (2c3692cd).overwrittenline is still there for whoever goes second.Still open, and the reason this is a comment and not an approval:
RX_VK_VRAM_MBaround 4000 (or on a real 6-8 GB card), with fps, VRAM and any 'made again' counts against main, would settle it. Also, was the tested-configurations row run before the rework (1 GB cap, no age limit)? If so, please say that in the row, or re-run it with the current version.New, a small nit inline: past its cap, a small resampled texture no longer falls back to
keep_finalized.CI: the one failure (
Python source checks (windows-2022, part 2/2)) istests/test_native_astc_texture_count.pyhittingscanfbeing deprecated on Windows. That test came from #2288 and main's own regression runs fail the same way, so it's not this PR. Everything else passes. Mergeable.Nit: this skips every resampled texture used in the last 1,800 presents, including the ones
keep_resampled::keepthen lets go because they're over its cap. So a small (<= 4 MB) resampled texture thatkeep_finalizedused to keep goes once the resampled total is over 512 MB. That only matters past the cap and the gap is small. If you want to close it, only skip it here when this rule actually kept it, e.g. by having the pass trykeep_finalizedafterkeepsays no.Fixed in
837ee609: the candidates are summed first, the oldest go beyond the cap, and anything unused for more than 1,800 presents is no longer a candidate. Thanks.Default is 512 MB now, thanks. The run at
RX_VK_VRAM_MB~4000 (or on a 6-8 GB card) is still open; see the review.@ -2472,6 +2473,10 @@ Texture* texture_touch(Texture* tex, bool for_sampling) {// GPU's texels, and a write before (another variable in the same page) re-uploaded garbage over a drawn LUT:// the rainbow sky)const uint32_t since = tex->gpu_written && tex->released ? std::max(tex->upload_gen, tex->release_gen) : tex->upload_gen;// (hookshot) one the game finalized and samples after that: a cache it keeps (the shadow map it draws, finalizes and samplesFixed: it is cleared at both places now, and the check keeps the two counts equal.
Closed in
ed765d40: past this rule's cap, a small finalized one falls back tokeep_finalizedagain (finalized_after_resampled). It joinskeep_finalized's total at that point in the walk, so the older ones judged before it were judged without it and keep slightly more; I took that over a second pre-pass. Only while the rule is on: withRX_VK_KEEP_RESAMPLED_MB=0the pre-pass already counts it, and adding it again would double it.Thanks for the second pass. Merged current
mainin (ed765d40, no force-push), which clears the conflict with #2198.vram_compact::kReserveUnuseditself, and strict (unused < 1800; before it was<= 1800, so at exactly 1,800 both claimed the texture). A texture this rule keeps is never one the reserve may take, and the two can't drift apart. For textures unused 600-1,800 presents the rule still goes ahead of the hold's own line, so the hold takes other live ones there. That's the trade-off you named, bounded by the 512 MB cap.tests/run_all.pyon Linux: 360 passed, 0 failed. The renderer builds withnx2/build.py.Still open: the run at
RX_VK_VRAM_MB~4000 in Hyrule Field against main. I'll post it here with a new tested-configurations row once the machine is free for it.Thanks. Looked at
ed765d40against2c3692cd. The PR's own diff is still the seven files, and the merge of main brings in #2198 cleanly.Fixed or answered:
keep_finalizedgap past the cap (my last nit): closed. A candidatekeeplets go now falls back tokeep_finalizedthroughfinalized_after_resampled, only while the rule is on, so with=0nothing is counted twice. Joining the total mid-walk, so the older ones keep a little more, is a fair trade against a second pre-pass.vram_compact::kReserveUnuseditself as the age line, strictly (< 1800), means a texture this rule keeps is never one the reserve may take, and the two can't drift apart. That's better than counting the bytes in the reserve. For textures unused 600-1,800 presents the rule still goes before the hold's own line, so the hold takes other live textures there. You named that trade-off and the 512 MB cap bounds it. Fine with me.Still open, and still why this is a comment and not an approval: the run at
RX_VK_VRAM_MB~4000 in Hyrule Field (or on a real 6-8 GB card) against main, with fps, VRAM and the 'made again' counts. You said it's coming. Once it's posted and shows nothing worse than main, I'm happy to approve.Two small nits inline: a comment in
keep_resampled.hgot mangled in the edit, and the new pre-pass could check the cheap flags before the overlap scans.CI: Python checks (Ubuntu and Windows), the Windows Vulkan build and the SPIR-V fixtures pass. The
appimageandHookshot Builder package and setupfailures are bothactions/upload-artifactgetting HTML back from GitHub after the build succeeded, andConventional Commit titlestopped with no error on a valid title. All three look like GitHub hiccups, so a re-run should clear them. Mergeable, no conflicts.@ -0,0 +1,36 @@// Which textures the GPU drew and the game samples again after finalizing them the eviction pass keeps (vk_texture.cpp).Nit: lines 17-20 read like two versions of the comment got merged. "...went unused 400 presents before the eviction took it" has no full stop and runs into "the compaction's reserve's own line, so...". Something like: "Only while used in the last kMaxUnused presents (unused for fewer), the compaction reserve's own line (#2198): one from an area left behind goes as before, and a texture this rule keeps is never one the reserve may take. The 12288 x 12288 map went unused 400 presents before the eviction took it."
@ -4120,9 +4127,14 @@ void texture_frame(uint64_t completed) {// after a while of play (issues 107 and 386). Kept, up to RX_VK_KEEP_FINALIZED_MB (512 by default; 0:Nit: this runs
drawn_over_shiftedanddrawn_overfor every GPU-drawn texture on each pass (every 10 presents), and then drops most of them onresampled. Testingtex->resampled(and the age) first would skip the overlap scans for nearly all of them, e.g.if (tex->resampled && keep_resampled::candidate(...)). It gives the same result.@ -0,0 +1,36 @@// Which textures the GPU drew and the game samples again after finalizing them the eviction pass keeps (vk_texture.cpp).Fixed in
3fbad818with your wording: one comment now, ending at "...before the eviction took it."@ -4120,9 +4127,14 @@ void texture_frame(uint64_t completed) {// after a while of play (issues 107 and 386). Kept, up to RX_VK_KEEP_FINALIZED_MB (512 by default; 0:Done in
3fbad818: the pre-pass teststex->resampled,tex->gpu_writtenand the age first, and only then runsdrawn_over_shifted/drawn_overthroughcandidate. Same result;tests/test_keep_resampled.pystill passes.Here's the capped-memory run you asked for, plus the two nits (
3fbad818) and a tested-configurations row (229e0e5e).Short version: in Hyrule Field this change does no worse than main, at any budget I tried and with the card full. Frame time, evictions and GPU memory match within noise, and neither side sampled a texture EMPTY. One caveat: in the clean runs the rule itself never had to keep anything (details below).
Setup
3fbad818. Main is the renderer of6c8c1bfb(the main this branch last merged), built the same way and swapped into that folder, so both sides share the host and the flags. Runs alternate main / this.kwin_wayland --virtualat 2560x1440,HOS_FPS_MAX=240,RX_VK_LOG_REMADE=1. Load the test save, teleport to Hyrule Field, then 3 minutes of scripted running into the field, turning now and then.RX_VK_FRAME_LOG. GPU memory is the whole card, fromnvidia-smievery 2 s. Evictions are the "textures retired" lines'budget N.Results
RX_VK_VRAM_MB=2000RX_VK_VRAM_MB=1000RX_VK_VRAM_MB=4000(3 pairs)Caveat: where the rule acted
In the clean runs above, the rule never logged keeping a texture, so they show the 512 MB it reserves doesn't push other textures out harder near the limit, but they don't exercise a kept shadow map under pressure. It kept textures (20 "kept, not evicted" lines, the log's limit) in two earlier runs that went further into the field, with 3.0-3.7 GB of textures and over 3,000 evictions over the budget.
If you want that case covered before approving, a cleaner way to force it would help, say a spot where the cached shadow map is reliably evicted over the budget, like Dodongo's Cavern at 512 MB from the first row.
CI:
appimageandHookshot Builder package and setup(theupload-artifactHTML replies) and the cancelledConventional Commit titleneed a re-run. GitHub refuses a re-run from my side, likely because this comes from a fork.Merged current
mainin (6a0aa3c7, no force-push) to clear the conflict.The conflict: only
docs/development.md. Main rewrote theRX_VK_VRAM_MBrow (the budget in MiB, counted by blocks, #2624; the integrated GPU's caps, #2603) and added the capture switches' row above it (#2651). I kept main's rows as they are, and this PR'sRX_VK_KEEP_RESAMPLED_MBrow stays right after the budget's.vk_texture.cppmerged without a conflict, and I checked the eviction pass by hand since main changed it in several places: the evicted textures kept in a set, the 97% squeeze held off while the textures' pool is down to its blocks made at the start, the integrated GPU's budget, and the drawn write-back of #2545. None of that touches the keep rules.keep_resampledstill comes before the compaction hold, its age line is stillvram_compact::kReserveUnused(strict), and past its cap a small finalized one still falls back tokeep_finalized.Earlier review points: the two nits from the last review are still in place after the merge: the single comment in
keep_resampled.h, and the pre-pass testingtex->resampled,gpu_writtenand the age before the overlap scans (3fbad818).resampledis still cleared at both placesreleasedgoes back to false. All the inline threads already have answers.Tested:
tests/test_keep_resampled.pyand the other 27 checks that readvk_texture.cpppass. The mergedvk_texture.cppcompiles withnx2/build.py's flags. The fulltests/run_all.pyon Linux: 463 passed, 1 failed, 37 skipped. The one failure istests/test_lift_cache.py, which fails onmainon this machine too.Still open: the same as before. The capped-memory runs in Hyrule Field didn't make the rule keep anything under real pressure, so a kept shadow map near the limit hasn't been measured cleanly yet. Whether that's needed before approving is your call. The merge changes nothing in the game, so I haven't run it in the game again.
(_trash.panda's agent)