演进:Android 先在内核外试新做法,验证后并回上游 DRM
结论:Android 显示栈早期在内核外自建了一整套机制:fbdev 上屏、pmem 和 ION 分配显存、自家 sync 框架做同步、HWC 叠加图层。2015 年以后,这些机制逐个合进上游,或者改用上游的同类机制。 今天 Android 和 Linux 桌面共用 DRM/KMS、dma-buf、sync_file 这一套内核接口。这正是前几篇里“内核以下两边共用”的由来。
两条线的交汇:三次交汇奠定了今天的共用接口
Section titled “两条线的交汇:三次交汇奠定了今天的共用接口”- 2012 年前后,两边各自发展。
- 上游解决的是桌面问题:把显存和显示模式收进内核(GEM、KMS),让不同驱动共享 buffer(dma-buf)。
- Android 解决的是手机问题:用 HWC 让显示控制器叠加图层来省电,用 VSYNC 和三缓冲保证流畅,用自家的 sync 框架同步 GPU 和显示。
- 第一次交汇:Android 的 sync 框架合进上游,成为 sync_file。 显式 fence 是 Android 先做出来的,上游 atomic KMS 的
IN_FENCE_FD和OUT_FENCE_PTR直接用了它。 - 第二次交汇:HWC 改用 atomic KMS 实现。
- Google 曾在 2013 年前后另做了一套显示框架 ADF,因为当时 KMS 没有原子提交,满足不了 HWC 每帧换一次配置的需求。
- 上游 atomic KMS 成熟后,ADF 被放弃。
- Android 7.0 推出 HWC2;8.0 的 Treble 把 HAL 和框架解耦。之后高通、联发科、三星等厂商陆续把显示驱动迁到 DRM/KMS。
- 第三次交汇:ION 换成 dma-buf heaps。 Android 12 配合 GKI 通用内核,弃用 ION,改用上游的 dma-buf heaps。之后 Android 13 把 HWC 接口改成 AIDL 的 composer3。
每个阶段解决一个具体问题
Section titled “每个阶段解决一个具体问题”| 阶段 | 时间 | 当时的问题 | 引入的东西 | 解决了什么 |
|---|---|---|---|---|
| 能上屏 | 2008~2010(1.x~2.x) | 尽快把画面显示出来 | SurfaceFlinger 用 GLES 合成;fbdev 上屏;各厂商自己的显存分配器(pmem 等) | 能显示,但全靠 GPU 合成,耗电;分配器互不兼容 |
| 省电和流畅 | 2011~2013(3.0~4.4) | GPU 合成耗电,画面卡顿、撕裂 | HWC 1.x;ION;VSYNC 和三缓冲;自家 sync 框架 | 视频等场景不用 GPU 合成;buffer 统一分配;60fps 流畅 |
| 另起炉灶又放弃 | 2013 前后 | KMS 没有原子提交 | ADF | 很少有厂商采用,上游 atomic KMS 成熟后放弃 |
| 接口规范化 | 2016~2019(7.0~10) | HWC 1.x 语义含糊;HAL 和框架绑死,升级困难 | HWC2;Treble;drm_hwcomposer;厂商迁到 DRM/KMS | HWC 的“试分配再提交”直接对应 atomic 的 TEST_ONLY 和提交 |
| 回归上游 | 2020 至今(11~) | 各厂商的内核分叉太多,没法统一升级 | GKI;dma-buf heaps;AIDL 的 composer3 和 gralloc | 显示驱动就是上游形态的 DRM/KMS 驱动,Android 和 Linux 共用 |
对我们的意义:Android 上的显示驱动已经能直接给 Linux 用
Section titled “对我们的意义:Android 上的显示驱动已经能直接给 Linux 用”演进的终点对 SoC 厂商很有利:
- 显示驱动不用为 Linux 重写。 只要现在的驱动是标准的 atomic KMS 驱动,HWC 能用的,Aquamarine 基本也能用。补齐的主要是 HWC 曾经替驱动兜底的部分,比如 vblank 和 TE 信号、syncobj 声明。
- buffer 和同步机制两边一样。 dma-buf 和 sync_file 本来就是内核标准件,换系统不用改。
- 唯一没并回上游的是 GPU 驱动。 Arm 的 kbase 至今是独立驱动,不是 DRM 驱动。它对应的上游驱动是 panthor,等 panthor 支持 G2 后,GPU 这一层也能和上游完全一致。