(buffer …)
Creates a GPU buffer for storing vertex data, uniforms, storage data, or indices.
Syntax
Section titled “Syntax”(buffer :name name :size 1024 :usage [vertex storage] :pool 2)A buffer whose initial contents come from a (data …) entry writes :data
instead of :size: the data sizes it.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
:name |
symbol | Yes | - | Unique buffer name |
:usage |
array | Yes | - | Usage flags, at least one |
:size |
number/expression/(define …) name |
No* | - | Size in bytes |
:data |
reference | No* | - | A (data …) entry whose bytes fill the buffer at creation, and size it |
:index-of |
reference | No* | - | Size + bytes + index-format from an indexed shape |
:file |
string | No* | - | WASM file whose exports supply size + initial bytes |
:index-format |
symbol | No | - | uint16 / uint32 when bound as an index buffer without :index-of |
:pool |
number | No | 1 |
Ping-pong buffer pool size (1 to 255) |
*A buffer has to name its size somehow: :size, or one of :data,
:index-of and :file, which size it from the bytes they carry. Naming none
of the four is a compile error that points at the form.
Type: number | expression | (define …) name
Buffer size in bytes: a non-negative integer. Can be:
- Number:
:size 1024 - Expression:
:size (* 4 256)(evaluated at compile time) (define …)name::size VERTEX_BYTES, on its own or inside an expression
A (data …) name is not one of them. Bytes and the size they imply are said
once, with :data.
(define :name VERTEX_SIZE :value 40)(define :name VERTEX_COUNT :value 100)(define :name VERTEX_BYTES :value 4000)
(buffer :name vertices :size (* VERTEX_SIZE VERTEX_COUNT) :usage [vertex])
(buffer :name morePositions :size VERTEX_BYTES :usage [vertex])A uniform buffer of fewer than 16 bytes is refused: that is WebGPU’s floor
for a uniform binding.
:usage
Section titled “:usage”Type: array of symbols
GPU buffer usage flags. At least one required.
| Flag | Description |
|---|---|
vertex |
Vertex buffer |
index |
Index buffer |
uniform |
Uniform buffer (var<uniform>) |
storage |
Storage buffer (var<storage>) |
copy-src |
Source of copy operations |
copy-dst |
Destination of copies and write-buffer ops |
indirect |
Holds indirect draw/dispatch arguments |
query-resolve |
Receives resolve-query-set results |
map-read |
Mappable for CPU readback; combines with copy-dst and nothing else |
map-write |
Mappable for CPU upload; combines with copy-src and nothing else |
A mappable buffer is host memory the GPU may only copy in or out of, so the two
map flags do not combine with vertex, storage, uniform and the rest. The
compiler reports the combination rather than leaving it to the device.
(buffer :name storageBuffer :size 4096 :usage [storage copy-dst])Type: reference
Reference to a (data …) form holding the buffer’s initial contents. The
buffer is created with those bytes already mapped, and takes its size from
them, so :size is not written beside it. copy-dst is added to the usage
flags automatically.
(data :name cubeVertices (cube :format [position4 color4 uv2]))
(buffer :name vertexBuffer :usage [vertex] :data cubeVertices)Writing a numeric :size beside :data is legal and means what it says: the
buffer is that many bytes and the data fills the front of it. A :size smaller
than the data is a compile error.
:index-of
Section titled “:index-of”Type: reference
Sources the buffer’s size, initial bytes, and index-format from an indexed
shape’s index data (teapot, dragon). This references the shape (data …)
directly; no synthetic _indices name is needed.
(data :name teapotMesh (teapot :format [position3 normal3]))
(buffer :name indexBuffer :index-of teapotMesh :usage [index]):index-format
Section titled “:index-format”Type: symbol (uint16 | uint32)
The byte width of index entries. With :index-of, the format propagates from
the shape’s index data and this key is unnecessary. Set it explicitly when a
buffer is bound as a pass’s :index-buffer without an :index-of source, for
example when a compute pass generates the indices:
(buffer :name genIndices :size 4096 :usage [index storage] :index-format uint32)Type: string (file path relative to the source)
A WASM (WebAssembly) module whose l / s / gen exports supply both the buffer’s size and
its initial bytes, so :size is not authored at all. This is the same module
convention the (pass … :data …) sugar uses, and
the sugar synthesizes such buffers for you.
(buffer :name palette :usage [storage] :file "colors.wasm")Type: number (1 to 255)
Creates multiple buffer instances for ping-pong patterns (alternating each frame between the instance being read and the instance being written). Used in compute simulations where you read from one buffer and write to another.
(buffer :name particles :size 32768 :usage [vertex storage] :pool 2)With :pool 2, the runtime creates two buffer instances. Selection uses:
actual_id = base_id + (frame_counter + pool_offset) % pool_sizeSee Ping-Pong Pattern below.
Examples
Section titled “Examples”Simple Vertex Buffer
Section titled “Simple Vertex Buffer”(buffer :name positions :size 48 :usage [vertex])Uniform Buffer with Copy
Section titled “Uniform Buffer with Copy”(buffer :name uniforms :size 16 :usage [uniform copy-dst])Shape Generator Buffer
Section titled “Shape Generator Buffer”Use compile-time shape generators for mesh data:
(data :name cubeVertices (cube :format [position4 color4 uv2]))
(buffer :name vertexBuffer :usage [vertex] :data cubeVertices):data sizes the buffer from the generated shape data, so there is no byte
count to write down or keep in step.
Indexed Mesh Buffers
Section titled “Indexed Mesh Buffers”An indexed shape produces both vertex data and an index companion. Size the
vertex buffer from the shape and the index buffer with :index-of:
(data :name teapotMesh (teapot :format [position3 normal3]))
(buffer :name vertexBuffer :usage [vertex] :data teapotMesh)
(buffer :name indexBuffer :index-of teapotMesh :usage [index])Manual Data Buffer
Section titled “Manual Data Buffer”(data :name triangleVerts :float32 [ 0.0 0.5 1 0 0 -0.5 -0.5 0 1 0 0.5 -0.5 0 0 1])
(buffer :name vertexBuffer :usage [vertex] :data triangleVerts)Compute Storage Buffer
Section titled “Compute Storage Buffer”(define :name NUM_PARTICLES :value 2048)(define :name PARTICLE_SIZE :value 16)
(buffer :name particles :size (* NUM_PARTICLES PARTICLE_SIZE) :usage [vertex storage] :pool 2)Ping-Pong Pattern
Section titled “Ping-Pong Pattern”For compute simulations requiring double-buffering:
(buffer :name particles :size 32768 :usage [vertex storage] :pool 2)
(bind-group :name simBindGroup :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 simulate :pipeline simPipeline :bind-groups [simBindGroup] :bind-groups-pool-offsets [0] (dispatch :workgroups [32]))Each frame alternates which buffer is read vs written.
Validation Rules
Section titled “Validation Rules”| Rule | Error |
|---|---|
| A name is declared once, across every form kind | duplicate_cross_ref_target, or duplicate name 'x': already declared as a (buffer …) at line N |
A builtin spelling (canvas, context-current-texture, the runtime input sources) cannot be declared |
'canvas' is a builtin symbol and cannot name a (buffer …) |
:usage required |
missing_required_key |
:usage needs at least one flag |
vector_too_short |
:data / :index-of must reference a (data …) |
not_cross_ref |
:size must be a non-negative integer, or a declared (define …) name |
number_below_min, not_cross_ref |
:pool must be in [1,255] |
number_below_min / number_above_max |
| The buffer has to name its size | buffer 'b' names no size: give it :size …, or :data …, or :index-of …, or :file … |
:size may not be smaller than :data |
buffer 'b' size (N bytes) is smaller than data 'd' (M bytes) |
A uniform buffer is at least 16 bytes |
uniform buffer 'b' size (N bytes) is below WebGPU's 16-byte uniform floor |
map-read combines only with copy-dst, map-write only with copy-src |
buffer 'b' is :usage [map-read … storage …] |
| A copy endpoint carries the usage the copy needs | buffer 'b' is the :source of a copy but its :usage has no copy-src |
The rows with a code are schema checks; the rows with a message are compiler checks that read a value rather than a shape, and they report the line the form is written on.
WebGPU Mapping
Section titled “WebGPU Mapping”Maps to GPUBuffer:
device.createBuffer({ size: size, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE, mappedAtCreation: hasMappedData});Related
Section titled “Related”(data …)- Define buffer data and shape generators(bind-group …)- Bind buffers to shaders(render-pass …)- Use as vertex buffer(queue …)- Write data to buffer at runtime