03-pipeline-and-vertex-processing
第 3 篇:3D 流水线总览与顶点处理(Pipeline Overview, Vertex Processing)
原文:Part 3
一句话定位
顶点阶段把索引与顶点数据组织成可并行批次,在通用 shader 执行单元上运行,然后按后续图元装配需要保存和排序结果。
API 阶段与硬件数据流
API 常把流水线画成 IA → VS → PA → Rasterizer → PS → OM。硬件实现更像多个生产者/消费者队列:固定功能单元准备工作,通用执行单元处理 batch,结果进入缓冲,再交给下一固定功能阶段。阶段边界未必对应独立物理模块。
flowchart LR
I[Index Buffer] --> IA[Input Assembler]
V[Vertex Buffers] --> IA
IA --> C[索引/顶点复用与批次构建]
C --> S[Shader Core 上运行 VS]
S --> O[结果缓冲与顺序整理]
O --> PA[Primitive Assembly]
输入装配与顶点复用
- 索引绘制允许多个图元共享顶点。若一个顶点已经着色且结果仍在缓存中,就不必再次运行 VS。
- 缓存大小有限,驱动/应用提供的索引顺序会影响命中率。网格重排优化的目标之一就是让共享顶点在时间上靠近。
- 硬件通常成批收集“需要新计算的顶点”,同时保留索引到结果位置的映射。批次边界要照顾完整图元,避免后续装配阶段拿不到一整个三角形。
- 非索引绘制省去索引查找,但不能自动获得跨图元复用。
为什么要批处理
单个顶点的 shader invocation 太小,无法高效调度到宽 SIMD/SIMT 机器上。派发器会聚合一组顶点,再把它们作为 wave/warp 类执行批次运行。批处理摊销调度成本,也提供足够的并行工作来隐藏指令与内存延迟。
分支会让同一执行批次中的 lane 走不同路径。硬件常以掩码分段执行各路径,因此控制流分歧会降低有效利用率。这里的关键不是“GPU 不能分支”,而是同一批次越一致越高效。
结果为何还要缓冲
VS 完成顺序可能不同:缓存未命中、分支、纹理访问和资源争用都会引入变化。后续 PA 需要按照正确的图元关系消费顶点,因此需要保存结果并恢复所需顺序。GPU 不是让所有阶段锁步前进,而是在局部使用 FIFO、标签和完成跟踪解耦阶段。
术语对照
| 缩写/英文 | 中文 | 作用 |
|---|---|---|
| IA | 输入装配器 | 解释顶点布局、索引并取数据 |
| VS | 顶点着色器 | 对每个输入顶点产生输出属性 |
| PA | 图元装配 | 按拓扑把顶点组合成点/线/三角形/patch |
| post-transform cache | 变换后缓存 | 复用已着色顶点结果 |
| lane | 执行通道 | 一个 SIMD/SIMT 批次中的元素 |
| divergence | 分歧 | lane 控制流不一致导致分段执行 |
面向 UMD/KMD 的阅读提示
- 这篇主要对应 UMD 的 pipeline/state 编译与硬件前端配置,KMD 通常只看见资源和命令,不理解“这是哪个三角形”。
- 追踪 vertex buffer 描述符:基址、stride、format、offset、越界/鲁棒访问语义在哪里落实?
- 追踪 shader key:哪些 API 状态会产生不同 VS 变体,哪些由固定功能寄存器表达?
- 查 draw packet 如何表达 indexed/non-indexed、base vertex、instance count 与 primitive topology。
自测与实验
- 用同一网格比较顺序索引与随机索引,观察 VS invocation 数或 GPU counter。
- 为什么“VS 输出必须最终按图元关系可重建”不要求每个 batch 的完成时间严格有序?
- instance divisor 应在输入装配的哪个位置影响地址计算?
本篇总结
顶点处理是“索引驱动的数据获取 + 复用 + 宽执行 + 有序交接”,其中缓存与批次组织往往和 ALU 本身同样重要。

浙公网安备 33010602011771号