Chapter 03
Variables & Expressions
WGSL separates values, variables, references, and pointers carefully. Most confusing errors become easier to read once you ask whether an expression is producing a value or naming memory that can be loaded from.
Module variables name external resources
A module-scope variable with @group and @binding is part of the host contract. A
function-local variable is ordinary shader state for one invocation.
@group(0) @binding(0)
var<storage, read_write> data : array<f32>;
fn write_value(i : u32, scale : f32) {
let x = f32(i) * scale;
data[i] = sin(x);
}
References load when an expression needs a value
Indexing into a buffer points at a location. Assignment writes through that location. Passing the expression to arithmetic loads the current value. That implicit load rule is why many WGSL diagnostics mention reference and pointer types even when source code looks value-oriented.
fn step(i : u32) {
let old_value = data[i];
data[i] = old_value + 1.0;
}
Const evaluation happens before runtime
const and override values shape code before dispatch. Use them for dimensions,
feature switches, and values that should be visible to validation instead of hidden behind runtime state.
override WIDTH : u32 = 256u;
const SCALE : f32 = 0.0625;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id : vec3u) {
if (id.x < WIDTH) {
data[id.x] = f32(id.x) * SCALE;
}
}