This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Textures and Samplers
Creating a texture
(gfx:create-texture device :width 512 :height 512 :format :rgba8-srgb
:mip-levels :all :usage '(:sampled :copy-dst))
(gfx:create-texture device :width 1280 :height 720 :format :depth32-float
:usage '(:render-target))
(gfx:create-texture device :width 256 :dimension :cube :format :rgba16-float
:usage '(:render-target :sampled))
| Key | Default | Meaning |
|---|---|---|
:width |
required | Width in texels. |
:height |
1 | Height in texels. A :1d texture has none, and a cube takes its :width. |
:depth |
1 | Depth in texels, for a :3d texture only. |
:array-layers |
1 | Number of layers, for array textures only. |
:dimension |
:2d |
The shape (below). |
:mip-levels |
1 | A count, or :all for every level down to one texel. |
:samples |
1 | 1, 2, 4 or 8. More than 1 makes a multisampled texture. |
:format |
required | A texture format (below). |
:usage |
required | What the texture is used for (below). |
Sizes and layer counts are 32-bit unsigned integers from 1, and a mip level
is from 0 to 31, as write-texture and read-texture take it.
Dimensions
:dimension |
Shape | Sized by |
|---|---|---|
:1d |
a row of texels | :width |
:2d |
a rectangle | :width, :height |
:3d |
a box | :width, :height, :depth |
:cube |
six square faces, sampled by direction | :width |
:1d-array, :2d-array |
layers of the above | plus :array-layers |
:cube-array |
layers of cubes | plus :array-layers |
:1d, :1d-array and :3d textures cannot be render targets.
Usages
| Usage | Lets the texture be |
|---|---|
:render-target |
a color or depth attachment of a render pass |
:sampled |
a :texture binding, read by shaders through a sampler |
:storage |
a :storage-texture binding, read and written texel by texel |
:copy-src |
read back with read-texture |
:copy-dst |
written with write-texture |
Formats
| Kind | Formats |
|---|---|
| 8-bit colour | :rgba8-unorm, :rgba8-srgb, :bgra8-unorm, :bgra8-srgb, :r8-unorm, :rg8-unorm |
| Float | :r32-float, :rg16-float, :rg32-float, :rgba16-float, :rgba32-float |
| Unsigned integer | :r32-uint, :rg16-uint, :rgba8-uint, :rgba16-uint, :rgba32-uint |
| Signed integer | :r32-sint |
| Depth | :depth32-float, :depth24-stencil8 |
- sRGB formats store sRGB-encoded texels. Shaders read them decoded to
linear values and write linear values that get encoded.
srgb-format-ptells you whether a format is one. - Integer formats are read by shaders as they are, unfiltered, through
integer sampler types such as GLSL's
usampler2D.integer-format-ptells you whether a format is one. - Depth formats are used as depth attachments, and can be sampled, for
example for shadows through a comparing sampler.
depth-format-ptells you whether a format is one. texel-sizereturns the size of one texel in bytes.
See Formats and Types for the full table.
Writing texels
(gfx:write-texture texture data &key (x 0) (y 0) (z 0) width height depth (level 0))
data is a vector, of bytes or of anything else write-buffer takes, such
as single-floats for :rgba32-float, or a foreign pointer. It holds the texels of the region, layer by layer (or slice by slice for a
:3d texture), rows from top to bottom without padding, each texel as its
format lays it out, for example red, green, blue, alpha for :rgba8-unorm.
The texture needs :copy-dst.
The region defaults to the whole mip level. :z and :depth count:
- slices of a
:3dtexture; - layers of an array texture;
- faces of a cube, six per cube, in the order +X, −X, +Y, −Y, +Z, −Z.
texture-layer-count returns how many layers :z counts for a texture.
;; A 2 by 2 checkerboard
(let ((image (gfx:create-texture device :width 2 :height 2 :format :rgba8-srgb
:usage '(:sampled :copy-dst))))
(gfx:write-texture image (make-array 16 :element-type '(unsigned-byte 8)
:initial-contents '(255 255 255 255 0 0 0 255
0 0 0 255 255 255 255 255)))
image)
;; A cube map, face by face
(let ((sky (gfx:create-texture device :width 512 :dimension :cube :format :rgba8-srgb
:mip-levels :all :usage '(:sampled :copy-dst))))
(dotimes (face 6)
(gfx:write-texture sky (face-texels face) :z face :depth 1))
(gfx:generate-mipmaps sky))
When the write takes effect works as it does for :device buffers: during a
frame it is staged and copied before the frame's passes, without waiting;
between frames it waits for the GPU. You may reuse data as soon as the
call returns. A wrong data length, or a region outside the level, signals an
error that gives the expected sizes.
Mipmaps
Make a texture with :mip-levels :all (or a count), fill level 0 by writing
or drawing into it, then call generate-mipmaps. It fills each level after
the first by filtering down from the one before. During a frame it runs on
the GPU without waiting; between frames it waits.
Depth textures and multisampled textures cannot have their mipmaps generated.
To write or read a specific level, pass :level to write-texture or
read-texture. To draw into a level, pass :level to the attachment in
with-render-pass.
Multisampling
(let ((samples (gfx:create-texture device :width 640 :height 480 :format :rgba8-unorm
:samples 4 :usage '(:render-target)))
(image (gfx:create-texture device :width 640 :height 480 :format :rgba8-unorm
:usage '(:render-target :sampled))))
(gfx:with-render-pass (pass frame :color ((samples :clear '(0 0 0 1) :resolve image)))
(gfx:set-pipeline pass pipeline) ; made with :samples 4
...))
A multisampled texture is :2d or :2d-array, with one mip level; any other
dimension is refused, with a message that says which. It is drawn into,
a layer at a time for an array, and resolved into a texture of one sample,
with the same format and size and :render-target usage, when the pass
ends; the pipeline must be made with the same :samples. With :sampled
usage, shaders read it a sample at a time, as a :sampler2d-ms or
:sampler2d-ms-array does; it cannot be sampled with filtering or
mipmapped. With :storage usage, shaders write it a sample at a time,
where the device has the :multisample-storage-textures feature.
Reading texels back
(gfx:read-texture texture &key (level 0) (element-type '(unsigned-byte 8)))
;; => a vector of ELEMENT-TYPE
Returns every texel of a mip level, laid out as write-texture takes them,
as bytes or as the :element-type you ask for, as read-buffer takes it:
(gfx:read-texture hdr-image :element-type 'single-float) ; :rgba32-float texels
It waits for the GPU, so call it between frames; during a frame it signals
an error. The texture needs :copy-src. It is meant for tests and
screenshots.
Accessors
texture-width, texture-height, texture-depth, texture-array-layers,
texture-dimension, texture-mip-levels, texture-samples,
texture-format, texture-usage, and texture-layer-count (layers as :z
and a pass's :layer count them: 6 per cube, so 6 × texture-array-layers
for a :cube-array).
Destroying
destroy-texture marks the texture destroyed at once and releases it once
no frame in flight uses it. Don't destroy the textures
acquire-surface-texture returns: the surface owns them.
Samplers
A sampler says how a shader reads a texture. It is paired with a texture in
a :texture binding of a bind group.
(gfx:create-sampler device) ; linear, clamped
(gfx:create-sampler device :filter :nearest) ; pixel art
(gfx:create-sampler device :address-mode :repeat :mipmap-filter :linear
:max-anisotropy 16) ; tiled ground
(gfx:create-sampler device :compare :less-equal) ; shadow maps
| Key | Default | Values |
|---|---|---|
:filter |
:linear |
:linear blends the nearest texels; :nearest takes the nearest one. |
:address-mode |
:clamp-to-edge |
Outside 0 to 1: :clamp-to-edge, :repeat or :mirror-repeat. |
:mipmap-filter |
:nearest |
:nearest reads the nearest mip level; :linear blends the two nearest. |
:compare |
nil |
A compare function, such as :less-equal, makes a comparing sampler for shadow maps: reading a depth texture through it compares a depth with the texels. |
:max-anisotropy |
1 | 1 to 16. Sharpens textures seen at a slant. Every device has anisotropic filtering. |
Accessors: sampler-filter, sampler-address-mode, sampler-mipmap-filter,
sampler-compare, sampler-max-anisotropy. Destroy with destroy-sampler.