WGSL Textures and Samplers
Declare WGSL texture and sampler handle types and call the texture builtin functions correctly per shader stage in WebGPU 1.0-stable.
Quick Reference
Texture handle types
All texture and sampler variables live in the handle address space. They are declared
at module scope with @group(g) @binding(b) and have access mode read (storage
textures have a parameterized access mode).
| WGSL type | Purpose | Sample type T |
|---|---|---|
texture_1d<T> |
1D sampled texture | f32, i32, u32 |
texture_2d<T> |
2D sampled texture | f32, i32, u32 |
texture_2d_array<T> |
2D array sampled texture | f32, i32, u32 |
texture_3d<T> |
3D sampled texture | f32, i32, u32 |
texture_cube<T> |
Cube sampled texture | f32, i32, u32 |
texture_cube_array<T> |
Cube array sampled texture | f32, i32, u32 |
texture_multisampled_2d<T> |
MSAA 2D texture (load only) | f32, i32, u32 |
texture_depth_2d |
2D depth texture | implicit f32 |
texture_depth_2d_array |
2D array depth texture | implicit f32 |
texture_depth_cube |
Cube depth texture | implicit f32 |
texture_depth_cube_array |
Cube array depth texture | implicit f32 |
texture_depth_multisampled_2d |
MSAA depth texture (load only) | implicit f32 |
texture_storage_1d<F, A> |
1D storage texture | format F, access A |
texture_storage_2d<F, A> |
2D storage texture | format F, access A |
texture_storage_2d_array<F, A> |
2D array storage texture | format F, access A |
texture_storage_3d<F, A> |
3D storage texture | format F, access A |
texture_external |
Imported video / external frame | implicit |
Storage access A is one of read, write, read_write. Storage format F MUST be a
storage-capable format (for example rgba8unorm, rgba16float, r32float, rgba32uint).
Sampler types
| WGSL type | Use with | Host bind-group sampler.type |
|---|---|---|
sampler |
texture_*<f32> color textures |
"filtering" or "non-filtering" |
sampler_comparison |
texture_depth_* for shadow lookups |
"comparison" |
Texture builtin functions
| Builtin | Returns | Legal stages | Derivatives |
|---|---|---|---|
textureSample |
vec4<f32> / f32 (depth) |
fragment only | implicit |
textureSampleBias |
vec4<f32> |
fragment only | implicit |
textureSampleGrad |
vec4<f32> |
fragment only | explicit grad |
textureSampleCompare |
f32 |
fragment only | implicit |
textureSampleLevel |
vec4<f32> / f32 (depth) |
any stage | explicit level |
textureSampleCompareLevel |
f32 |
any stage | explicit level 0 |
textureSampleBaseClampToEdge |
vec4<f32> |
any stage | base level |
textureLoad |
vec4<T> / f32 (depth) |
any stage | none |
textureStore |
nothing | any stage | none |
textureGather |
vec4<f32> / vec4<T> |
any stage | none |
textureGatherCompare |
vec4<f32> |
any stage | none |
textureDimensions |
u32 / vec2<u32> / vec3<u32> |
any stage | none |
textureNumLayers |
u32 |
any stage | none |
textureNumLevels |
u32 |
any stage | none |
textureNumSamples |
u32 |
any stage | none |
Full signatures and per-overload coordinate types are in references/methods.md.
Decision Tree
Need a texel value in a shader?
|
+- In a @fragment entry, mip selected automatically by screen-space derivatives?
| +- Color texture + sampler -> textureSample
| +- With LOD bias -> textureSampleBias
| +- With explicit ddx/ddy gradients -> textureSampleGrad
| +- Depth texture, shadow comparison -> textureSampleCompare (sampler_comparison)
|
+- In @vertex or @compute (no implicit derivatives available)?
| +- Filtered sample, you supply the mip level -> textureSampleLevel
| +- Depth comparison, you supply level 0 -> textureSampleCompareLevel
| +- Exact texel, integer coords, no filter -> textureLoad
|
+- Reading a texture_multisampled_2d or texture_storage_* read texture?
| +- textureLoad (textureSample is illegal on these types)
|
+- Writing a texel into a texture_storage_* write/read_write texture?
| +- textureStore
|
+- Need 4-texel footprint for soft shadows / custom filtering?
+- Color -> textureGather
+- Depth -> textureGatherCompare
Core Patterns
Pattern 1: ALWAYS use textureSampleLevel or textureLoad outside the fragment stage
textureSample, textureSampleBias, textureSampleGrad, and textureSampleCompare
compute screen-space derivatives implicitly. Derivatives only exist in the fragment
stage. ALWAYS replace them with an explicit-level builtin in @vertex and @compute.
// NEVER in @compute / @vertex: textureSample has no derivatives there.
@compute @workgroup_size(8, 8)
fn cs(@builtin(global_invocation_id) id : vec3u) {
// ALWAYS supply the level explicitly outside the fragment stage.
let texel = textureSampleLevel(srcTex, srcSampler, uv, 0.0);
// OR: integer-coordinate fetch, no sampler needed.
let exact = textureLoad(srcTex, vec2i(id.xy), 0);
}
Pattern 2: ALWAYS call textureSample in uniform control flow
textureSample requires uniform control flow because all invocations in a quad must
agree on whether the call executes, or derivatives are undefined. NEVER call it inside
an if/switch/loop whose condition varies per invocation. Hoist the sample out, or
use textureSampleLevel.
@fragment
fn fs(@location(0) uv : vec2f) -> @location(0) vec4f {
let color = textureSample(colorTex, colorSampler, uv); // hoisted, uniform
if (uv.x > someNonUniformValue) {
return color * 0.5;
}
return color;
}
Pattern 3: ALWAYS pair texture_depth_* with sampler_comparison for shadows
A depth texture sampled for shadow comparison MUST use sampler_comparison and
textureSampleCompare. The host bind-group entry MUST set sampler.type: "comparison"
and texture.sampleType: "depth". Using a plain sampler with a depth texture is a
shader-creation error.
@group(0) @binding(0) var shadowMap : texture_depth_2d;
@group(0) @binding(1) var shadowSampler : sampler_comparison;
@fragment
fn fs(@location(0) shadowCoord : vec3f) -> @location(0) vec4f {
// returns 1.0 if shadowCoord.z <= stored depth, else 0.0 (filtered)
let visible = textureSampleCompare(
shadowMap, shadowSampler, shadowCoord.xy, shadowCoord.z);
return vec4f(vec3f(visible), 1.0);
}
Pattern 4: ALWAYS match the storage-texture format and access to the host layout
A texture_storage_2d<F, A> declaration MUST use the exact F and A that the
host storageTexture bind-group-layout entry specifies. Write with textureStore.
textureSample and a sampler are illegal on storage textures.
// Host layout: storageTexture { access: "write-only", format: "rgba8unorm" }
@group(0) @binding(0) var outImg : texture_storage_2d<rgba8unorm, write>;
@compute @workgroup_size(8, 8)
fn cs(@builtin(global_invocation_id) id : vec3u) {
textureStore(outImg, vec2i(id.xy), vec4f(1.0, 0.0, 0.0, 1.0));
}
Pattern 5: ALWAYS make the WGSL handle type match the bind-group-layout entry
The WGSL texture handle type at @group(g) @binding(b) MUST correspond to the host
texture / storageTexture / externalTexture entry at the same slot. Mismatches
(dimension, sample type, multisampled flag) fail pipeline validation. See
webgpu-syntax-bind-groups for the host side.
| WGSL handle type | Host bind-group-layout entry |
|---|---|
texture_2d<f32> |
texture: { sampleType: "float", viewDimension: "2d" } |
texture_2d<u32> |
texture: { sampleType: "uint", viewDimension: "2d" } |
texture_depth_2d |
texture: { sampleType: "depth", viewDimension: "2d" } |
texture_multisampled_2d<f32> |
texture: { sampleType:"float", multisampled:true } |
texture_storage_2d<rgba8unorm, write> |
storageTexture: { access:"write-only", format:"rgba8unorm" } |
texture_external |
externalTexture: {} |
sampler |
sampler: { type: "filtering" } |
sampler_comparison |
sampler: { type: "comparison" } |
Pattern 6: ALWAYS use textureLoad for multisampled and external textures
texture_multisampled_2d<T> and texture_depth_multisampled_2d support only
textureLoad with an explicit sample_index. texture_external supports
textureSampleBaseClampToEdge, textureLoad, and textureDimensions.
@group(0) @binding(0) var msaaTex : texture_multisampled_2d<f32>;
@fragment
fn resolve(@builtin(position) pos : vec4f) -> @location(0) vec4f {
let coord = vec2i(pos.xy);
// textureLoad takes a sample index for multisampled textures.
return (textureLoad(msaaTex, coord, 0) + textureLoad(msaaTex, coord, 1)) * 0.5;
}
Common Anti-Patterns
- Calling
textureSamplefrom a@vertexor@computeentry point. It needs screen-space derivatives that only the fragment stage has. UsetextureSampleLevelortextureLoadinstead. Shader-creation error. - Using
samplerinstead ofsampler_comparisonwith atexture_depth_*texture intextureSampleCompare. The comparison family requires a comparison sampler. Shader-creation error. - Declaring a storage-texture format or access mode that does not match the host
storageTexturelayout entry, or callingtextureSampleon a storage texture. Pipeline-creation / shader-creation error.
Full anti-pattern analysis with WHY explanations is in references/anti-patterns.md.
Critical Warnings
- NEVER call
textureSample,textureSampleBias,textureSampleGrad, ortextureSampleCompareoutside the fragment stage. Derivatives do not exist there. - NEVER call
textureSamplein non-uniform control flow. The uniformity analysis rejects it or emits aderivative_uniformitydiagnostic. - NEVER use a plain
samplerwithtextureSampleCompare; it requiressampler_comparison. - NEVER call
textureStoreon atexture_storage_*<F, read>texture; the access mode must includewrite. - NEVER call
textureSampleontexture_multisampled_2d,texture_storage_*, ortexture_external; only the builtins listed for each type apply. - NEVER let the WGSL handle type drift from the bind-group-layout entry; dimension, sample type, and multisampled flag all MUST match.
Reference Files
references/methods.md— every texture handle type, sampler type, and texture builtin signature with per-overload coordinate types and stage legality.references/examples.md— verified WGSL: fragment sampling,textureLoadin compute, storage-texture write, depth comparison shadow lookup.references/anti-patterns.md— texture mistakes with WHY-it-fails explanations.
Cross-links
webgpu-wgsl-builtins— full builtin-function and builtin-value catalogue.webgpu-wgsl-uniformity— uniform control flow and thederivative_uniformitydiagnostic.webgpu-syntax-textures— host-sidecreateTexture,createSampler, texture views.webgpu-syntax-bind-groups— host-side bind-group-layouttexture/sampler/storageTexture/externalTextureentries.