[perf] Frame-loop barriers use ALL_COMMANDS scope (inter-pass + acquire/present) (Window.cpp) #115

Closed
opened 2026-06-17 21:18:52 +02:00 by jorijnvdgraaf · 0 comments

Found by an adversarially-verified performance audit (confirmed real after a skeptic pass).

This issue covers all ALL_COMMANDS-scoped barriers in the frame loop — both the inter-pass barriers and the acquire/present barriers. They share one fix (derive the real per-pass stage union) and one correctness constraint (RT passes), so they are a single co-dependent deliverable. (Merged from the former #117.)

Locations: implementations/Crafter.Graphics-Window.cpp:881-888 (inter-pass) and :818-893 (acquire/present)
Severity: medium · Effort: moderate · Category: sync

Problem

  1. Inter-pass (:881-888): between two consecutive passes a global VkMemoryBarrier sets both srcStageMask and dstStageMask to ALL_COMMANDS, flushing/invalidating all caches and fully serializing against every pipeline stage — when all the next pass needs is the swapchain image written by the previous one to be visible.
  2. Acquire/present (:818-893): the acquire dstStageMask and present srcStageMask are also ALL_COMMANDS, though the swapchain image is only written by compute/RT.

Both defeat the GPU's ability to overlap the tail of one pass with the head of the next, and force full cache round-trips every frame.

Fix

Replace the inter-pass global barrier with an image memory barrier scoped to the swapchain image (the codebase already does this intra-pass at UI.cpp:264), and narrow the acquire/present stage masks to the actual writer stages.

Constraint (why moderate, not trivial)

Passes are polymorphic: a compute pass writes via COMPUTE_SHADER, an RT pass via RAY_TRACING_SHADER. A hardcoded COMPUTECOMPUTE mask would under-synchronize an RT pass and produce a correctness bug. Derive the stage mask per-pass from what each pass actually does, or use the conservative-but-tight union COMPUTE_SHADER | RAY_TRACING_SHADER | TRANSFER for the frame-edge barriers. These are frame-edge/per-pass barriers, so the practical bubble is modest but the per-frame cache flush is real.

Found by an adversarially-verified performance audit (confirmed real after a skeptic pass). This issue covers **all `ALL_COMMANDS`-scoped barriers in the frame loop** — both the inter-pass barriers and the acquire/present barriers. They share one fix (derive the real per-pass stage union) and one correctness constraint (RT passes), so they are a single co-dependent deliverable. (Merged from the former #117.) **Locations:** `implementations/Crafter.Graphics-Window.cpp:881-888` (inter-pass) and `:818-893` (acquire/present) **Severity:** medium · **Effort:** moderate · **Category:** sync ### Problem 1. **Inter-pass** (`:881-888`): between two consecutive passes a global `VkMemoryBarrier` sets *both* `srcStageMask` and `dstStageMask` to `ALL_COMMANDS`, flushing/invalidating all caches and fully serializing against every pipeline stage — when all the next pass needs is the swapchain image written by the previous one to be visible. 2. **Acquire/present** (`:818-893`): the acquire `dstStageMask` and present `srcStageMask` are also `ALL_COMMANDS`, though the swapchain image is only written by compute/RT. Both defeat the GPU's ability to overlap the tail of one pass with the head of the next, and force full cache round-trips every frame. ### Fix Replace the inter-pass global barrier with an image memory barrier scoped to the swapchain image (the codebase already does this intra-pass at `UI.cpp:264`), and narrow the acquire/present stage masks to the actual writer stages. ### Constraint (why moderate, not trivial) Passes are polymorphic: a compute pass writes via `COMPUTE_SHADER`, an RT pass via `RAY_TRACING_SHADER`. A hardcoded `COMPUTE`→`COMPUTE` mask would **under-synchronize** an RT pass and produce a correctness bug. Derive the stage mask per-pass from what each pass actually does, or use the conservative-but-tight union `COMPUTE_SHADER | RAY_TRACING_SHADER | TRANSFER` for the frame-edge barriers. These are frame-edge/per-pass barriers, so the practical bubble is modest but the per-frame cache flush is real.
jorijnvdgraaf changed title from [perf] Inter-pass barrier is ALL_COMMANDS global cache flush per frame (Window.cpp) to [perf] Frame-loop barriers use ALL_COMMANDS scope (inter-pass + acquire/present) (Window.cpp) 2026-06-17 21:21:54 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
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
Catcrafts/Crafter.Graphics#115
No description provided.