Skip to content

(texture …)

Creates a GPU texture for sampling, render targets, or storage.

(texture :name name
:size canvas
:format rgba8unorm
:usage [texture-binding render-attachment]
:sample-count 4)
Key Type Required Default Description
:name symbol Yes - Unique texture name
:size vector / symbol / reference Yes - Texel size (see below)
:format symbol Yes - Texel format
:usage array Yes - Usage flags, at least one
:sample-count number No 1 Samples per texel: 1 or 4 (MSAA, multisample anti-aliasing)
:dimension symbol No 2d 1d, 2d or 3d
:mip-level-count number No 1 Mip levels to allocate
:pool number No 1 Ping-pong texture pool size (1 to 255)

:size, :format and :usage are required, as the three required members of GPUTextureDescriptor are. A texture that names no size is a validation error, not a texture of some guessed default size.

Type: vector | symbol | reference

One key with three spellings, because GPUTextureDescriptor.size is one member:

  • An extent vector: [w], [w h] or [w h d], in texels. The missing dimensions are WebGPU’s own defaults of 1, and each element is at least 1. The third element is depthOrArrayLayers: the slice count of a 3d texture, or the layer count of a 2d array.
  • canvas: track the canvas size at runtime, so the texture resizes with it.
  • An (image-bitmap …) name: take the decoded image’s dimensions, so they are not restated.

Each element may be a literal, an expression, or a (define …) constant.

; Canvas-relative render target
(texture :name renderTarget
:size canvas
:format preferred-canvas-format
:usage [render-attachment])
; Static size
(texture :name image
:size [512 512]
:format rgba8unorm
:usage [texture-binding])
; Sized from a decoded image
(data :name logoFile :file "assets/logo.png" :mime "image/png")
(image-bitmap :name logo :data logoFile)
(texture :name diffuseMap
:size logo
:format rgba8unorm
:usage [texture-binding copy-dst render-attachment])

Type: symbol

Texture format. Common formats:

Format Description
rgba8unorm 8-bit RGBA, normalized
bgra8unorm 8-bit BGRA, normalized
rgba16float 16-bit float RGBA
rgba32float 32-bit float RGBA
r8unorm 8-bit red channel
r32float 32-bit float red channel
depth24plus 24-bit depth
depth32float 32-bit float depth
depth24plus-stencil8 Depth + stencil
preferred-canvas-format Browser preferred format

Type: array of symbols

Texture usage flags. At least one required.

Flag Description
texture-binding Sample in shaders
storage-binding Read/write in compute
render-attachment Render target
copy-src Copy from texture
copy-dst Copy to texture

Type: number (1 or 4)

Samples per texel. WebGPU takes 1 or 4 and nothing between, and the compiler says so where the value is written. Above 1 the texture is multisampled, which constrains the rest of its descriptor: it must be 2d, have one mip level and one array layer, carry render-attachment usage (multisampling happens during rasterization, so the texture has to be attachable), and not carry storage-binding (a storage write addresses one texel, and a multisampled texel is four samples). Each of those is a separate located error.

(define :name SAMPLE_COUNT :value 4)
(texture :name msaaTarget
:size canvas
:format preferred-canvas-format
:usage [render-attachment]
:sample-count SAMPLE_COUNT)

Type: symbol (1d | 2d | 3d)

Texture dimensionality, spelled as the WebGPU specification spells it: 1d, 2d (the default), or 3d. A number there is a wrong_underlying error and a spelling outside the three is a not_member that lists them.

A 3d texture takes its slice count from the third :size element. A 2d array keeps :dimension 2d and puts the layer count in that same third element.

A 1d texture cannot carry render-attachment usage: a render pass rasterizes into a 2d or 3d target.

Sampling a 1d, 3d, or array texture requires a texture view whose dimension matches; a default view (used when a texture is bound directly) is always 2d.

Type: number (≥ 1)

Number of mip levels to allocate (default 1). Levels beyond what you write stay uninitialized until generated (e.g. by a mip-generation pass).

(texture :name mipped
:size [256 256]
:mip-level-count 4
:format rgba8unorm
:usage [texture-binding copy-dst])

Type: number (1 to 255)

Creates N sequential ping-pong texture instances, alternating each frame between the one being read and the one being written (feedback render targets, for example). The same mechanism as a (buffer …) pool.

(texture :name feedbackTex
:size canvas
:format rgba8unorm
:usage [texture-binding render-attachment]
:pool 2)
(texture :name colorTarget
:size canvas
:format preferred-canvas-format
:usage [render-attachment texture-binding])
(texture :name depthTexture
:size canvas
:format depth24plus
:usage [render-attachment])
(render-pass :name main
(depth-stencil-attachment :view depthTexture
:depth-clear-value 1.0
:depth-load-op clear
:depth-store-op store)
:pipeline cubePipeline
(draw :vertex-count 36))
(define :name SAMPLE_COUNT :value 4)
(texture :name msaaTexture
:size canvas
:format preferred-canvas-format
:usage [render-attachment]
:sample-count SAMPLE_COUNT)
(render-pass :name msaaPass
(color-attachment :view msaaTexture
:resolve-target context-current-texture
:clear-value [0 0 0 1]
:load-op clear :store-op discard)
:pipeline pipeline
(draw :vertex-count 3))
(data :name imageData :file "texture.png" :mime "image/png")
(image-bitmap :name image :data imageData)
(texture :name diffuseMap
:size image
:format rgba8unorm
:usage [texture-binding copy-dst render-attachment])
(queue :name loadTexture
(copy-external-image-to-texture (source :image image) (destination :texture diffuseMap)))
Rule Error
A name is declared once, across every form kind duplicate_cross_ref_target, or duplicate name 'x': already declared as a (texture …) at line N
:size, :format and :usage are required missing_required_key
:size is an extent vector, canvas, or an (image-bitmap …) name union_no_branch_matched
:usage needs at least one flag vector_too_short
:dimension is 1d, 2d or 3d not_member; wrong_underlying for a number
Each :size element and :mip-level-count are ≥ 1 number_below_min
:pool must be in [1,255] number_below_min / number_above_max
:sample-count is 1 or 4 texture 't' sets :sample-count 2
Above 1 it means 2d, one mip level, one layer, attachable and not storage texture 't' is :sample-count 4 without render-attachment usage
render-attachment needs a 2d or 3d texture texture 't' is :dimension 1d with render-attachment usage
A copy endpoint carries the usage the copy needs texture 't' is the destination of a copy but its :usage has no copy-dst

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.

Maps to GPUTexture via:

device.createTexture({
size: [width, height, depthOrArrayLayers],
dimension: "3d", // when :dimension 3d (omitted for the 2d default)
mipLevelCount: mipLevelCount,
sampleCount: sampleCount,
format: format,
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
});