Skip to main content

Module texel_array

Module texel_array 

Source
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.
TexelArray
A flat array of fixed-size elements stored in a texture.
TexelArrayLayout
A texel array’s shape and texel format.

Enums§

ArrayHandle
A flat array of fixed-size elements, as a bindable resource.
ArrayStorage 🔒
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 a and b.
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_dimension rows.