texture! and sampler!
Declare texture and sampler bindings.
texture!
#![allow(unused)]
fn main() {
texture!(group(N), binding(M), NAME: TextureKind<SampleType>);
texture!(group(N), binding(M), NAME: Texture2D<f32>, unfilterable);
}
Generates:
@group(N) @binding(M) var NAME: TextureKind<SampleType>;
Texture Kinds
| Kind | Depth variant |
|---|---|
Texture1D | |
Texture2D | TextureDepth2D |
Texture2DArray | TextureDepth2DArray |
Texture3D | |
TextureCube | TextureDepthCube |
TextureCubeArray | TextureDepthCubeArray |
TextureMultisampled2D |
The sample type for color textures is typically <f32>. Depth textures need no sample type parameter.
Unfilterable Textures
Some texture formats are not filterable on the GPU — R32Float, Rg32Float, and other 32-bit float formats. Bind group layouts must declare this, or binding such a texture fails wgpu validation. Declare it with the unfilterable modifier:
#![allow(unused)]
fn main() {
texture!(group(0), binding(0), HEIGHTS: Texture2D<f32>, unfilterable);
}
The modifier is only valid on <f32>-sampled textures; integer-sampled, depth, and storage textures reject it with a compile error, since their layouts have no filtering flag.
The WGSL output is unchanged — WGSL has no unfilterable variant of texture_2d<f32>. The modifier only sets filterable: false on the generated wgpu bind group layout entry, so binding a non-filterable format (e.g. R32Float) passes validation.
Read texels with texture_load, or sample through a non-filtering sampler declared with the same modifier:
#![allow(unused)]
fn main() {
sampler!(group(0), binding(1), HEIGHTS_SMP: Sampler, unfilterable);
}
The bound wgpu sampler must use FilterMode::Nearest for all filters — a non-filtering layout slot rejects filtering samplers. WGSL output is unchanged: sampler has no non-filtering variant, so the modifier only sets SamplerBindingType::NonFiltering on the generated bind group layout entry.
Storage Textures
Storage textures (texture_storage_* in WGSL) are declared with TextureStorage types. They take two type parameters: a texel format marker and an access mode marker:
#![allow(unused)]
fn main() {
texture!(group(0), binding(0), OUTPUT: TextureStorage2D<Rgba8unorm, Write>);
}
| Kind | WGSL |
|---|---|
TextureStorage1D<F, A> | texture_storage_1d<format, access> |
TextureStorage2D<F, A> | texture_storage_2d<format, access> |
TextureStorage2DArray<F, A> | texture_storage_2d_array<format, access> |
TextureStorage3D<F, A> | texture_storage_3d<format, access> |
Texel format markers (unit structs implementing WgslTexelFormat):
| Marker | WGSL format | Value type |
|---|---|---|
Rgba8unorm | rgba8unorm | Vec4f |
Rgba8uint | rgba8uint | Vec4u |
Rgba16float | rgba16float | Vec4f |
R32uint | r32uint | Vec4u |
R32float | r32float | Vec4f |
Rg32float | rg32float | Vec4f |
Rgba32float | rgba32float | Vec4f |
Bgra8unorm | bgra8unorm | Vec4f |
Access mode markers:
| Marker | WGSL | Description |
|---|---|---|
Read | read | Read-only (load with texture_load_storage) |
Write | write | Write-only (store with texture_store) |
ReadWrite | read_write | Both (requires texture_formats_tier1) |
The
enable texture_formats_tier1;directive is auto-hoisted to the start of the assembled WGSL translation unit when storage textures are present — you don’t need to add it manually.
sampler!
#![allow(unused)]
fn main() {
sampler!(group(N), binding(M), NAME: Sampler);
sampler!(group(N), binding(M), NAME: SamplerComparison);
sampler!(group(N), binding(M), NAME: Sampler, unfilterable);
}
Generates:
@group(N) @binding(M) var NAME: sampler;
@group(N) @binding(M) var NAME: sampler_comparison;
The unfilterable modifier does not change the WGSL output; it sets
SamplerBindingType::NonFiltering on the generated wgpu bind group layout
entry (see Unfilterable Textures).
Two-Level Binding
Both macros produce a hidden __NAME static plus a visible pub const NAME: &'static ... so the binding can be passed by value. You reference NAME directly in functions:
#![allow(unused)]
fn main() {
#[wgsl]
pub mod texturing {
use wgsl_rs::std::*;
texture!(group(0), binding(0), ALBEDO: Texture2D<f32>);
sampler!(group(0), binding(1), LIN: Sampler);
#[fragment]
pub fn fs_main(
#[location(0)] uv: Vec2f,
) -> Vec4f {
texture_sample(ALBEDO, LIN, uv)
}
}
}
Passing to Functions
Texture and sampler bindings are passed by value (no &) — the visible NAME is already a reference:
#![allow(unused)]
fn main() {
pub fn sample_albedo(uv: Vec2f, tex: Texture2D<f32>, smp: Sampler) -> Vec4f {
texture_sample(tex, smp, uv)
}
}
Notes
SamplerComparisonis used withtextureSampleCompareandtextureSampleCompareLevelfor shadow maps.- Pair each texture with its sampler; binding numbers must not collide within a group.