Skip to content

(bind-group-layout …)

Defines the layout of a bind group explicitly, specifying what resource can be bound at each binding index and which shader stages may access it. Use it instead of inferring the layout from a pipeline’s auto-layout.

Compose one or more of these into an explicit (pipeline-layout …) to bind @group(0), @group(1), … to distinct layouts, or name one directly as a (bind-group …)’s :layout.

(bind-group-layout :name layoutName
(entry :binding 0 :visibility [vertex fragment] (buffer :type uniform))
(entry :binding 1 :visibility [fragment] (sampler :type filtering))
(entry :binding 2 :visibility [fragment] (texture :sample-type float))
(entry :binding 3 :visibility [compute] (storage-texture :format rgba8unorm)))
Key / Sub-form Type Required Default Description
:name symbol Yes - Layout name
(entry …) sub-form No - Binding layout entries, one per slot

A layout with no (entry …) children is legal and describes an empty group, the same as WebGPU’s empty entries list.

Each (entry …) sub-form describes a single binding slot:

Key / Sub-form Type Required Description
:binding number Yes Binding index (0-based, up to 255)
:visibility array Yes Shader stages that can access
resource sub-form sub-form Yes Exactly one of (buffer …), (sampler …), (texture …), (storage-texture …)

Exactly one resource per entry, as GPUBindGroupLayoutEntry says: none is positional_missing and two is positional_too_many, both located, rather than a binding kind chosen for you.

Array of shader stages:

Value Description
vertex Vertex shader
fragment Fragment shader
compute Compute shader

Any other spelling is a not_member error naming the three that are allowed.

Buffer binding layout:

Key Type Required Description
:type symbol No (uniform) How the buffer is exposed to WGSL
Type WGSL
uniform var<uniform> (read-only, size-limited)
storage var<storage, read_write>
read-only-storage var<storage, read>
Key Type Default Description
:type symbol filtering filtering, non-filtering, or comparison

comparison corresponds to WGSL’s sampler_comparison; pair it with a (sampler … :compare …) for shadow mapping.

Sampled-texture binding layout:

Key Type Default Description
:sample-type symbol float float, unfilterable-float, depth, sint, uint
:view-dimension symbol 2d View dimension (see the table below)
:multisampled boolean false Whether the binding is a multisampled texture
Key Type Default Description
:format symbol required Texel format; WebGPU requires a storage-capable one, e.g. rgba8unorm
:access symbol write-only write-only, read-only, or read-write
:view-dimension symbol 2d View dimension (see the table below)

:view-dimension is spelled the way the WebGPU spec spells it, the same spellings (texture-view …) takes:

Value GPUTextureViewDimension
1d One-dimensional view (texture_1d)
2d Two-dimensional view (texture_2d), the default
2d-array Array of 2d layers (texture_2d_array)
cube Cube map, six 2d layers (texture_cube)
cube-array Array of cube maps (texture_cube_array)
3d Volume view (texture_3d)

The digit-leading spellings lex as numbers with a unit in SJON (the S-expression source format PNGine compiles), and the schema matches them on that parsed pair, so they are written bare, unquoted. A plain integer is a wrong_underlying error.

(bind-group-layout :name uniformLayout
(entry :binding 0 :visibility [vertex fragment] (buffer :type uniform)))
(bind-group-layout :name sceneLayout
(entry :binding 0 :visibility [vertex fragment] (buffer :type uniform))
(entry :binding 1 :visibility [fragment] (buffer :type uniform))
(entry :binding 2 :visibility [vertex fragment] (buffer :type uniform)))
(bind-group-layout :name computeLayout
(entry :binding 0 :visibility [compute] (buffer :type read-only-storage))
(entry :binding 1 :visibility [compute] (buffer :type storage)))
(bind-group-layout :name imageLayout
(entry :binding 0 :visibility [fragment] (sampler :type filtering))
(entry :binding 1 :visibility [fragment] (texture :sample-type float))
(entry :binding 2 :visibility [compute]
(storage-texture :format rgba8unorm :access write-only :view-dimension 2d)))
(bind-group-layout :name arrayLayout
(entry :binding 0 :visibility [fragment]
(texture :sample-type float :view-dimension 2d-array))
(entry :binding 1 :visibility [fragment]
(texture :sample-type float :view-dimension cube)))
(bind-group-layout :name shadowLayout
(entry :binding 0 :visibility [fragment] (sampler :type comparison))
(entry :binding 1 :visibility [fragment] (texture :sample-type depth)))
Rule Error
Name must be unique across every form kind duplicate_cross_ref_target
Each entry requires :binding and :visibility missing_required_key
Each entry must carry exactly one resource sub-form positional_missing / positional_too_many
(buffer …) requires :type missing_required_key
(storage-texture …) requires :format missing_required_key
:visibility takes only vertex, fragment, compute not_member
:view-dimension takes a spec spelling, not an integer wrong_underlying

A bind-group layout, a render pipeline and a compute pipeline share one cross-reference target group, because a (bind-group …) may name any of them, so a name collision between two of those kinds is a duplicate_cross_ref_target. Across unrelated kinds the compiler reports it instead, with a located message and no code: duplicate name 'x': already declared as a (buffer …) at line N.

The entry types must match the WGSL binding they front, or WebGPU rejects the pipeline; the native renderer reports that as a nonzero exit rather than a blank frame.

Maps to GPUBindGroupLayout via:

device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
buffer: { type: 'uniform' }
},
{
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
texture: { sampleType: 'float', viewDimension: '2d' }
}
]
});

:visibility becomes the ORed GPUShaderStage mask (vertex 0x1, fragment 0x2, compute 0x4). Each resource sub-form’s enum values are the WebGPU spellings, passed through verbatim.

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.

(bind-group-layout …) is GPUBindGroupLayoutDescriptor (MDN), restructured. The descriptor createBindGroupLayout() takes, with entries as positional (entry …) children.

Key WebGPU Note
:name GPUObjectDescriptorBase.label
(entry …) GPUBindGroupLayoutDescriptor.entries a positional child, not a key

(entry …) is GPUBindGroupLayoutEntry (MDN), restructured. One GPUBindGroupLayoutEntry; the resource layout is a positional child ((buffer …), (sampler …), (texture …) or (storage-texture …)), one of the four members the IDL makes optional and PNGine makes exactly-one.

Key WebGPU Note
:binding GPUBindGroupLayoutEntry.binding
:visibility GPUBindGroupLayoutEntry.visibility
(buffer …) GPUBindGroupLayoutEntry.buffer a positional child, not a key
(sampler …) GPUBindGroupLayoutEntry.sampler a positional child, not a key
(texture …) GPUBindGroupLayoutEntry.texture a positional child, not a key
(storage-texture …) GPUBindGroupLayoutEntry.storageTexture a positional child, not a key

(buffer …) mirrors GPUBufferBindingLayout (MDN).

Key WebGPU Note
:type GPUBufferBindingLayout.type

Not expressible in PNGine yet: GPUBufferBindingLayout.hasDynamicOffset, GPUBufferBindingLayout.minBindingSize.

(sampler …) mirrors GPUSamplerBindingLayout (MDN).

Key WebGPU Note
:type GPUSamplerBindingLayout.type

(texture …) mirrors GPUTextureBindingLayout (MDN).

Key WebGPU Note
:sample-type GPUTextureBindingLayout.sampleType
:view-dimension GPUTextureBindingLayout.viewDimension
:multisampled GPUTextureBindingLayout.multisampled

(storage-texture …) mirrors GPUStorageTextureBindingLayout (MDN).

Key WebGPU Note
:format GPUStorageTextureBindingLayout.format
:access GPUStorageTextureBindingLayout.access
:view-dimension GPUStorageTextureBindingLayout.viewDimension

visibility (:visibility on (entry …)) spells the GPUShaderStage flags.

Value WebGPU Note
vertex GPUShaderStage.VERTEX
fragment GPUShaderStage.FRAGMENT
compute GPUShaderStage.COMPUTE

buffer-binding-type (:type on (buffer …)) spells the GPUBufferBindingType enum.

Value WebGPU Note
uniform "uniform"
storage "storage"
read-only-storage "read-only-storage"

sampler-binding-type (:type on (sampler …)) spells the GPUSamplerBindingType enum.

Value WebGPU Note
filtering "filtering"
non-filtering "non-filtering"
comparison "comparison"

texture-sample-type (:sample-type on (texture …)) spells the GPUTextureSampleType enum.

Value WebGPU Note
float "float"
unfilterable-float "unfilterable-float"
depth "depth"
sint "sint"
uint "uint"

view-dimension (:view-dimension on (texture …), :view-dimension on (storage-texture …)) spells the GPUTextureViewDimension enum.

Value WebGPU Note
1d "1d"
2d "2d"
2d-array "2d-array"
cube "cube"
cube-array "cube-array"
3d "3d"

texture-format (:format on (storage-texture …)) spells the GPUTextureFormat enum.

50 values
Value WebGPU Note
rgba8unorm "rgba8unorm"
rgba8snorm "rgba8snorm"
bgra8unorm "bgra8unorm"
rgba16float "rgba16float"
rgba32float "rgba32float"
depth24plus "depth24plus"
depth24plus-stencil8 "depth24plus-stencil8"
depth32float "depth32float"
stencil8 "stencil8"
depth16unorm "depth16unorm"
r8unorm "r8unorm"
rg8unorm "rg8unorm"
r16float "r16float"
rg16float "rg16float"
r32float "r32float"
r32uint "r32uint"
rgba8unorm-srgb "rgba8unorm-srgb"
rgba8uint "rgba8uint"
rgba8sint "rgba8sint"
r8snorm "r8snorm"
r8uint "r8uint"
r8sint "r8sint"
rg8snorm "rg8snorm"
rg8uint "rg8uint"
rg8sint "rg8sint"
r16uint "r16uint"
r16sint "r16sint"
rg16uint "rg16uint"
rg16sint "rg16sint"
r32sint "r32sint"
rg32uint "rg32uint"
rg32sint "rg32sint"
rg32float "rg32float"
rgba16uint "rgba16uint"
rgba16sint "rgba16sint"
rgba32uint "rgba32uint"
rgba32sint "rgba32sint"
rgb10a2unorm "rgb10a2unorm"
rgb10a2uint "rgb10a2uint"
rg11b10ufloat "rg11b10ufloat"
rgb9e5ufloat "rgb9e5ufloat"
bgra8unorm-srgb "bgra8unorm-srgb"
r16unorm "r16unorm"
r16snorm "r16snorm"
rg16unorm "rg16unorm"
rg16snorm "rg16snorm"
rgba16unorm "rgba16unorm"
rgba16snorm "rgba16snorm"
depth32float-stencil8 "depth32float-stencil8"
preferred-canvas-format PNGine’s own the runtime’s negotiated canvas format; see References
52 of the spec's 101 values PNGine does not offer

bc1-rgba-unorm, bc1-rgba-unorm-srgb, bc2-rgba-unorm, bc2-rgba-unorm-srgb, bc3-rgba-unorm, bc3-rgba-unorm-srgb, bc4-r-unorm, bc4-r-snorm, bc5-rg-unorm, bc5-rg-snorm, bc6h-rgb-ufloat, bc6h-rgb-float, bc7-rgba-unorm, bc7-rgba-unorm-srgb, etc2-rgb8unorm, etc2-rgb8unorm-srgb, etc2-rgb8a1unorm, etc2-rgb8a1unorm-srgb, etc2-rgba8unorm, etc2-rgba8unorm-srgb, eac-r11unorm, eac-r11snorm, eac-rg11unorm, eac-rg11snorm, astc-4x4-unorm, astc-4x4-unorm-srgb, astc-5x4-unorm, astc-5x4-unorm-srgb, astc-5x5-unorm, astc-5x5-unorm-srgb, astc-6x5-unorm, astc-6x5-unorm-srgb, astc-6x6-unorm, astc-6x6-unorm-srgb, astc-8x5-unorm, astc-8x5-unorm-srgb, astc-8x6-unorm, astc-8x6-unorm-srgb, astc-8x8-unorm, astc-8x8-unorm-srgb, astc-10x5-unorm, astc-10x5-unorm-srgb, astc-10x6-unorm, astc-10x6-unorm-srgb, astc-10x8-unorm, astc-10x8-unorm-srgb, astc-10x10-unorm, astc-10x10-unorm-srgb, astc-12x10-unorm, astc-12x10-unorm-srgb, astc-12x12-unorm, astc-12x12-unorm-srgb

storage-texture-access (:access on (storage-texture …)) spells the GPUStorageTextureAccess enum.

Value WebGPU Note
write-only "write-only"
read-only "read-only"
read-write "read-write"

Checked against the WebGPU specification at revision b8c0fa9; the links go to the current draft.