08-pixel-fork-phase
第 8 篇:像素处理的分叉阶段(Pixel Processing — Fork Phase)
原文:Part 8
一句话定位
光栅器产生的少量有序 quad 流,需要被扩展为大量 shader 工作;派发器在保持必要顺序信息的同时,让数百个执行通道并行运行。
Fork:从窄流到宽执行
flowchart LR
R1[Rasterizer 0] --> Q[Quad/属性队列]
R2[Rasterizer 1] --> Q
Q --> D[批次构建与派发]
D --> W1[Warp/Wave 0]
D --> W2[Warp/Wave 1]
D --> W3[...]
D --> W4[Warp/Wave N]
多光栅器常按屏幕 tile 分工,因为深度、混合等顺序约束都附着于 framebuffer 位置。大三角形可能送到多个光栅器,小三角形通常只落入少数分区;场景集中在一个屏幕区域时会产生负载不均衡。
为什么是 2×2 Quad
像素 shader 的隐式导数通常由相邻像素值做有限差分得到。2×2 quad 同时提供 x、y 方向邻居,便于计算 dFdx/dFdy,也让纹理单元选择 mip 和过滤 footprint。
三角形边缘的 quad 可能只有一部分 lane 真正被覆盖,但未覆盖 lane 仍需运行以提供邻居值,这些就是 helper invocation/pixel。它们通常不应产生可见输出或副作用,却会消耗执行资源。小三角形和细边会让 helper 比例特别高。
属性插值
三角形设置阶段建立属性平面或重心坐标参数,像素阶段按采样位置求值。常见模式包括:
- perspective-correct:默认透视正确插值;
- no-perspective:屏幕空间线性插值;
- flat/constant:整个图元使用 provoking vertex 的值;
- centroid/sample:在 MSAA 下选择位于覆盖区域的点或按 sample 求值。
Centroid 的用途不是“更精确”,而是避免像素中心落在图元外时对属性做危险外推,尤其是纹理 atlas 边缘。
着色器执行与延迟隐藏
派发器把 quad 聚合成 warp/wave。执行单元在一组 lane 上执行同一指令,使用掩码处理分支、覆盖和 helper。纹理访问等长延迟操作发生时,调度器切换到其他 ready wave;因此 occupancy 需要足够,但寄存器和共享资源占用过高会限制并驻 wave 数。
术语对照
| 英文 | 中文理解 | 备注 |
|---|---|---|
| fork phase | 分叉阶段 | 把少量输入流派发到大量并行任务 |
| quad | 2×2 像素组 | 支持 x/y 导数 |
| helper invocation | 辅助调用 | 不覆盖但为导数而执行的 lane |
| warp / wavefront | 执行批次 | 一组锁步/SIMT lane,宽度因架构而异 |
| interpolation | 属性插值 | 从顶点输出得到片段输入 |
| centroid | 覆盖区域内插值位置 | 主要与 MSAA 边缘相关 |
| occupancy | 并驻度 | 可同时驻留的 wave/线程组程度 |
面向 UMD/KMD 的阅读提示
- 编译器需处理 derivative、helper、discard 与 side effect 的精确语义,控制流降级会影响导数是否合法。
- 查看 PS 输入插值描述、flat/centroid/sample 修饰如何映射到硬件参数。
- 驱动性能分析中区分 rasterized samples、PS invocations 和有效写出;三者差距可揭示 overdraw/helper/early-Z 效率。
- 寄存器分配是编译器与硬件占用率的连接点:更少 spill 不总等于更快,过多寄存器也可能减少并驻 wave。
自测与实验
- 渲染面积相同但三角形尺寸不同的网格,比较 PS invocation 与覆盖像素数。
- 为什么在动态非一致控制流中使用隐式导数可能出现未定义或不稳定结果?
- centroid 与 per-sample shading 分别解决什么问题,成本有什么不同?
本篇总结
像素前半程的核心不是“算一个颜色”,而是把屏幕局部工作组织成适合宽机器的 quad/wave,同时维持导数、覆盖和插值语义。

浙公网安备 33010602011771号