Skip to content

Game barriers: only a pass that wrote an aliasing texture takes the full barrier - #2093

Closed
Xipzer wants to merge 1 commit into
seedds:mainfrom
Xipzer:barrier-alias
Closed

Xipzer wants to merge 1 commit into
seedds:mainfrom
Xipzer:barrier-alias

Conversation

@Xipzer

@Xipzer Xipzer commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

With DM_NVN_BARRIER=game (draw_barriers game) and an image family heap alive, only a draw pass that may have written an aliasing texture takes the full barrier, instead of every pass.

Cause. Since image family heaps (c484b13), enc_r_begin_pass and enc_r_end_pass took the full barrier for every draw pass while one such heap lived (g_image_heap_n), for the alias flush. So draw_barriers game ordered nothing for a game that has one. Breath of the Wild has one (its resolution family): pass-entry barriers full 142 a frame, saved 0.

Change.

  • A pass is marked (g_pass_alias) when attach gives it a texture of a family heap (g_image_heap_owner_key on the texture or its parent), or when a draw of it uses a shader that writes images or storage (udc_note_draw's image_write).
  • A marked pass ends through m4_end, as every other encoder does, so the next encoder starts with the full barrier and its alias flush (resource_vis()).
  • A pass not marked keeps the relaxed end and flushes to the device only (plus the alias flush while large-pool chunks overlap, g_big_n, as before). The pass after it may start without a barrier.
  • The power line's draw-pass entry barriers part also counts the passes ended in full for that reason (mtl_barrier_stats has a third value).

Not taken from the fork: the bar_frag_only condition of #1572, which is a separate PR.

Measured on a fork that carries the change over an older main, Breath of the Wild:

  • M4 Max, uncapped, two alternated pairs against DM_NVN_BARRIER=full: saved 84 of 122 pass-entry barriers a frame (27 passes a frame attach a texture of the family); frame p50 7.46 / 7.42 ms to 7.13 / 7.24 ms.
  • Warm iPad Pro M4: the frame is bound by GPU time and the pairs show no difference (79.5 and 77.8 fps).

Not shown. The rule relies on the alias flush of the next encoder's start barrier making an aliased write visible to every later encoder, also one that starts without a barrier of its own. The GPU check for the flush on a start barrier is main's existing check_t_copy_alias, where the reader is the next encoder. The new cases in t_image_alias check the bookkeeping (which passes end relaxed, which start in full), not pixels read through an alias. No game was run on this branch, and scripts/regress.py check was not run (see the comment below).

Checked on this branch (dbf8499 plus the commit), Apple M4 Max:

  • make -C dm/gfx/tests GAME=01007EF00011E000 check_t_image_alias: 335, 335 and 343 checks under Metal validation, 0 failed. The third run is new (DM_NVN_BARRIER=game): barrier_game keeps its first answer, and Breath of the Wild's profile on main says full, so the fork's setenv inside the test came too late here. The new block runs when game barriers are on: a pass into a plain texture ends relaxed with a family alive, the pass after it starts without the full barrier, a pass that attached a family texture ends as other encoders do, and the next pass starts in full.
  • make -C dm/gfx/tests GAME=01007EF00011E000 check_t_copy_alias: 126 and 126 checks, 0 failed. By hand with DM_NVN_BARRIER=game: 126 checks, 0 failed.
  • 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.

The reference entry "The alias flush on every encoder's end barrier" (docs/reference/frame-rate.md) still says every encoder starts with a barrier carrying resource_vis(); it is not edited here.

…hat may have written an aliasing texture ends with the full barrier, instead of every pass: Breath of the Wild, which has one such heap, saves 84 of 122 pass-entry barriers a frame where it saved 0 of 142

Since image family heaps (c484b13), enc_r_begin_pass and enc_r_end_pass took the full barrier for every draw pass while one such heap lived, for the alias flush. A game with one got nothing from DM_NVN_BARRIER=game / draw_barriers game. Breath of the Wild has one (its resolution family): pass-entry barriers full 142 a frame, saved 0.

Now the pass is marked (g_pass_alias) when attach gives it a texture of a family heap, or a draw of it uses a shader that writes images or storage (udc_note_draw's image_write). A marked pass ends through m4_end, as every other encoder does, so the next encoder starts with the full barrier and its alias flush (resource_vis). A pass not marked keeps the relaxed end and flushes to the device only (plus the alias flush while large-pool chunks overlap, as before), and the pass after it may start without a barrier. The power line counts the passes ended in full for that reason.

Measured on a fork that carries the change over an older main, Breath of the Wild, M4 Max, uncapped, two alternated pairs against DM_NVN_BARRIER=full: saved 84 of 122 pass-entry barriers a frame (27 passes a frame attach a texture of the family), frame p50 7.46 / 7.42 ms to 7.13 / 7.24 ms. On a warm iPad Pro M4 the frame is bound by GPU time and the pairs show no difference (79.5 and 77.8 fps).

Not shown: the rule relies on the alias flush of the next encoder's start barrier making an aliased write visible to every later encoder, also one that starts without a barrier of its own. The GPU check for the flush on a start barrier is the existing check_t_copy_alias (the reader there is the next encoder). The new cases in t_image_alias check the bookkeeping (which passes end relaxed, which start in full), not pixels read through an alias.

Checked: check_t_image_alias, 335, 335 and 343 checks under Metal validation, 0 failed; its third run is new, with DM_NVN_BARRIER=game, since barrier_game keeps its first answer and the profile built for the test may say full. check_t_copy_alias 126 and 126 checks, 0 failed (and 126, 0 failed with DM_NVN_BARRIER=game by hand). 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).
@Xipzer

Xipzer commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

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).

@github-actions github-actions Bot added the nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) label Nov 10, 2026
@seedds

seedds commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Superseded by e775054 on main (from #2334). Instead of marking the passes that attach an aliasing texture, a pass that skips the full barrier flushes aliases in a start barrier before its fragment stage. That's free on the start barrier, and it doesn't put every family-texture pass behind the full barrier, which in Ocarina of Time is most passes. It also narrows the game-barrier bits. Breath of the Wild's profile still says draw_barriers full. Thanks for the t_image_alias bookkeeping cases and the BotW numbers.

@seedds seedds closed this Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants