WGSL 函数参考(WGSL Function Reference)

原作者:webgpufundamentals Contributors
原始链接:https://webgpufundamentals.org/webgpu/lessons/webgpu-wgsl-function-reference.html


导言

本文对 W3C 官方 WGSL 函数参考进行深度解读,帮助 WebGPU 开发者在编写着色器时快速查阅和理解内置函数。与 GLSL 相比,WGSL 的函数体系更加严格和模块化,尤其强调类型安全、内存访问修饰符以及未定义行为的明确界定。本文将按类别梳理核心函数,保留关键代码签名,并提炼出三个与性能优化密切相关的要点。


一、函数分类与核心代码

WGSL 内置函数共分为以下几大类,每类中选取高频使用的函数进行解读。

1. 比特位重解释(Bit Reinterpretation)

用于在不同数值类型之间重新解释底层二进制位,常见于打包数据、浮点位操作等场景。

fn bitcast<T>(e : T) -> T                       // 恒等变换(无类型变化)
fn bitcast<T>(e : S) -> T                       // S 与 T 为 i32/u32/f32 之一,且不同
fn bitcast<vecN<T>>(e : vecN<S>) -> vecN<T>     // 向量逐分量重解释

优化提示:bitcast 通常不会被编译为真实指令,但滥用可能影响着色器可移植性,应确保目标类型位数匹配。

2. 逻辑与数组函数

fn all(e: vecN<bool>) -> bool
fn any(e: vecN<bool>) -> bool
fn select(f: T, t: T, cond: bool) -> T          // 三元选择
fn select(f: vecN<T>, t: vecN<T>, cond: vecN<bool>) -> vecN<T>
fn arrayLength(p: ptr<storage, array<E>, AM>) -> u32

select 对应 GLSL 中的 mix 或条件运算符,但 WGSL 要求条件类型严格匹配。

3. 数值函数(Numeric Built-in Functions)

包含数学运算、位操作、插值等大量函数。以下仅列出最具代表性且与性能相关的几个:

fn clamp(e: T, low: T, high: T) -> T
fn mix(e1: T, e2: T, e3: T) -> T                // 线性混合
fn smoothstep(edge0: T, edge1: T, x: T) -> T
fn dot(e1: vecN<T>, e2: vecN<T>) -> T
fn cross(e1: vec3<T>, e2: vec3<T>) -> vec3<T>
fn length(e: T) -> S
fn normalize(e: vecN<T>) -> vecN<T>
fn pow(e1: T, e2: T) -> T
fn exp2(e: T) -> T
fn log2(e: T) -> T
fn abs(e: T) -> T
fn floor(e: T) -> T
fn ceil(e: T) -> T
fn trunc(e: T) -> T
fn fract(e: T) -> T
fn frexp(e: T) -> __frexp_result_f32
fn ldexp(e1: T, e2: I) -> T
fn extractBits(e: T, offset: u32, count: u32) -> T
fn insertBits(e: T, newbits: T, offset: u32, count: u32) -> T
fn countLeadingZeros(e: T) -> T
fn countOneBits(e: T) -> T
fn reverseBits(e: T) -> T

注意:pow 的底数为负数时结果未定义,这在跨平台开发中是常见的坑。

4. 导数函数(Derivative Built-in Functions)

仅在片元着色阶段可用,用于计算屏幕空间导数,常用于纹理过滤和边缘检测。

fn dpdx(e: T) -> T
fn dpdy(e: T) -> T
fn fwidth(e: T) -> T
fn dpdxCoarse(e: T) -> T
fn dpdyFine(e: T) -> T

术语对照:这些函数与 GLSL 中的 dFdx、dFdy、fwidth 直接对应。

5. 纹理函数(Texture Built-in Functions)

纹理操作是 WebGPU 着色器的核心,函数众多,按功能分为查询、采样、加载、存储四类。

查询类

fn textureDimensions(t: T) -> u32 / vec2<u32> / vec3<u32>
fn textureNumLayers(t: T) -> u32
fn textureNumLevels(t: T) -> u32
fn textureNumSamples(t: T) -> u32

采样类

fn textureSample(t: texture_2d<f32>, s: sampler, coords: vec2<f32>) -> vec4<f32>
fn textureSampleBias(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, bias: f32) -> vec4<f32>
fn textureSampleLevel(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, level: f32) -> vec4<f32>
fn textureSampleGrad(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32>
fn textureSampleCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32
fn textureGather(t: texture_2d<ST>, s: sampler, coords: vec2<f32>) -> vec4<ST>
fn textureGatherCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> vec4<f32>

加载与存储类

fn textureLoad(t: texture_2d<ST>, coords: vec2<C>, level: L) -> vec4<ST>
fn textureStore(t: texture_storage_2d<F, AM>, coords: vec2<C>, value: vec4<CF>)

关键区别:textureSample 会自动计算导数,因此不能在非均匀控制流中使用;而 textureSampleLevel 和 textureLoad 则没有此限制。

6. 原子操作(Atomic Built-in Functions)

用于内存共享场景,需在 storage 或 workgroup 地址空间中配合 atomic< T > 类型使用。

fn atomicAdd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
fn atomicSub(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
fn atomicMax(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
fn atomicAnd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
fn atomicCompareExchangeWeak(atomic_ptr: ptr<AS, atomic<T>, read_write>, cmp: T, v: T) -> __atomic_compare_exchange_result<T>

7. 数据打包与解包(Pack / Unpack)

用于高效利用带宽,将多个小数据类型压缩进 32 位整数。

fn pack4x8snorm(e: vec4<f32>) -> u32
fn pack4x8unorm(e: vec4<f32>) -> u32
fn pack2x16float(e: vec2<f32>) -> u32
fn unpack4x8snorm(e: u32) -> vec4<f32>
fn unpack4x8unorm(e: u32) -> vec4<f32>
fn unpack2x16float(e: u32) -> vec2<f32>

这类函数在优化顶点数据存储、纹理数据压缩时非常有用。

8. 同步与子组(Synchronization & Subgroup)

用于 workgroup 内协作和高级并行模式。

fn workgroupBarrier()
fn storageBarrier()
fn textureBarrier()
fn workgroupUniformLoad(p: ptr<workgroup, T>) -> T

fn subgroupAdd(e: T) -> T
fn subgroupBroadcast(e: T, id: I) -> T
fn subgroupShuffle(e: T, id: I) -> T
fn subgroupBallot(pred: bool) -> vec4<u32>

9. 四边形操作(Quad Operations)

与片元着色器中的 2x2 像元块相关,可实现如纹理 LOD 计算等操作。

fn quadBroadcast(e: T, id: I) -> T
fn quadSwapX(e: T) -> T
fn quadSwapY(e: T) -> T
fn quadSwapDiagonal(e: T) -> T

二、三个核心优化要点

从以上函数参考中可以提炼出以下三个与 WebGPU 性能密切相关的优化建议:

1. 避免未定义行为,提升跨设备稳定性

WGSL 明确列出了许多函数的未定义输入范围,例如 pow(x,y) 当 x<0 或 x==0 && y<=0,log(x) 当 x<=0,smoothstep(edge0, edge1, x) 当 edge0>=edge1 等。这些未定义行为在不同硬件上可能产生截然不同的结果,导致着色器在用户设备上出现难以复现的渲染错误。优化要点:在着色器代码中主动钳制输入值,确保始终位于合法区间,这不仅能避免视觉瑕疵,还能减少因驱动差异导致的性能波动。

2. 选择合适的纹理采样函数

textureSample 会隐式计算屏幕空间导数,因此它在非均匀控制流(如分支内)中无法使用,且会触发 uniform 性检查。相比之下:

  • textureSampleLevel 使用显式 LOD,无导数计算,可用于任意着色阶段,适合在计算着色器中采样纹理。
  • textureLoad 直接读取单个纹素,不做任何过滤,是最快且最可控的纹理访问方式。
  • textureSampleGrad 允许手动提供导数,在需要自定义过滤或实现高级效果时非常有用。 优化要点:在计算着色器或不需要过滤的场合,优先使用 textureLoad 或 textureSampleLevel,避免不必要的导数计算和依赖。

3. 利用数据打包和子组操作减少内存带宽

pack4x8unorm、pack2x16float 等函数可以将多个数据分量压缩进单个 32 位值,从而显著减少缓冲区或纹理的存储和传输带宽。同时,subgroup 系列操作(如 subgroupAdd、subgroupBroadcast)允许在 GPU 的子组内快速进行归约和广播,比使用 workgroup 共享内存的 workgroupBarrier 方案通常更高效。优化要点:在顶点数据、粒子属性、颜色等场景中积极使用打包数据类型,并在并行归约、前缀和等算法中优先考虑子组操作,以减少全局内存访问和同步开销。


三、GLSL / WebGL 专业术语检查

尽管本文是 WGSL 函数参考,但许多概念与 GLSL 有对应关系。以下是常见术语对照,帮助有 GLSL 背景的开发者快速迁移:

WGSL 术语GLSL 对应说明
bitcast< T >(e)intBitsToFloat / floatBitsToInt / uintBitsToFloat类型比特位重解释
select(f, t, cond)mix(f, t, cond) 或 cond ? t : f条件选择
textureSampletexture带导数纹理采样
textureSampleLeveltextureLod显式 LOD 纹理采样
textureLoadtexelFetch直接读取纹素
textureGathertextureGather收集四个纹素分量
dpdx / dpdydFdx / dFdy片元着色器导数
fwidthfwidth导数绝对值之和
subgroupAdd / subgroupBroadcastGLSL 的 subgroupAdd / subgroupBroadcast(需扩展)子组操作
workgroupBarrierbarrier()同步屏障,但作用域和内存模型更明确
atomicAdd / atomicMaxatomicAdd / atomicMax(针对 atomic_uint 等)原子操作函数
pack4x8unormpackUnorm4x8数据打包
unpack4x8unormunpackUnorm4x8数据解包

注意事项:

  • WGSL 使用指针参数 ptr<AS, atomic<T>, read_write> 进行原子操作,而 GLSL 通过特殊类型 atomic_uint 和布局限定符实现。
  • WGSL 的内存同步函数区分 storageBarrier、workgroupBarrier 和 textureBarrier,比 GLSL 的单一 barrier() 更精确,有助于编译优化。
  • WGSL 中的向量类型为 vecN<T>(如 vec3<f32>),而 GLSL 使用 vec3 等简写形式,两者在语法上略有差异。

结语

WGSL 函数参考是 WebGPU 开发必不可少的查阅文档。通过理解各类内置函数的适用场景和未定义行为,并合理选择采样、打包和并行操作,可以显著提升着色器的性能与可移植性。希望本深度解读能帮助你在实际项目中更高效地使用 WGSL。