(compute-pass …)
Defines a compute pass that dispatches compute shader workgroups.
Syntax
Section titled “Syntax”(compute-pass :name name :pipeline pipelineName :bind-groups [group0 group1] :bind-groups-pool-offsets [0] (dispatch :workgroups [64 1 1]))Keys & Sub-forms
Section titled “Keys & Sub-forms”| Key / Sub-form | Type | Required | Default | Description |
|---|---|---|---|---|
:name |
symbol | Yes | - | Pass name |
:pipeline |
reference | Yes | - | Compute pipeline |
:bind-groups |
array | No | [] |
Bind groups |
:bind-groups-pool-offsets |
array | No | [] |
Pool offsets |
(dispatch …) |
sub-form | * | - | Dispatch a workgroup grid stated in the document |
(dispatch-indirect …) |
sub-form | * | - | Dispatch a workgroup grid the GPU wrote into a buffer |
*A pass issues at least one dispatch command, of either kind. A pass that
dispatches nothing is refused (positional_missing). Several are allowed, and
they run in the order they are written.
:pipeline
Section titled “:pipeline”Reference to a (compute-pipeline …). A bare identifier resolves as a
cross-reference.
:pipeline simPipeline:bind-groups
Section titled “:bind-groups”Bind groups in order matching @group(N):
:bind-groups [particleGroup uniformGroup]:bind-groups-pool-offsets
Section titled “:bind-groups-pool-offsets”Pool offsets for ping-pong bind groups. A :pool N on a (bind-group …)
creates N sequential ids and the runtime rotates through them each frame, which
is the ping-pong pattern: one frame reads what the last frame wrote. Each
offset here corresponds to a bind group in the :bind-groups array.
:bind-groups [simBindGroup]:bind-groups-pool-offsets [0] ; Alternates each frameThe actual bind group used is:
actual_id = base_id + (frame_counter + offset) % pool_size(dispatch …)
Section titled “(dispatch …)”| Key | Type | Required | Description |
|---|---|---|---|
:workgroups |
array | Yes | Workgroup counts as [x], [x y] or [x y z] |
The missing dimensions default to 1, so [64] dispatches (64, 1, 1). The
vector is the only spelling: there is no bare :workgroups 64.
(dispatch :workgroups [64])(dispatch :workgroups [8 8 1])(dispatch :workgroups [4 4 4])Each element is a literal, a bare (define …) constant, or a bounded
expression over those constants:
(define :name NUM_PARTICLES :value 2048)(define :name WORKGROUP_SIZE :value 64)
(dispatch :workgroups [(ceil (/ NUM_PARTICLES WORKGROUP_SIZE))])(dispatch :workgroups [WORKGROUP_SIZE 1 1])Total threads = workgroups * workgroup_size (from the shader).
(dispatch-indirect …)
Section titled “(dispatch-indirect …)”| Key | Type | Required | Default | Description |
|---|---|---|---|---|
:buffer |
reference | Yes | - | Buffer holding the dispatch args (needs indirect usage) |
:offset |
number | No | 0 |
Byte offset of the args within that buffer |
Reads the three workgroup counts (3 × u32) from a GPU buffer instead of baking
them into the PNGB bytecode (the compiled payload); the counts are typically
written by an earlier compute pass, so the GPU decides its own dispatch size
without a CPU round trip.
The buffer needs indirect usage (plus whatever writes it):
(buffer :name dispatchArgs :size 12 :usage [storage indirect])
; An earlier pass writes [x y z] into dispatchArgs …(compute-pass :name plan :pipeline planPipeline :bind-groups [planGroup] (dispatch :workgroups [1]))
; … and this one reads them.(compute-pass :name work :pipeline workPipeline :bind-groups [workGroup] (dispatch-indirect :buffer dispatchArgs :offset 0))
(frame :name main :perform [plan work])Examples
Section titled “Examples”Basic Compute
Section titled “Basic Compute”(compute-pass :name process :pipeline processPipeline :bind-groups [dataBindGroup] (dispatch :workgroups [16 1 1]))2D Dispatch (Image Processing)
Section titled “2D Dispatch (Image Processing)”(compute-pass :name blur :pipeline blurPipeline :bind-groups [imageBindGroup] (dispatch :workgroups [64 64 1])) ; 512x512 with 8x8 workgroupsWith Expression
Section titled “With Expression”(define :name NUM_PARTICLES :value 4096)(define :name WORKGROUP_SIZE :value 64)
(compute-pass :name simulate :pipeline simPipeline :bind-groups [particleGroup] (dispatch :workgroups [(ceil (/ NUM_PARTICLES WORKGROUP_SIZE))]))Ping-Pong Simulation
Section titled “Ping-Pong Simulation”(buffer :name particles :size 65536 :usage [storage] :pool 2)
(bind-group :name simGroup :layout simPipeline :group 0 :pool 2 (entry :binding 0 :buffer particles :ping-pong 0) (entry :binding 1 :buffer particles :ping-pong 1))
(compute-pass :name update :pipeline simPipeline :bind-groups [simGroup] :bind-groups-pool-offsets [0] (dispatch :workgroups [64 1 1]))
(frame :name main :perform [update render])Each frame:
- Frame 0: Read from
particles_0, write toparticles_1 - Frame 1: Read from
particles_1, write toparticles_0 - Frame 2: Read from
particles_0, write toparticles_1 - etc.
Multiple Compute Passes
Section titled “Multiple Compute Passes”(compute-pass :name integrate :pipeline integratePipeline :bind-groups [physicsGroup] :bind-groups-pool-offsets [0] (dispatch :workgroups [64 1 1]))
(compute-pass :name collide :pipeline collidePipeline :bind-groups [physicsGroup] :bind-groups-pool-offsets [0] (dispatch :workgroups [64 1 1]))
(frame :name physics :perform [integrate collide render])Validation Rules
Section titled “Validation Rules”| Rule | Error |
|---|---|
| A name is unique across every form kind | duplicate_cross_ref_target |
:pipeline key is required |
missing_required_key |
At least one (dispatch …) / (dispatch-indirect …) child |
positional_missing |
| A sub-form head the pass does not accept | not_head_member |
:workgroups takes 1 to 3 elements |
vector_too_long |
| Referenced resources must exist | not_cross_ref |
A misspelt (define …) name in a count |
not_cross_ref |
The empty-pass rule reads:
form `compute-pass` requires at least 1 positional childfrom [dispatch | dispatch-indirect], found 0WebGPU Mapping
Section titled “WebGPU Mapping”Maps to compute pass encoding:
const passEncoder = commandEncoder.beginComputePass();passEncoder.setPipeline(pipeline);passEncoder.setBindGroup(0, bindGroup);passEncoder.dispatchWorkgroups(64, 1, 1);// or, with (dispatch-indirect …)// passEncoder.dispatchWorkgroupsIndirect(argsBuffer, 0);passEncoder.end();Related
Section titled “Related”(compute-pipeline …)- Pipeline configuration(bind-group …)- Resource bindings(buffer …)- Storage buffers(frame …)- Execute passes