(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 | † | - | Colour 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 colour target for multiple render targets (MRT). Each colour 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 colour [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();Where these words come from
Section titled “Where these words come from”Every key and value on this page traced to the WebGPU name it stands for, with a link to the definition. How the tracing is made, and what keeps it from rotting, is the subject of Where the Words Come From.
(render-pass …) is GPURenderPassDescriptor (MDN) handed to GPUCommandEncoder.beginRenderPass(). The form is both the descriptor and the commands recorded on the encoder the call returns, so its keys come from two places in the spec.
| Key | WebGPU | Note |
|---|---|---|
:name |
GPUObjectDescriptorBase.label |
|
:occlusion-query-set |
GPURenderPassDescriptor.occlusionQuerySet |
|
:pipeline |
GPURenderCommandsMixin.setPipeline() |
|
:vertex-buffers |
GPURenderCommandsMixin.setVertexBuffer() |
|
:vertex-buffers-pool-offsets |
PNGine’s own | which instance of a pooled buffer each slot binds this frame |
:index-buffer |
GPURenderCommandsMixin.setIndexBuffer() |
|
:bind-groups |
GPUBindingCommandsMixin.setBindGroup() |
|
:bind-groups-pool-offsets |
PNGine’s own | which instance of a pooled bind group each slot binds this frame |
:stencil-reference |
GPURenderPassEncoder.setStencilReference() |
|
:blend-constant |
GPURenderPassEncoder.setBlendConstant() |
|
:viewport |
GPURenderPassEncoder.setViewport() |
|
:scissor-rect |
GPURenderPassEncoder.setScissorRect() |
|
:execute-bundles |
GPURenderPassEncoder.executeBundles() |
|
(color-attachment …) |
GPURenderPassDescriptor.colorAttachments |
a positional child, not a key |
(depth-stencil-attachment …) |
GPURenderPassDescriptor.depthStencilAttachment |
a positional child, not a key |
(timestamp-writes …) |
GPURenderPassDescriptor.timestampWrites |
a positional child, not a key |
(color-attachment …) mirrors GPURenderPassColorAttachment (MDN).
| Key | WebGPU | Note |
|---|---|---|
:view |
GPURenderPassColorAttachment.view |
|
:resolve-target |
GPURenderPassColorAttachment.resolveTarget |
|
:clear-value |
GPURenderPassColorAttachment.clearValue |
|
:load-op |
GPURenderPassColorAttachment.loadOp |
|
:store-op |
GPURenderPassColorAttachment.storeOp |
Not expressible in PNGine yet: GPURenderPassColorAttachment.depthSlice.
(depth-stencil-attachment …) mirrors GPURenderPassDepthStencilAttachment (MDN).
| Key | WebGPU | Note |
|---|---|---|
:view |
GPURenderPassDepthStencilAttachment.view |
|
:depth-clear-value |
GPURenderPassDepthStencilAttachment.depthClearValue |
|
:depth-load-op |
GPURenderPassDepthStencilAttachment.depthLoadOp |
|
:depth-store-op |
GPURenderPassDepthStencilAttachment.depthStoreOp |
|
:stencil-clear-value |
GPURenderPassDepthStencilAttachment.stencilClearValue |
|
:stencil-load-op |
GPURenderPassDepthStencilAttachment.stencilLoadOp |
|
:stencil-store-op |
GPURenderPassDepthStencilAttachment.stencilStoreOp |
Not expressible in PNGine yet: GPURenderPassDepthStencilAttachment.depthReadOnly, GPURenderPassDepthStencilAttachment.stencilReadOnly.
(timestamp-writes …) mirrors GPURenderPassTimestampWrites (MDN).
| Key | WebGPU | Note |
|---|---|---|
:query-set |
GPURenderPassTimestampWrites.querySet |
|
:beginning-of-pass-write-index |
GPURenderPassTimestampWrites.beginningOfPassWriteIndex |
|
:end-of-pass-write-index |
GPURenderPassTimestampWrites.endOfPassWriteIndex |
(occlusion-query …) is a call to GPURenderPassEncoder.beginOcclusionQuery() (MDN); each key is one argument.
| Key | Argument | Note |
|---|---|---|
:query-index |
queryIndex |
The draws inside the form run between beginOcclusionQuery() and endOcclusionQuery().
(draw …) is a call to GPURenderCommandsMixin.draw() (MDN); each key is one argument.
| Key | Argument | Note |
|---|---|---|
:vertex-count |
vertexCount |
|
:instance-count |
instanceCount |
|
:first-vertex |
firstVertex |
|
:first-instance |
firstInstance |
(draw-indexed …) is a call to GPURenderCommandsMixin.drawIndexed() (MDN); each key is one argument.
| Key | Argument | Note |
|---|---|---|
:index-count |
indexCount |
|
:instance-count |
instanceCount |
|
:first-index |
firstIndex |
|
:base-vertex |
baseVertex |
|
:first-instance |
firstInstance |
(draw-indirect …) is a call to GPURenderCommandsMixin.drawIndirect() (MDN); each key is one argument.
| Key | Argument | Note |
|---|---|---|
:buffer |
indirectBuffer |
|
:offset |
indirectOffset |
(draw-indexed-indirect …) is a call to GPURenderCommandsMixin.drawIndexedIndirect() (MDN); each key is one argument.
| Key | Argument | Note |
|---|---|---|
:buffer |
indirectBuffer |
|
:offset |
indirectOffset |
Values
Section titled “Values”load-op (:load-op on (color-attachment …), :depth-load-op on (depth-stencil-attachment …), :stencil-load-op on (depth-stencil-attachment …)) spells the GPULoadOp enum.
| Value | WebGPU | Note |
|---|---|---|
clear |
"clear" |
|
load |
"load" |
store-op (:store-op on (color-attachment …), :depth-store-op on (depth-stencil-attachment …), :stencil-store-op on (depth-stencil-attachment …)) spells the GPUStoreOp enum.
| Value | WebGPU | Note |
|---|---|---|
store |
"store" |
|
discard |
"discard" |
Checked against the WebGPU specification at revision b8c0fa9; the links go to the current draft.
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