Repository navigation
Conversation
|
Note: I could not run scripts/regress.py check for this one. This Mac has Xcode 27.1 (Metal 32023.921), and the chain lock on main names 27.2's 32023.922, so the local games' shaders do not build against main's chain here. Measured on a fork that carries the same change over an older main (numbers in the description). |
A draw that samples an attachment it writes reads the tile when it opens its pass too, not only in an open one: Breath of the Wild opens about 3.75 passes a frame with such a draw, each a full-size snapshot before (30,000 snapshots to 4 over 11,000 frames on an M4 Max) fb_tile_plan needed an open pass, since the tile-reading pipeline is named by the pass's formats, so a draw that met no pass took a snapshot: a full-size copy, where the pass it opens loads the same content into the tile. Breath of the Wild's 753d0169ae3ce4f0 and 84862343ae82c6fa read G-buffer targets they write and open their pass. The formats are now learned. The first draw of a program on a set of targets that opens its pass and samples an attachment it writes takes its snapshot, and fb_early_after notes the pass rp_begin_ex opened for it (64 entries, keyed by the two shaders and the targets). Later ones plan the tile read with those formats while no pass is open and are checked once the pass opens: a pass that differs (formats, size, slice-merged) is ended, the draw takes its snapshot through fb_prepare again and the pass reopens; three such passes and the entry plans no more. A clear still waiting becomes the pass's load action, so the tile holds the cleared content, as the snapshot does since b54968e. Where fb_depth_rule ends the pass before the plan, the draw counts as opening its pass. DM_NVN_FB_TILE_EARLY=0 turns it off. Measured on a fork that carries the change over an older main (413e880 plus the fork's commits), Breath of the Wild. M4 Max, 190% render scale, uncapped: snapshots 30,000 to 4 over 11,000 frames, GPU p50 5.99 to 5.56 ms and 6.9 to 5.9 ms in two alternated pairs, pictures the same. iPad Pro 13" M4, 120% render scale, target 120 fps: snapshots 150 a second to 0; GPU p50 10.88 to 10.77 ms and 11.62 to 10.90 ms, 75.9 to 75.6 fps and 70.7 to 74.7 fps in two alternated pairs (the iPad's runs drift with its temperature). That fork's fb_prepare returned early for a draw that samples no attachment of its pass (PR seedds#1469) and had no depth rule; here the plan itself notes only draws that sample an attachment they write. Checked: check_t_feedback, 293 checks under Metal validation, 0 failed, with new cases (nothing learned by a draw that writes no attachment it samples, the first pass-opening draw's snapshot and the noted pass, the next one's plan with no pass open and its pipeline, the GPU reading green and red that the opened pass loaded, a refused pass, the snapshot after it, no plan after three). The host builds for 01007EF00011E000, the iOS compile check has 0 of 262 failed, test_env_switches, test_docs, test_lint_c and test_architecture pass. Not run on this main: any game (regress.py check).
149c2b5 to
501f12a
Compare
|
Rebased onto main (2e6edda) without conflicts, retitled to the Checked on this commit (M4 Max): host build, |
|
Now that this Mac has the chain lock's compiler (Xcode 27.2), regress.py check (full): PASSBaseline
BOTW
|
A draw that samples an attachment it writes now reads the tile when it opens its render pass too, not only when it meets an open one. fb_tile_plan needed an open pass, since the tile-reading pipeline is named by the pass's formats, so such a draw took a full-size snapshot where the pass it opens loads the same content into the tile. Breath of the Wild opens about 3.75 passes a frame with one (753d0169ae3ce4f0, 84862343ae82c6fa). The formats are learned: the first such draw of a program on a set of targets takes its snapshot and fb_early_after notes the pass rp_begin_ex opened for it (64 entries); later ones plan with those formats and are checked once the pass opens. A pass that differs (formats, size, slice-merged) is ended, the draw takes its snapshot and the pass reopens; after three the entry plans no more. A waiting clear becomes the opened pass's load action. DM_NVN_FB_TILE_EARLY=0 turns it off. Measured: Breath of the Wild, regress route on an M4 Pro: 4.0 -> 0 snapshots a frame, 293 -> 295 tile reads, no pass refused; regress.py check PASS (19,592 shaders, no MSL change, no new RED). The author's M4 Max: 30,000 -> 4 snapshots over 11,000 frames, GPU p50 5.99 -> 5.56 and 6.9 -> 5.9 ms. check_t_feedback: 336 checks, 0 failed. Docs: rendering.md's tile-read entry, the game's note. Co-authored-by: Xipz <60103178+Xipzer@users.noreply.github.com>
|
Landed on main as 854cdc0, your code unchanged and credited as co-author. Thank you for this one. Added on top: the pass-opening case in Checked on current main (Apple M4 Pro): |
A draw that samples an attachment it writes now reads the tile when it opens its render pass too, not only when it meets an open one.
Cause.
fb_tile_planneeded an open pass, since the tile-reading pipeline is named by the pass's formats. A draw that met no pass took a snapshot: a full-size copy, where the pass it opens loads the same content into the tile. Breath of the Wild opens about 3.75 passes a frame with such a draw (shaders753d0169ae3ce4f0and84862343ae82c6faread G-buffer targets they write).Change. The formats are learned:
fb_early_afternotes the passrp_begin_exopened for it (64 entries, keyed by the two shaders and the targets).fb_prepareagain for its snapshot, and the pass reopens. Three such passes and the entry plans no more.DM_NVN_FB_TILE_EARLY=0turns it off.Fitted to main. The change was written on a fork over an older main (413e880), where
fb_preparereturned early for a draw that samples no attachment of its pass (PR #1469, still open) and had no depth rule. Two differences here:fb_tile_planruns for every draw whose shader has a tile-readable sample, so the plan itself notes a pass only for a draw that samples an attachment it writes. Otherwise other pass-opening draws would take entries of the 64-entry table and push the real ones back to a snapshot.fb_depth_rule(PR A draw that writes depth and samples its own depth attachment reads a copy of it: Ocarina of Time's light-blue dashes over the rock at Death Mountain Trail's cave entrance are gone #1917) runs before the plan and may end the pass. Such a draw then counts as opening its pass, and the secondfb_prepareafter a refused pass runs the depth rule again (a depth copy taken the first time is reused).Measured on that fork, Breath of the Wild:
Checked on this branch (dbf8499 plus the commit), Apple M4 Max:
make -C dm/gfx/tests GAME=01007EF00011E000 check_t_feedback: 293 checks under Metal validation, 0 failed. New cases: a draw that writes no attachment it samples learns nothing; the first pass-opening draw takes its snapshot and the pass is noted; the next plans with no pass open and gets the pipeline built for the open-pass case; the GPU reads green and red that the opened pass loaded; a pass other than the planned one is refused and counted; the draw then takes a snapshot; no plan after three.make -C dm/host GAME=01007EF00011E000: builds, no errors.dm/ios/tools/compile_check.py: 262 files, 0 failed.test_env_switches,test_docs,test_lint_c,test_architecture: 31 tests, OK.Not shown. No game was run on this branch, and
scripts/regress.py checkwas not run (see the comment below). The numbers are from the fork, whosefb_preparediffers as described above. No reference entry is added todocs/reference/rendering.md.