Render Passes and Bundles
Render passes
A render pass draws into its attachments. Record one inside a frame:
(gfx:with-render-pass (pass frame :color ((texture :clear '(0 0 0 1)))
:depth (depth-texture :clear 1.0))
(gfx:set-pipeline pass pipeline)
(gfx:set-bind-group pass 0 group)
(gfx:set-vertex-buffer pass 0 vertices)
(gfx:set-index-buffer pass indices :uint16)
(gfx:draw-indexed pass 36))
with-render-pass ends the pass however its body is left. Its attachment
lists are written out in the macro, not evaluated at run time, so beginning a
pass allocates nothing. begin-render-pass and end-render-pass take the
same attachments as lists built at run time.
Only one pass, render or compute, is open at a time.
Attachments
:color lists the color attachments, in the order of the pipeline's
:color-formats. Each is:
(texture &key clear level layer resolve)
| Key | Meaning |
|---|---|
:clear |
A color, a list or vector of four reals (red green blue alpha). The texture is cleared to it first. Without it, the texture's contents are kept. |
:level |
The mip level drawn into, by default 0. |
:layer |
The layer drawn into, by default 0. Counts the faces of a cube, six per cube. |
:resolve |
A texture of one sample that this multisampled texture is resolved into when the pass ends. |
:depth is one attachment, (texture &key clear level layer), where :clear
is a depth, usually 1.0. The texture must have a depth format.
Every attachment needs :render-target usage. The attachments are as large
as the first one's level, and the viewport and scissor start out covering
it.
;; Render the six faces of a cube map
(dotimes (face 6)
(gfx:with-render-pass (pass frame :color ((cube :clear '(0 0 0 1) :layer face)))
(draw-face pass face)))
;; Draw into a multisampled texture and resolve it
(gfx:with-render-pass (pass frame :color ((samples :clear '(0 0 0 1) :resolve image)))
...)
Draw commands
All of these record into the pass and allocate nothing.
| Function | Does |
|---|---|
(set-pipeline pass pipeline) |
Draw with pipeline from here on. Bind groups already set stay set. |
(set-bind-group pass index group) |
Use group as bind group index. See Bind Groups. |
(set-vertex-buffer pass slot buffer &optional offset) |
Read vertex buffer slot, as the pipeline's :vertex-buffers numbers them, from buffer at offset bytes. |
(set-index-buffer pass buffer format &optional offset) |
Read indices of format, :uint16 or :uint32, from buffer, for draw-indexed. |
(set-viewport pass x y width height &key min-depth max-depth) |
Draw into this rectangle of the attachments, with depths from min-depth (0.0) to max-depth (1.0), both from 0 to 1. (0, 0) is the top left. |
(set-scissor pass x y width height) |
Draw only inside this rectangle, in pixels. |
(draw pass vertex-count &key instance-count first-vertex first-instance) |
Draw vertex-count vertices, instance-count (1) times. |
(draw-indexed pass index-count &key instance-count first-index base-vertex first-instance) |
Draw index-count indexed vertices, adding base-vertex to each index. |
(execute-render-bundle pass bundle) |
Execute a render bundle (below). |
Instancing
;; Pipeline with a per-instance buffer in slot 1 (:step :instance)
(gfx:set-vertex-buffer pass 0 quad-vertices)
(gfx:set-vertex-buffer pass 1 sprite-instances)
(gfx:draw pass 6 :instance-count sprite-count)
A full-screen pass
A vertex shader that makes its own triangle needs no vertex buffer:
(gfx:with-render-pass (pass frame :color ((texture)))
(gfx:set-pipeline pass post-process)
(gfx:set-bind-group pass 0 scene-group)
(gfx:draw pass 3))
Pass order and synchronisation
Passes run in the order they are recorded, and each sees what the ones before it wrote. The backend tracks each buffer and texture and records exactly the barriers a pass needs before it begins. A shadow map drawn in one pass can be sampled in the next; a compute pass can fill a vertex buffer that the next render pass draws.
Within one pass, a texture cannot be both an attachment and read by shaders, nor both sampled and stored to. These are refused when recorded; see Bind Groups.
Render bundles
A render bundle is a list of draw commands recorded once and executed in render passes of later frames. Use it for what does not change from frame to frame.
(let ((bundle (gfx:create-render-bundle device)))
(gfx:record-render-bundle (encoder bundle)
(gfx:set-pipeline encoder pipeline)
(gfx:set-bind-group encoder 0 group)
(gfx:set-vertex-buffer encoder 0 vertices)
(gfx:draw encoder 3))
;; Then, in each frame's pass:
(gfx:execute-render-bundle pass bundle))
record-render-bundlebinds an encoder, which takes the same draw commands as a pass, and replaces what the bundle held. It may be recorded outside a frame.- A bundle starts with nothing set: it sets its own pipeline, bind groups and buffers.
- What it sets does not last after it. After
execute-render-bundle, the pass sets its pipeline and buffers again before drawing. - A bundle executes only in a render pass, not inside another bundle.
- The pipeline and attachments of a bundle's draws must match the pass it executes in, as for draws recorded directly.
- The objects a bundle refers to must outlive it.
destroy-render-bundledestroys it; a frame that already executed it still draws it. Closing the device destroys the bundles left.
Recording rules
- Draw commands are accepted only while a pass or a bundle is open. Outside,
they signal
gfx-error. - The pass object is reused for every pass of the frame. Don't keep it after the pass ends.
- A compute pipeline cannot be set in a render pass, nor a render pipeline in a compute pass.