frag_depth 一写,early-Z 就被整个 draw call 关掉——Chrome 156 给 WGSL 加的 less/greater 方向词保住了它

写过 WebGPU 三维渲染的人都踩过这个坑:片元着色器里一旦写 @builtin(frag_depth) 自定义深度,整个 draw call 的 early-Z 深度预剔除就被 GPU 悄悄关掉了——帧率掉一截,你连原因都查不到,因为驱动不会给你任何警告。Chrome 156(2026-10-20 上 Stable,Beta 已可测)今天给 WGSL 加了 less / greater 两个方向修饰符,把这个性能暗坑堵上了。

为什么写一行深度,整个 draw call 都要陪葬

early-Z 是 GPU 最基础的优化之一:在跑片元着色器之前,先拿光栅化插值出的深度和深度缓冲比一遍,被遮挡的像素直接丢弃,着色器一秒都不用执行。过度绘制严重的场景(粒子、植被、UI 叠层),early-Z 砍掉的是 30%~70% 的无效着色计算。

问题出在 @builtin(frag_depth):一旦片元着色器可能覆写深度,驱动就无法保证写出的值和插值深度方向一致——着色器可能写得更小,也可能更大。深度方向不确定,early-Z 的剔除结果就不可信,驱动的保守策略是:整个 draw call 的 early-Z 全关。

Chrome 155-156 周期(Francois Beaufort,Chrome for Developers Blog,2026-10-07)把保守深度声明带进了 WGSL:

// before:裸写 frag_depth,early-Z 整个 draw call 被关掉
struct FSOut {
  @location(0) color: vec4f,
  @builtin(frag_depth) depth: f32,
};

// after:声明方向意图,early-Z 保留
struct FSOut {
  @location(0) color: vec4f,
  @builtin(frag_depth, less) depth: f32,   // 写出的深度只会比插值深度更小
  // greater 同理:只会更大
};

less / greater 的语义和 WebGL 时代的 EXT_conservative_depth 一脉相承:着色器承诺写出的深度相对插值深度方向确定,驱动据此判断 early-Z 仍然安全,优化不再陪葬。

这个特性有多硬

  • 落地时间线:Intent to Ship 于 2026-08-24~26 通过 W3C GPU for the Web WG 批准(Safari、Firefox 代表在场),WebKit 公开 Positive 信号,Gecko 尚无信号;rollout 计划 Chrome 桌面 / Android / WebView 156 全量开启,无需 flag。
  • 真实使用方:游戏引擎 Construct(Scirra)在 blink-dev 邮件里直接背书——他们的 WebGL 渲染器已经在用 EXT_conservative_depth 优化 3D 渲染,这个特性把同样的优化路径搬到了 WebGPU。
  • 测试覆盖:gpuweb/cts 的 frag_depth 校验测试 2026-07/08 已合入,随 Chromium 常态跑。

同批上线的还有三个值得顺手看一眼的 WebGPU 更新:纹理压缩非对齐拷贝(texture compression unaligned)、srgb-linear / display-p3-linear 线性色域、snorm10-10-10-2 顶点格式。

三步把这个优化吃进自己的渲染管线

第一步:找出在写 frag_depth 的着色器。 全局搜 WGSL 源码里的 @builtin(frag_depth),每一处都是潜在的 early-Z 损失点——阴影贴图、地形高度混合、体积效果最容易命中。

第二步:给每个命中点加方向修饰符。 想清楚你的深度只会变小还是变大(深度测试 less 配合”只写更近的值”用 less),加完先在 Chrome 156 Beta 验证编译。旧浏览器兜底用 getCompilationInfo() 检查修饰符是否被识别,报错就回退裸写版本:

const mod = device.createShaderModule({ code: wgsl });
const info = await mod.getCompilationInfo();
const bad = info.messages.some(m => m.type === 'error' && /frag_depth/.test(m.message));
const finalWgsl = bad ? wgslBare : wgslQualified; // 双版本择一

第三步:用 timestamp-query 量出来。 打开 timestamp-query 特性,在 pass 前后插 writeTimestamp,对比加修饰符前后的 GPU 耗时——过度绘制密集的 pass 差距最明显,能直接反映在帧时间上。

Chrome 156 Beta 已经可以跑,Stable 定在 2026-10-20。现在把 WGSL 里那些裸写的 frag_depth 逐个标上方向,等 156 全量推开时优化自动生效——这是一次改两行、白拿的渲染性能。

评论区

0 条评论

登录后可评论。

阿速·性能优化 11 阅读