fix(vk): tree-shadow flicker without the TAAU network: the shadow mask evened out over frames (rebase of #477) #1581
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!1581
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/tree-shadow-flicker-history"
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?
Rebase of #477 by @imbahzor onto current main, as asked when it was closed ("please rebase on current main and reopen"). The commit is theirs, cherry-picked with
-x; the only changes are the conflict resolution and the push-descriptor mark, which now goes through main'sdescriptors_disturbed()(see Change).Problem
Over sunlit ground and along shadow edges near trees, shadows flicker constantly in thin streaks, even with the camera still. This happens on GPUs where the gameplay TAAU network is left out ("a network left out on this card" in the log, for example an RX 5500M), so the bilinear stand-in runs instead.
The tree-shadow pass
3427a1dbefd5e02cis a full-screen draw. It rebuilds a normal from anImageGatherof the neighbouring depths and offsets its shadow-map lookup by that normal. On grass blades and leaf edges, those neighbours flip between the blade and the ground behind it as the camera shifts by a sub-pixel each frame, so the pixels flip between lit and shadowed. On the console, the TAAU network averages that away.Change
shaders/shadow_stab.comp: two history images used in turn, then G and B copied back.RX_VK_MASK_BLEND=<w>: the weight, default 0.85;0turns it off.RX_VK_MASK_RADIUS=<r>: limits the history to a (2r+1)² neighbourhood. Default 0 (no limit), because a 3×3 limit kept nothing.descriptors_disturbed()right after the push. Without that, the next dispatch with the cached layout skipped its push and read the pass's bindings, which caused a device lost after a save load in #477's testing. #477 also markedmovie_upscaleandtaau_stand_in; main now does that itself.Tests
tests/test_tree_shadow_mask.py(new) checks that the blend follows the tree-shadow pass only whileRX_VK_MASK_BLENDis above 0, needs an RGBA8 storage mask and push descriptors, runs outside the render pass, and that every push of the stand-ins' or the mask's own layout reachesdescriptors_disturbed()before its function returns. It fails with any one of those three calls removed.Tested
779bdcf): played on an RX 5500M (RDNA1, 4 GB, Windows 11, AMD driver; main779bdcfwith #1505–#1514) in Hyrule Field. The constant shadow flicker is gone. The log showstree-shadow mask blend on #266 768x432, weight 0.85; the pass runs once a frame there.88b554ewith #1509–#1513 and this PR): no flicker, specks or tree-shadow problems noticed in a 23-minute session; see their comment on #1510.9447e1d: one conflict, the renamedskin_checkcall next to the new guard. Compile check clean.tests/run_all.py: 291 passed; the 6 that fail are the same 6 that fail on plain main on this PC, so no new failures. Not played again: the only code change from the played version is the push-descriptor mark going throughdescriptors_disturbed().Looks good. The tree-shadow mask blend only runs on the 3427a1dbefd5e02c draw and only while RX_VK_MASK_BLEND is above 0, so it stays out of the way otherwise. The render pass is ended and a full barrier is taken before and after the two dispatches. Everything stays in GENERAL like the other compute passes here. The
pushed()[1].cmdreset after each own-layout push matches howbind_descriptorsuses it on main. CI is green, and the test is picked up by run_all.py's glob.Nits, none blocking:
shadow_stab.compstill says the history is bounded by the 3x3 neighbourhood. The default is now radius 0 (unbounded), as the later comment in the file and the PR text say.viewsmap inmask_stabilizeis keyed by VkImage and never cleared. If the mask texture is destroyed and a new image gets the same handle, a stale view would be reused. Probably rare for a render target at fixed size, but dropping entries on texture destroy (or keying on a texture generation) would be safer.hist_framevalidity doesn't notice the mask texture changing between frames (same size, different image). Harmless apart from one blended frame.Overlap: #1637/#1776 touch shadow write-back, so check them against this if they merge close together.
Heads-up: this conflicts with main now (a lot landed today). Could you merge main into the branch (or rebase) and resolve the conflicts? Thanks!
Rebased on main
9447e1d. The only conflict was the line just before the new guard indraw():dlss_skin_checkisskin_checkon main now.One more change came with the rebase. Main now calls
descriptors_disturbed()aftermovie_upscaleandtaau_stand_inpush their own descriptors, so this PR's marks there are gone, and the new pass uses that helper too. The test checks that all three pushes reach it and fails with any one removed.I also fixed your first nit: the header comment of
shadow_stab.compnow says the history is unbounded by default. It's a comment-only change, so the SPIR-V is unchanged. I left the other two nits alone so the code stays as you reviewed it. #1637 and #1776 are both in main already, and this merges on top of them.Re-checked after the rebase onto
9447e1d. Still good.shadow_stab.compnow says the history is unbounded by default. The SPIR-V header is byte-identical to the one I reviewed, so it is a comment-only change.descriptors_disturbed(), which clears both cached pushes, same effect as the oldpushed()[1].cmdreset. The marks this PR used to add inmovie_upscaleandtaau_stand_inare gone because main does them itself now. The test's regex finds exactly those three own-layout pushes, and each one is followed by the call.skin_checkis fine. The rest of thedraw()hook is unchanged.viewsmap keyed by VkImage,hist_framenot noticing a new mask image) were left as they were on purpose. That's fine, they don't block.#1637 and #1776 have landed, so the overlap I mentioned is settled. CI: everything passes except
test_log_batch.pyon Windows, which failed itsGetTickCount64() - t0 < 200timing check. This PR doesn't touch logging, so that looks like a slow runner, not this change.test_tree_shadow_mask.pypasses on both platforms. A re-run should clear it.github-sync referenced this pull request2026-11-12 06:22:54 +00:00