Expand description
Texture-backed arrays, for devices whose shaders cannot read storage buffers.
WebGL2 has no storage buffers at all: a device created with WebGL2’s limits
reports max_storage_buffers_per_shader_stage == 0, and a bind-group layout
naming one is rejected outright. An array that would otherwise live in a
var<storage, read> still has to be readable per vertex, and the one
facility WebGL2 does have for that is a texture read — textureLoad is
available in every stage, and a float texture’s texels can be read as the
f32 values the array held.
A TexelArray is that translation: a flat array of fixed-size elements
stored in a 2D texture, addressed by element index, with the element’s bytes
laid out exactly as the storage buffer laid them out. Because the byte
layout is the same, the CPU packing is shared: the same element bytes are
uploaded whether the shader reads them as a buffer or as texels.
§Layout
The texture holds f32 lanes in texel order. An element occupies a whole
number of texels, and a row holds a whole number of elements, so an element
never straddles a row boundary. The element index therefore maps to a texel
index by a single multiplication, and the flat index maps to a texel
coordinate by dividing by the row width — which is the texture’s own width,
so the shader reads it back with textureDimensions rather than being told
it as a constant:
let width = textureDimensions(tex).x;
let texel = element * TEXELS_PER_ELEMENT;
let value = textureLoad(tex, vec2<i32>(i32(texel % width), i32(texel / width)), 0);§Texel format
An element whose size is a whole number of 16-byte rows is stored in
Rgba32Float, four f32 lanes per texel — a mat4x4<f32>, for instance,
is four texels, one per column. Any other element size is stored one f32
per texel in R32Float. Both are readable in every stage without the
FLOAT32_FILTERABLE feature: filtering is what that feature gates, and
textureLoad does not filter.
Neither format needs a bitcast of the values. They are read as the f32s
they are, which is why the formats are float rather than the u32 a storage
buffer would have held: a shader reading a u32 texel back would have to
bitcast every lane, and the lanes are the same bits either way. Only a
u32 member of an element needs one, and that is the shader’s concern.
§Rows and padding
A row is always a whole number of [wgpu::COPY_BYTES_PER_ROW_ALIGNMENT]
bytes, which is what a buffer-to-texture copy demands, so an upload can be
staged and recorded into the frame’s own encoder rather than costing a
submission of its own. The row width is in elements, not bytes: the row is
exactly elements_per_row * element_size bytes, so the only padding is the
unused tail of the last populated row. An element never straddles a row, so
growing the row width never splits one.
Re-exports§
use crate::resources::TextureExt;🔒use crate::resources::TextureView;🔒use crate::staging::StagingBuffer;🔒
Structs§
- Array
- A growable flat array of fixed-size elements, held in whichever resource the device can read.
- Texel
Array - A flat array of fixed-size elements stored in a texture.
- Texel
Array Layout - A texel array’s shape and texel format.
Enums§
- Array
Handle - A flat array of fixed-size elements, as a bindable resource.
- Array
Storage 🔒 - Where an
Array’s elements live.
Functions§
- create_
array_ 🔒buffer - Create the storage buffer an array’s elements live in.
- create_
texture 🔒 - Create the texture a layout describes.
- gcd 🔒
- The greatest common divisor of
aandb. - row_
elements 🔒 - The number of elements a row holds: enough for a row to reach the copy
alignment, then doubled until the elements fit within
max_dimensionrows.