10-geometry-shaders
第 10 篇:几何着色器(Geometry Shaders)
原文:Part 10
一句话定位
几何着色器以完整图元为输入,并可输出数量可变的新图元;它揭示了“API 中存在一个阶段”并不意味着芯片上必有一个同名独立硬件块。
功能模型与实现模型
API 图会把 GS 永远画在流水线中,即使应用没有绑定 GS;这便于定义功能语义。硬件为了效率,未启用时通常直接旁路,而启用时可能复用通用 shader core,再由固定功能的输入/输出逻辑完成图元拆装。
flowchart LR
P[装配后的图元<br/>可含邻接信息] --> D[GS 派发]
D --> S[通用 Shader Core]
S --> B[可变长度输出缓冲]
B --> A[重新装配输出图元]
A --> R[Rasterization]
B --> SO[可选 Stream-Out]
与 VS/PS 的共性
多个 shader stage 都可抽象为:固定功能单元准备输入 → 聚合批次 → 通用执行单元运行程序 → 结果暂存/排序 → 下一阶段消费。区别主要在输入记录形状、输出协议以及是否存在可变数量输出。
因此读驱动时,不要为每个 API shader stage 预设一套独立执行硬件。很多架构共享指令集、寄存器文件和执行核心,只在前后端控制与可访问系统值上不同。
可变放大的代价
GS invocation 可发出零个、一个或多个图元,导致:
- 输出大小运行前不完全确定,需要预留或分块管理缓冲;
- 不同 invocation 的输出量差异会造成负载不均;
- 需要维持同一 invocation 内的 strip/restart 语义及 invocation 之间的必要顺序;
- 大幅 amplification 会把瓶颈从 shader ALU 转移到输出带宽、缓冲容量或后续光栅化。
GS 能表达点扩展、图层选择、少量几何生成等功能,但“能做”不等于“是最高吞吐路径”。若工作可由 instancing、tessellation、mesh/task 类阶段或 compute 预处理表达,应结合目标硬件测量。
邻接与多次调用
带 adjacency 的拓扑会给 GS 提供邻接顶点,便于轮廓等算法,但输入记录更大、装配更复杂。GS instancing 让同一输入图元触发多个 invocation;它增加并行工作,也放大输出与排序压力。
术语对照
| 英文 | 中文理解 | 说明 |
|---|---|---|
| Geometry Shader | 几何着色器 | 以图元为输入,可生成图元 |
| adjacency | 邻接信息 | 向 invocation 额外提供邻接顶点 |
| amplification | 几何放大 | 输出图元数大于输入 |
| emit / cut | 发射 / 截断 strip | GS 输出协议的一部分 |
| GS instancing | GS 多实例 | 同一输入图元运行多个 invocation |
| bypass | 旁路 | 未启用阶段时直接跳过硬件工作 |
面向 UMD/KMD 的阅读提示
- 编译器后端要处理 GS 输入布局、输出 ring、emit/cut 和最大输出顶点数。
- UMD 需配置中间缓冲大小、图元类型、invocation count 和后续 raster/stream-out 路径。
- 查找“ring buffer”时先辨别它是命令 ring 还是 shader stage 间的数据 ring;同名概念用途完全不同。
- KMD 通常只管理承载这些 ring/状态的内存与提交,不参与几何放大策略。
自测与实验
- 为什么可变输出比固定一进一出的 VS 更难缓冲?
- 未绑定 GS 时,API 图中的 GS 阶段为何仍可在硬件上完全不存在?
- 固定总输出三角形数,比较一次调用高 amplification 与更多输入/低 amplification,观察吞吐差异。
本篇总结
GS 的关键挑战不是“对三角形运行一段代码”,而是把可变长度、可能放大的输出重新组织成有序、可继续消费的数据流。

浙公网安备 33010602011771号