Repository navigation
Conversation
…ina of Time's snow no longer flickers with black 16x16 blocks The snow draws (55845639d605e028, ee567dea65288f58 and others) test depth LEQUAL with writes while their fragment shader samples that depth through a texture slot; Metal leaves the read undefined and Apple's GPUs returned whole 16x16 tiles of another state. fb_prepare now asks such a draw (a fragment texture slot or bindless handle naming the depth it writes) for a pass whose depth attachment is a copy (g_dswap): rp_begin_ex_ copies the depth in, the draw writes the copy and samples the original, and rp_end_at copies it back, with full barriers. Pool and dynamic reads are left out (they may name any texture). DM_NVN_DEPTH_SWAP=0 turns it off. New GPU test t_depth_swap (19 checks, at the default and off); reference entry "A draw that samples the depth it writes". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # dm/games/0400486024DEC000/README.md
|
Closing: #1917 (merged in 8190693) handles the same case: a draw that writes depth and samples it through a texture slot now reads a copy. That covers the snow draws here ( |
…s pass before it copies: the first draw after a clear that samples and writes its target read the content from before the clear, in the colour snapshots and the depth copies alike (issue #1932's patch) A clear with no draw after it waits to be the load action of the next pass on its targets (R.clear_c / clear_d). fb_prepare's colour snapshot and fb_depth_snap only ended an open pass before copying, so when the draw taking the copy was the first of its pass, the copy held what the attachment had before the game's clear, while the draw wrote into the cleared attachment. Both copy paths now call rp_flush, which ends the pass or lands that clear first. Found by reading dean0396's patch in issue #1932 against what landed in 8190693; not seen in a game. Checked: check_t_feedback, 281 checks under Metal validation, with new cases for the clear still waiting (real Depth32Float and Depth32Float_Stencil8 depth copies in a colour and a depth-only pass, and a colour snapshot); 6 of them failed before. The graphics suite, test_docs.py, test_env_switches.py; lint_c.py tidy at the baseline. regress.py check on NieR:Automata (its FXAA takes a colour snapshot every frame) against 04811a6: no new red line, fps 59.5 -> 59.7, frame p99 17.74 -> 17.91 ms, snapshot #1000 reached on both sides; the screenshot difference it flagged is the opening flight a moment further along (first frame 0.5 s later), both clean. Not run on Ocarina of Time (no libraries for this chain on disk, no save at the snow) or on iOS. The reference entry also records Ocarina of Time's snow in adult Zora's Domain (issue #1932) and on frozen walls (PR #1995): its shaders sample the depth they write through texture slot 6, which the copy from 8190693 covers; not yet run on main. Co-authored-by: dean0396 <4335213+dean0396@users.noreply.github.com>
|
Thanks for tracing this down to the snow draws. Main now covers them another way, so I'm closing this PR without landing it:
The storage-overlap match ( Could you retest the frozen walls, pillars and stairs on current main? If the blocks are still there, please open an issue with the spot and a pass dump. |
What and why
Symptom. In The Legend of Zelda: Ocarina of Time (
0400486024DEC000, v131072), the snowy areas showed black or dark 16×16 blocks, aligned to the screen grid, where the snow meets rock and walls: in front of the frozen walls, at the foot of pillars, on snowy stairs. The blocks moved every frame. Inside them, what lies behind the snow showed through, as if the snow had failed its depth test there. Nothing changed them: present upscaling off, render scale, docked or handheld,DM_NVN_SKIP_MESH=1, the fixed-resolution cut or not,DM_NVN_FB_SNAP=all,DM_NVN_UNWRITTEN_DC=0.Cause. The snow draws (
55845639d605e028, tessellated, andee567dea65288f58, among others) test depth LEQUAL with depth writes on, and their fragment shader samples that same depth texture through a bound texture slot (a depth fade against what lies under the snow). Sampling an attachment the draw itself writes is undefined in Metal: on Apple's GPUs whole tiles of another state come back, the same failure as Same-pass feedback read through a snapshot for colour attachments (#377). NVN gives the draw the depth from before it.fb_preparealready ended the pass before such a draw when an earlier draw of the pass had read the depth, but the draw's own read of what it writes stayed undefined.Change. General, in
nvn_mtl.m:fb_prepare: a draw that writes depth while a fragment texture slot (fb_slot_reads, new) or a bindless handle (fb_texu_reads) names the depth attachment's storage asks for a depth copy, and the open pass ends.rp_begin_ex_: the pass that draw opens copies the depth attachment into a texture of the same format and size (g_dswap, kept between uses) and attaches the copy instead. The draw writes the copy and samples the original, which nothing changes until the pass ends.rp_end_at: the copy goes back into the depth attachment. A full barrier orders both copies with the passes around them (g_game_barrier).pool_ds_read); taking them would give many draws a pass and two copies each.DM_NVN_DEPTH_SWAP=0turns it off.Reference entry: A draw that samples the depth it writes. The game note has a line for it, and the symptom index is regenerated.
Checked
dm/gfx/tests/t_depth_swap.m(make -C dm/gfx/tests GAME=<id> check_t_depth_swap), through the productionfb_prepare,rp_beginandrp_end, withMTL_DEBUG_LAYER=1, once at the default and once withDM_NVN_DEPTH_SWAP=0. A draw writing depth whose texture slot names the depth takes the copy, and the depth it wrote reaches the attachment. A draw writing depth it does not read, and one reading depth it does not write, keep the attachment. A request no depth pass takes is not left for a later pass. 19 checks each, 0 failed.check_t_feedback: 203 checks, 0 failed. One check, "two reports in one command buffer read two times", failed once onmainwithout this change and passed in the run with it; it looks flaky and unrelated.dm/tools/tests/test_docs.pyandtest_env_switches.pypass. The host builds (make -C dm/host GAME=0400486024DEC000 dmhost).dm/tools/lint_c.py tidy(no clang-tidy 23 on this Mac).0400486024DEC000, v131072), Mac, docked, around the snowy areas (the frozen walls and pillars, the snowy stairs).DM_CTLtexdumpwithDM_NVN_TEXSNAPcopies of the 1536×864 RG11B10F scene after each pass. The blocks appeared in the passes of55845639d605e028andee567dea65288f58, one draw each, each pass ended byfb_preparebefore it. A temporary switch that turned those two shaders' depth writes off removed the blocks where they draw, and not where other snow draws.depth read while written: …), no new[RED]line, and a frame time p50 of 16.6 ms in the area (300-frame windows). That is not a matched A/B.main.regress.py checkneeds games this Mac does not have beyond Ocarina of Time. The change only acts on a draw that writes depth while a texture slot or bindless handle of its fragment shader names that depth, which was undefined on Metal before; every other draw takes the same path as before.Docs
docs/reference/rendering.mdand the game note, andpython3 dm/tools/docs_symptoms.pywas run (the symptom index)🤖 Generated with Claude Code