// Apply per-plane statistics: y = (x - mean) * inverseStddev * scale + bias. // The vectorized route packs four adjacent spatial values per invocation; each // packed load and store remains within one plane. {% set vectorized = vectorized if vectorized is defined else false %} {% if usesF16 %} enable f16; {% endif %} {% set LOAD_OPEN = "vec4(" if usesF16 else "" %} {% set LOAD_CLOSE = ")" if usesF16 else "" %} {% set STORE_OPEN = "vec4(" if usesF16 else "" %} {% set STORE_CLOSE = ")" if usesF16 else "" %} {% set CHAN_OPEN = "f32(" if usesF16 else "" %} {% set CHAN_CLOSE = ")" if usesF16 else "" %} {{ env.wgsl.resourceDeclarations }} const WG: u32 = {{ applyWorkgroupSize }}u; @compute @workgroup_size(WG, 1, 1) fn main(@builtin(global_invocation_id) gid: vec3, @builtin(num_workgroups) nwg: vec3) { {% if not vectorized %} // 2D-folded flat index: gid.y carries the high bits after dispatch folding. {% endif %} let index = gid.x + gid.y * nwg.x * WG; if (index >= params.count) { return; } {% if vectorized %} // The vectorized path requires each plane to contain a multiple of four // values, so a packed load/store never crosses an instance boundary. let plane = (index * 4u) / params.spatial; {% else %} let plane = index / params.spatial; {% endif %} let channel = plane % params.channels; let mean = stats[plane * 2u]; let inv_std = stats[plane * 2u + 1u]; {% if vectorized %} let value = {{ LOAD_OPEN }}input[index]{{ LOAD_CLOSE }}; output[index] = {{ STORE_OPEN }}(value - vec4(mean)) * vec4(inv_std * {{ CHAN_OPEN }}scale[channel]{{ CHAN_CLOSE }}) + vec4({{ CHAN_OPEN }}bias[channel]{{ CHAN_CLOSE }}){{ STORE_CLOSE }}; {% else %} output[index] = {{ scalar }}((f32(input[index]) - mean) * inv_std * f32(scale[channel]) + f32(bias[channel])); {% endif %} }