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
});

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.

(texture …) mirrors GPUTextureDescriptor (MDN).

Key WebGPU Note
:name GPUObjectDescriptorBase.label the label of GPUObjectDescriptorBase, and the name every cross-reference resolves
:size GPUTextureDescriptor.size
:format GPUTextureDescriptor.format
:usage GPUTextureDescriptor.usage
:sample-count GPUTextureDescriptor.sampleCount
:dimension GPUTextureDescriptor.dimension
:mip-level-count GPUTextureDescriptor.mipLevelCount
:pool PNGine’s own ping-pong: N instances, one selected per frame

Not expressible in PNGine yet: GPUTextureDescriptor.viewFormats, GPUTextureDescriptor.textureBindingViewDimension.

texture-format (:format on (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

texture-usage (:usage on (texture …)) spells the GPUTextureUsage flags (MDN).

Value WebGPU Note
copy-src GPUTextureUsage.COPY_SRC
copy-dst GPUTextureUsage.COPY_DST
texture-binding GPUTextureUsage.TEXTURE_BINDING
storage-binding GPUTextureUsage.STORAGE_BINDING
render-attachment GPUTextureUsage.RENDER_ATTACHMENT

Not offered by PNGine (1 of the spec’s 6 values): transient-attachment.

texture-dimension (:dimension on (texture …)) spells the GPUTextureDimension enum.

Value WebGPU Note
1d "1d"
2d "2d"
3d "3d"

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