(render-pass …)
Defines a render pass that configures render targets and issues draw commands.
Syntax
Section titled “Syntax”(render-pass :name name (color-attachment :view context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op store) (depth-stencil-attachment :view depthTexture :depth-clear-value 1.0 :depth-load-op clear :depth-store-op store) :pipeline pipelineName :bind-groups [group0 group1] :vertex-buffers [vertices] (draw :vertex-count 3))Keys & Sub-forms
Section titled “Keys & Sub-forms”| Key / Sub-form | Type | Required | Default | Description |
|---|---|---|---|---|
:name |
symbol | Yes | - | Pass name |
(color-attachment …) |
sub-form | † | - | Color render targets (repeat for multiple render targets) |
(depth-stencil-attachment …) |
sub-form | † | - | Depth/stencil target (at most one) |
:pipeline |
reference | * | - | Render pipeline |
:bind-groups |
array | No | [] |
Bind groups |
:bind-groups-pool-offsets |
array | No | [] |
Pool offsets for bind groups |
:vertex-buffers |
array | No | [] |
Vertex buffers |
:vertex-buffers-pool-offsets |
array | No | [] |
Pool offsets for vertex buffers |
:index-buffer |
reference | No | - | Index buffer |
:stencil-reference |
number | No | - | Stencil reference value |
:blend-constant |
array | No | [0 0 0 0] |
[r g b a] value the constant blend factors multiply by |
:viewport |
array | No | full target | [x y w h] or [x y w h minDepth maxDepth] |
:scissor-rect |
array | No | full target | [x y w h] in integer pixels |
:occlusion-query-set |
reference | No | - | (query-set :type occlusion) the pass writes |
:execute-bundles |
array | * | - | (render-bundle …) forms to replay |
(draw …) |
sub-form | * | - | Draw call parameters |
(draw-indexed …) |
sub-form | * | - | Indexed draw parameters |
(draw-indirect …) |
sub-form | * | - | Non-indexed draw with GPU-supplied args |
(draw-indexed-indirect …) |
sub-form | * | - | Indexed draw with GPU-supplied args |
(occlusion-query …) |
sub-form | No | - | Brackets draws in an occlusion query |
(timestamp-writes …) |
sub-form | No | - | Records GPU timestamps at pass start/end (at most one) |
† A pass needs at least one attachment, of either kind. WebGPU cannot begin a render pass that writes nowhere, so a pass with none is refused by the compiler with the pass named.
*None of the starred keys is required on its own. The usual pass declares a
:pipeline and at least one draw sub-form, or replays pre-recorded work via
:execute-bundles (the bundle carries its own pipeline and draw). A pass with
attachments and nothing else is legal too: it is a clear-only pass, and it
compiles. A pass may issue several draws; they run in the order they are
written.
(color-attachment …)
Section titled “(color-attachment …)”Repeat the sub-form once per color target for multiple render targets (MRT). Each color attachment:
| Key | Type | Required | Description |
|---|---|---|---|
:view |
reference | Yes | A (texture …), or context-current-texture (the canvas’s current texture) |
:resolve-target |
reference | No | MSAA resolve target |
:clear-value |
array | No | Clear color [r g b a], exactly four components |
:load-op |
symbol | Yes | clear or load |
:store-op |
symbol | Yes | store or discard |
:load-op and :store-op are required. WebGPU defaults neither, so a pass
that does not say whether it clears or loads is a pass whose contents depend on
whatever was left in the texture.
:clear-value is optional because WebGPU does default it (to transparent
black), but it belongs with a clear: stating one beside :load-op load is an
error, since WebGPU loads the existing contents and never reads the value.
(color-attachment :view context-current-texture :clear-value [0.1 0.1 0.1 1.0] :load-op clear :store-op store)(depth-stencil-attachment …)
Section titled “(depth-stencil-attachment …)”Depth and stencil attachment. At most one per pass.
| Key | Type | Required | Description |
|---|---|---|---|
:view |
reference | Yes | The depth/stencil (texture …) this pass tests and writes |
:depth-clear-value |
number | Conditional | Depth cleared to, in [0,1] |
:depth-load-op |
symbol | Conditional | clear or load |
:depth-store-op |
symbol | Conditional | store or discard |
:stencil-clear-value |
number | Conditional | Stencil cleared to |
:stencil-load-op |
symbol | Conditional | clear or load |
:stencil-store-op |
symbol | Conditional | store or discard |
Each aspect’s op pair is required exactly when the view’s format carries that
aspect, and rejected when it does not. A depth24plus view takes
:depth-load-op and :depth-store-op and no stencil ops; a stencil8 view
takes the stencil pair and no depth ops; a depth24plus-stencil8 view takes
both pairs. The rule reads the (texture …)’s :format, so the compiler
reports it on the attachment and names the view:
the depth/stencil attachment of render-pass 'draw' states :stencil-load-op,but its view 'depthTex' is :format 'depth24plus', which has no stencil aspectA clear value belongs with a clear. :depth-clear-value is required when
:depth-load-op is clear, because WebGPU has no default for it, and stating
one beside :depth-load-op load is an error rather than inert input. The
stencil pair works the same way.
:pipeline
Section titled “:pipeline”Reference to a (render-pipeline …). A bare identifier resolves as a
cross-reference.
:pipeline myPipeline:bind-groups
Section titled “:bind-groups”Bind groups in order matching @group(N):
:bind-groups [frameBindGroup materialBindGroup objectBindGroup]:bind-groups-pool-offsets
Section titled “:bind-groups-pool-offsets”Pool offsets for ping-pong bind groups. A :pool N on a (bind-group …) or a
(buffer …) creates N sequential ids that the runtime rotates through each
frame, which is the ping-pong pattern: one frame reads what the last frame
wrote. Each offset here corresponds to a bind group.
:bind-groups [simBindGroup]:bind-groups-pool-offsets [0] ; Alternates each frame:vertex-buffers
Section titled “:vertex-buffers”Vertex buffers in order matching vertex stage buffer indices:
:vertex-buffers [positionBuffer normalBuffer uvBuffer]:vertex-buffers-pool-offsets
Section titled “:vertex-buffers-pool-offsets”Pool offsets for ping-pong vertex buffers:
:vertex-buffers [particleBuffer]:vertex-buffers-pool-offsets [1] ; Use the "other" buffer(draw …)
Section titled “(draw …)”Non-indexed draw call:
| Key | Type | Default | Description |
|---|---|---|---|
:vertex-count |
number | required | Vertices to draw |
:instance-count |
number | 1 |
Instances |
:first-vertex |
number | 0 |
Offset into the vertex buffers, in vertices |
:first-instance |
number | 0 |
First instance index |
(draw :vertex-count 3)(draw :vertex-count 36 :instance-count 100)(draw-indexed …)
Section titled “(draw-indexed …)”Indexed draw call. It reads the pass’s :index-buffer. The index format
(uint16 / uint32) comes from that (buffer …): from its :index-of
source for a generated shape’s index companion, or from its own
:index-format key when nothing sources it.
| Key | Type | Default | Description |
|---|---|---|---|
:index-count |
number | required | Indices to draw |
:instance-count |
number | 1 |
Instances |
:first-index |
number | 0 |
Offset into the index buffer, in indices |
:base-vertex |
number | 0 |
Added to each index before vertex lookup |
:first-instance |
number | 0 |
First instance index |
:index-buffer indices(draw-indexed :index-count 36)(draw-indirect …) / (draw-indexed-indirect …)
Section titled “(draw-indirect …) / (draw-indexed-indirect …)”Reads the draw arguments from a GPU buffer rather than from the PNGB bytecode
(the compiled payload). The buffer is typically written by a compute pass, so
the GPU decides how much to draw.
draw-indirect reads GPUDrawIndirectArgs (4 × u32: vertex-count,
instance-count, first-vertex, first-instance); draw-indexed-indirect reads
GPUDrawIndexedIndirectArgs (5 × u32) and requires the pass’s :index-buffer.
| Key | Type | Default | Description |
|---|---|---|---|
:buffer |
reference | required | Buffer holding the args (needs indirect usage) |
:offset |
number | 0 |
Byte offset of the args within the buffer |
(buffer :name drawArgs :size 16 :usage [storage indirect])
(render-pass :name draw (color-attachment :view context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op store) :pipeline pipe (draw-indirect :buffer drawArgs :offset 0)):viewport and :scissor-rect
Section titled “:viewport and :scissor-rect”:viewport takes four numbers (minDepth and maxDepth default to 0 and 1)
or all six. :scissor-rect discards fragments outside its rectangle.
(render-pass :name halfScreen (color-attachment :view context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op store) :pipeline pipe :viewport [0 0 256 512] :scissor-rect [0 0 256 512] (draw :vertex-count 3)):blend-constant and :stencil-reference
Section titled “:blend-constant and :stencil-reference”:blend-constant is the RGBA value that a target’s constant /
one-minus-constant blend factors multiply by (setBlendConstant); it defaults
to [0 0 0 0]. :stencil-reference is the value the pipeline’s stencil
:compare tests against and that the replace stencil operation writes.
:blend-constant [0.5 0.5 0.5 1]:stencil-reference 1Queries
Section titled “Queries”:occlusion-query-set plus (occlusion-query …) brackets count the samples a
draw passes; (timestamp-writes …) records GPU timestamps at pass start and end.
Both are covered on the (query-set …) page.
(render-pass :name draw (color-attachment :view context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op store) (timestamp-writes :query-set ts :beginning-of-pass-write-index 0 :end-of-pass-write-index 1) :pipeline pipe :occlusion-query-set occ (occlusion-query :query-index 0 (draw :vertex-count 36)))Examples
Section titled “Examples”Simple Triangle
Section titled “Simple Triangle”(render-pass :name drawTriangle (color-attachment :view context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op store) :pipeline trianglePipeline (draw :vertex-count 3))With Depth Buffer
Section titled “With Depth Buffer”(texture :name depthTexture :size canvas :format depth24plus :usage [render-attachment])
(render-pass :name render3D (color-attachment :view context-current-texture :clear-value [0.1 0.1 0.2 1] :load-op clear :store-op store) (depth-stencil-attachment :view depthTexture :depth-clear-value 1.0 :depth-load-op clear :depth-store-op store) :pipeline meshPipeline :bind-groups [uniforms materials] :vertex-buffers [vertices] :index-buffer indices (draw-indexed :index-count 1000))MSAA with Resolve
Section titled “MSAA with Resolve”(define :name SAMPLE_COUNT :value 4)
(texture :name msaaTarget :size canvas :format preferred-canvas-format :usage [render-attachment] :sample-count (* SAMPLE_COUNT 1))
(render-pass :name msaaPass (color-attachment :view msaaTarget :resolve-target context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op discard) :pipeline msaaPipeline (draw :vertex-count 3))Instanced Drawing
Section titled “Instanced Drawing”(render-pass :name drawParticles (color-attachment :view context-current-texture :load-op load :store-op store) :pipeline particlePipeline :bind-groups [uniforms] :vertex-buffers [quadVerts particleData] :vertex-buffers-pool-offsets [0 1] (draw :vertex-count 6 :instance-count 2048))Ping-Pong Rendering
Section titled “Ping-Pong Rendering”(buffer :name particles :size 131072 :usage [vertex storage] :pool 2)
(render-pass :name drawParticles (color-attachment :view context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op store) :pipeline renderPipeline :vertex-buffers [particles] :vertex-buffers-pool-offsets [0] ; Read from current frame's buffer (draw :vertex-count 6 :instance-count 2048))Render to Texture
Section titled “Render to Texture”(texture :name offscreen :size [1024 1024] :format rgba8unorm :usage [render-attachment texture-binding])
(render-pass :name offscreenPass (color-attachment :view offscreen :clear-value [0 0 0 0] :load-op clear :store-op store) :pipeline scenePipeline (draw :vertex-count 1000))Validation Rules
Section titled “Validation Rules”| Rule | Error |
|---|---|
| A name is unique across every form kind | duplicate_cross_ref_target |
| Referenced resources must exist | not_cross_ref |
(color-attachment …) states :load-op and :store-op |
missing_required_key |
:clear-value must have exactly 4 components |
vector_length_mismatch |
:scissor-rect takes exactly 4 numbers |
vector_length_mismatch |
:viewport takes 4 numbers or 6 |
union_no_branch_matched |
| A sub-form head the pass does not accept | not_head_member |
At most one (depth-stencil-attachment …) or (timestamp-writes …) |
positional_too_many |
Three more are the compiler’s, reading values no schema can see. They carry a located message and no code:
| Rule | Message shape |
|---|---|
| A pass needs at least one attachment | render-pass 'draw' has no (color-attachment …) and no (depth-stencil-attachment …) |
A clear value belongs with a clear |
color attachment 0 of render-pass 'draw' states a :clear-value beside ':load-op load' |
(occlusion-query …) needs :occlusion-query-set |
render-pass 'draw' brackets draws in an (occlusion-query …) but states no :occlusion-query-set |
WebGPU Mapping
Section titled “WebGPU Mapping”Maps to render pass encoding:
const passEncoder = commandEncoder.beginRenderPass({ colorAttachments: [{ view: texture.createView(), clearValue: { r: 0, g: 0, b: 0, a: 1 }, loadOp: 'clear', storeOp: 'store' }]});passEncoder.setPipeline(pipeline);passEncoder.setBindGroup(0, bindGroup);passEncoder.setVertexBuffer(0, vertexBuffer);passEncoder.draw(3);passEncoder.end();Related
Section titled “Related”(render-pipeline …)- Pipeline configuration(bind-group …)- Resource bindings(buffer …)- Vertex/index/indirect buffers(texture …)- Render targets(render-bundle …)- Pre-recorded draws for:execute-bundles(query-set …)- Occlusion and timestamp queries(frame …)- Execute passes