(bind-group …)
Creates a bind group that binds GPU resources (buffers, textures, samplers) to shader binding slots.
Syntax
Section titled “Syntax”(bind-group :name groupName :layout pipelineName :group 0 :pool 2 (entry :binding 0 :buffer bufferName) (entry :binding 1 :texture textureName) (entry :binding 2 :sampler samplerName))Keys & Sub-forms
Section titled “Keys & Sub-forms”| Key / Sub-form | Type | Required | Default | Description |
|---|---|---|---|---|
:name |
symbol | Yes | - | Bind group name |
:layout |
reference | Yes | - | The layout this group is built against |
:group |
number | No | 0 |
Which @group index of a pipeline’s layout it binds |
:pool |
number | No | - | Ping-pong pool size (1 to 255); absent means a single bind group |
(entry …) |
sub-form | No | - | Resource bindings, one per slot |
Layout source
Section titled “Layout source”:layout is one key, mirroring GPUBindGroupDescriptor.layout, which is one
member. Its value names either a pipeline, whose auto-derived layout the group
is built against, or an explicit
(bind-group-layout …).
Derived from a pipeline’s layout:
:layout renderPipeline :group 0Explicit bind group layout reference:
:layout myLayout:group is @group(N) as the WGSL spells it, and it pairs with the entries’
:binding. It is meaningful for the pipeline arm only: that arm resolves the
layout as pipeline.getBindGroupLayout(:group), while an explicit
(bind-group-layout …) is the layout and an index selects nothing there.
Stating :group 0 beside one changes nothing: the document compiles to the
same bytes, with no diagnostic. Any other index warns (bind-group 'g' names an explicit (bind-group-layout …) as its :layout, so :group 1 selects nothing),
and because the index is still applied to the WGSL match, a binding the shader
uses at @group(0) is then reported as never bound and the compile fails.
A render pipeline, a compute pipeline and a bind-group layout are one namespace,
so the name resolves to exactly one form: a collision between two of those kinds
is a duplicate_cross_ref_target at the second declaration rather than a guess
here.
(entry …) entries
Section titled “(entry …) entries”Each (entry …) sub-form binds one resource to a binding index:
| Key | Type | Required | Description |
|---|---|---|---|
:binding |
number | Yes | Binding index |
:buffer |
reference | * | Buffer reference |
:sampler |
reference | * | Sampler reference |
:texture |
reference | * | Texture reference (bound via its default 2d view) |
:texture-view |
reference | * | Explicit (texture-view …) reference (for 1d/3d/array/cube sampling) |
:offset |
number | No | Byte offset of the bound slice into :buffer (default 0) |
:size |
number | No | Byte size of the bound slice (default: the rest of :buffer) |
:ping-pong |
number | No | Pool offset for ping-pong (see :pool below) |
*Exactly one of :buffer, :sampler, :texture, or :texture-view per entry.
None is required_one_of_missing, two is mutually_exclusive_keys_present.
(bind-group :name materials :layout renderPipeline :group 1 (entry :binding 0 :buffer uniformBuffer) (entry :binding 1 :texture colorTexture) (entry :binding 2 :sampler linearSampler))Binding a slice of a buffer
Section titled “Binding a slice of a buffer”:offset and :size bind a sub-range of a buffer instead of the whole thing:
the GPUBufferBinding offset/size pair. Both require :buffer (stating either
on a sampler or texture entry is a located dependent_key_missing), and
:offset + :size must fit inside the declared buffer size, which the compiler
checks and reports as (entry …) :offset/:size slice ends at byte N, past the end of buffer 'b'.
(buffer :name scene :size 512 :usage [uniform copy-dst])
(bind-group :name perObject :layout pipe :group 0 (entry :binding 0 :buffer scene :offset 256 :size 16))WebGPU’s dynamic-offset alignment rules apply: 256 bytes for uniform bindings, 256 for storage bindings by default.
Creates multiple bind group instances for ping-pong patterns, where a resource
is read from one slot and written to another and the two swap each frame. Used
with pooled buffers or textures; each (entry …) selects its read/write slot via
:ping-pong, and the runtime resolves the actual id as
base + (frame + ping-pong) % pool.
(bind-group :name particleBindGroup :layout simPipeline :group 0 :pool 2 (entry :binding 0 :buffer particles :ping-pong 0) (entry :binding 1 :buffer particles :ping-pong 1))Examples
Section titled “Examples”Simple Uniform Binding
Section titled “Simple Uniform Binding”(buffer :name uniforms :size 64 :usage [uniform copy-dst])
(bind-group :name mainBindGroup :layout mainPipeline :group 0 (entry :binding 0 :buffer uniforms))Texture and Sampler
Section titled “Texture and Sampler”(texture :name diffuse :size [512 512] :format rgba8unorm :usage [texture-binding])
(sampler :name linearSampler :mag-filter linear :min-filter linear)
(bind-group :name materials :layout renderPipeline :group 1 (entry :binding 0 :texture diffuse) (entry :binding 1 :sampler linearSampler))Explicit Bind Group Layout
Section titled “Explicit Bind Group Layout”(bind-group-layout :name layout0 (entry :binding 0 :visibility [vertex fragment] (buffer :type uniform)))
(bind-group :name group0 :layout layout0 (entry :binding 0 :buffer uniforms))Ping-Pong Compute
Section titled “Ping-Pong Compute”(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 simulate :pipeline simPipeline :bind-groups [simGroup] :bind-groups-pool-offsets [0] (dispatch :workgroups [64 1 1]))Multiple Bind Groups
Section titled “Multiple Bind Groups”(bind-group :name perFrame :layout renderPipeline :group 0 (entry :binding 0 :buffer frameUniforms))
(bind-group :name perMaterial :layout renderPipeline :group 1 (entry :binding 0 :texture albedo) (entry :binding 1 :sampler linearSampler))
(render-pass :name draw (color-attachment :view context-current-texture :clear-value [0 0 0 1] :load-op clear :store-op store) :pipeline renderPipeline :bind-groups [perFrame perMaterial] (draw :vertex-count 1000))Validation Rules
Section titled “Validation Rules”| Rule | Error |
|---|---|
| Name must be unique across every form kind | duplicate_cross_ref_target |
:layout is required |
missing_required_key |
:layout must name a pipeline or a bind-group layout |
not_cross_ref |
(entry …) requires :binding |
missing_required_key |
No resource in an (entry …) |
required_one_of_missing |
Two resources in one (entry …) |
mutually_exclusive_keys_present |
:offset / :size without :buffer |
dependent_key_missing |
| Referenced resources must exist | not_cross_ref |
Two rules are the compiler’s, so they carry a located message and no code: a
slice that runs past the end of its buffer, and a non-zero :group beside an
explicit (bind-group-layout …), which warns and then fails as an unbound
binding (:group 0 there is silent and harmless). Reusing another form’s name
is a compiler diagnostic too, because names are one namespace across every
form kind: duplicate name 'x': already declared as a (buffer …) at line N.
WebGPU Mapping
Section titled “WebGPU Mapping”Maps to GPUBindGroup via:
device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: { buffer: uniformBuffer } } ]});Related
Section titled “Related”(bind-group-layout …)- Define layouts(buffer …)- Buffer resources(texture …)- Texture resources(sampler …)- Sampler resources(render-pass …)- Use bind groups in passes