Pipeline
模块二 · 图形学基础
一本写给 Android 显示图形工程师的实时渲染入门教材。
它假设你:读过 libgui / libhwui / SurfaceFlinger / libEGL / libvulkan,清楚一帧 buffer 怎么流转——谁生产、谁消费、什么时候 latch、fence 什么时候 signal。
它要补的:那块 buffer 里的像素本身是怎么被算出来的。你看得见 buffer 进进出出,看不见里面的内容是怎么生成的——这本书讲的就是中间那一段。
它的写法:每篇都从一段你读过的 AOSP 源码开始,抛出几个"你可能从没细想过"的问题,正文推完,结尾回到那段代码把问题答掉。
所以缓冲区、显存、同步、vsync、多缓冲、EGL 上下文这些,一句都不教——你比教材更懂。
篇目
主线是一帧画面的生成顺序:几何进管线 → 变成像素 → 上色 → 合成输出。
| # | 篇目 | 源码锚点 / 要点 |
|---|---|---|
| Part 1 · 几何怎么变成像素 | ||
| 1 | 开篇:buffer 里的像素从哪来 | drawLayersInternal 底下 GPU 干了什么;合成和游戏渲染是不是一回事 |
| 2 | 顶点:三角形怎么被摆到屏幕上 | canvas->concat() 是谁在乘;裁剪为什么反而让三角形变多 |
| 3 | 光栅化:三角形怎么变成像素 | Region 能精确答的题,GPU 为什么只能逐像素问 |
| Part 2 · 像素怎么上色(对你是新大陆,节奏放慢) | ||
| 4 | 纹理:颜色从贴图里采出来 | 那圈 preScale/postScale 在换什么坐标系;GPU 怎么自动挑 mipmap |
| 5 | 深度:谁遮住谁 | 你的 stable_sort 排 Z,游戏为什么不排 |
| 6 | 混合:半透明怎么叠 | kPremul/kUnpremul 差在哪;kOpaque 是个什么承诺 |
| 7 | 光照:颜色怎么算出来 | hwui 里那盏灯照亮了什么 |
| 8 | 阴影:影子怎么来的 | elevation 阴影和游戏阴影是两套算法,不是简化关系 |
| Part 3 · 画面怎么收尾(你的半懂地带,重点篇) | ||
| 9 | 抗锯齿:为什么边缘是毛的 | kSampleCount = 1 为什么还能 enable AA |
| 10 | 帧缓冲与后处理:画到别处再加工 | 背景模糊为什么必须先开一张离屏画布 |
| 11 | 手机 GPU 为什么长这样 | HWC 省的到底是什么;前面那些"移动端例外"的共同根据 |
怎么读
- Part 1(1~3 篇)建议按序读,前一篇的结论是后一篇的前提。第 1 篇会告诉你一个可能反直觉的事实:你的图层合成和游戏渲染走的是同一条管线。
- Part 2(4~8 篇)要慢。合成器没有深度、没有光照、没有阴影,这几篇对你基本是全新的。
- Part 3(9~11 篇)最值得反复读。这三篇讲的东西你天天见到(
setAntiAlias、背景模糊、HWC 合成决策),但可能没系统想过底下的机制——半懂比不懂危险。 - 第 11 篇同时是全书的汇合点:前面几篇留下的"移动端例外"(MSAA 反而便宜、depth pre-pass 可能亏、G-buffer 判死刑)在那里被一次收掉,根据只有一条。
- 每篇末尾的自测答案是折叠的,先自己答一遍再展开。
和模块一的分工
数学一律引用模块一,本模块不重讲。遇到"这个式子从哪来"的疑问,去这几篇:
| 本模块用到 | 去模块一哪篇 |
|---|---|
| 重心坐标、透视校正插值、双线性 | 第 6 篇 插值 |
| 走样、奈奎斯特、卷积、mipmap 为什么存在 | 第 7 篇 采样、走样与滤波 |
| 深度为什么非线性、z-fighting | 第 5 篇 投影与深度 |
| MVP、法线的逆转置陷阱 | 第 4 篇 3D 变换与 MVP 链 |
| 点积的三种用法 | 第 1 篇 坐标系与向量 |
| 线性空间铁律、gamma | 第 8 篇 Gamma 与线性空间 |
源码基准
正文引用的 AOSP 代码摘自 Android 17 分支(本机路径 ~/Project/a17-x6878),贴的是精简片段 + 文件路径,不带行号(行号必随版本漂移)。
⚠️ 那棵树带厂商定制(TranSFEffect、BoxShadowUtils、//SDD: 与 //add for tran 注释块),正文引用的一律是剔除定制分支后的 AOSP 干净版本。你在自己的树上看到的代码可能多出一些厂商分支,主干逻辑一致。
如何复现插图
每篇的插图都由 labs/m2/ 下的同名脚本生成,不是手绘——图里的每个数字都和正文的算例同源。脚本运行时会把正文引用的数值单独打印出来(--- 正文引用数值 --- 一段)。
python3 -m pip install -r ../../labs/requirements.txt
python3 ../../labs/m2/lab_09_antialiasing.py # 重新生成第 9 篇的全部插图脚本可从任意目录运行,会在 docs/assets/generated/ 下原地覆盖对应 SVG 并打印 [plotstyle] saved ...。配色、字号、中文字体统一遵循 STYLE.md。
如果系统 Python 连 pip 都没有,可在源项目的 labs/README.md 中查看 uv 方案。