07-depth-stencil
第 7 篇:深度/模板处理的三条路径(Z/Stencil Processing)
原文:Part 7
一句话定位
深度/模板测试既可以在像素着色前尽早拒绝工作,也可能必须在着色后按精确结果更新;硬件用 Early Z、Late Z 与层次测试平衡正确性和效率。
深度插值
经过投影与视口变换后,深度可以在屏幕空间按平面方程增量计算。其他属性通常需要透视正确插值:可以理解为先对与 1/w 相关的量做线性插值,再恢复正确属性,或使用透视正确的重心坐标。
三个层级
Early Z
当 shader 不会改变深度,也不会以影响可见性的方式 kill sample 时,着色前测试能避免大量无用 shader 工作。即使某些情况不能提前写深度,也可能安全地做保守的提前拒绝。
为什么需要 Late Z
以下行为会限制提前测试或提前写入:
- shader 写自定义深度;
discard/alpha test/alpha-to-coverage 等可能删除片段或样本;- shader 副作用与 API 的 early-test 语义相互作用;
- 顺序和同步要求不能通过保守判断满足。
API 或 shader 装饰可显式要求 early tests,但程序必须接受相应语义:例如某些副作用可能在最终片段未产生颜色时已经发生。
Hierarchical Z/Stencil
层次结构为 tile 保存保守摘要,例如一块区域的深度范围。若新三角形的整块深度肯定无法通过,就无需进入逐像素测试。摘要允许假阴性效率损失(没能拒绝),但不能错误拒绝本应通过的样本。
顺序与写入位置
Early 和 Late 阶段若都能写同一深度缓冲,会产生先后批次交叠和竞争风险。真实实现需要集中排序/提交写入、保守延迟写、flush,或使用其他机制保证 API 顺序。性能上“开启深度测试”不是单一开关,而是 shader 行为、深度写、比较函数与批次切换共同决定的路径。
深度压缩与快速清除
深度具有强空间局部性,tile 内值常可用基值/差值或其他元数据压缩,减少带宽。快速清除可只更新元数据,把“整个表面是某个清除值”延迟物化;后续读者若不能理解该压缩状态,就必须先 resolve/decompress。
术语对照
| 英文 | 中文理解 | 关键约束 |
|---|---|---|
| Early Z | 提前深度测试 | 尽早拒绝,必须保持结果等价 |
| Late Z | 延后深度测试 | 可使用 shader 最终深度/存活信息 |
| Hierarchical Z | 层次深度 | tile 级保守拒绝 |
| depth write | 深度写入 | 与测试、顺序和压缩状态耦合 |
| conservative test | 保守测试 | 可漏掉优化机会,不可误杀可见样本 |
| fast clear | 快速清除 | 用元数据表示统一清除值 |
面向 UMD/KMD 的阅读提示
- 编译器分析 shader 是否写 depth、使用 discard、产生存储副作用,并选择 early/late 相关控制位。
- 状态跟踪要知道 depth attachment 的布局、压缩和 clear 元数据;切换用途时可能插入 decompress/resolve/barrier。
- render pass 合并与 tile-based GPU 的 attachment load/store 策略,会显著改变深度流量,但语义问题相同。
- KMD 不判断 early Z 是否安全;它负责相关 BO、同步和队列执行的系统级正确性。
自测
- 为什么
discard可能使“提前写深度”不安全,但仍可能允许“提前拒绝”? - 层次 Z 为什么必须保守?元数据陈旧会导致哪两类结果?
- 关闭深度写但保留深度测试,可能改善或恶化哪些路径?
本篇总结
深度优化的本质是在不改变可观察结果的前提下尽可能早地证明“这批样本没有贡献”,并用层次元数据把证明粒度从样本扩大到 tile。

浙公网安备 33010602011771号