两套框架对照:内核以下做一次,用户态各做三件
结论:内核以下的部件两边共用,包括 KMS 显示驱动、kbase、dma-buf 和板级支持,做好一次两套框架都受益。用户态两边各有三件事要做:Android 是 HWC、gralloc、DDK 集成;Linux 是 DDK 打包、Hyprland 补丁、Omarchy 适配。 两套框架行为上最大的差别是合成策略:HWC 尽量让显示控制器叠加图层,Hyprland 全部交给 GPU。这决定了同一台手机在两个系统上的功耗。
逐层对照:每个 Android 部件在 Hyprland 里都有对应物
Section titled “逐层对照:每个 Android 部件在 Hyprland 里都有对应物”熟悉 Android 显示栈的同事,可以按下表换算:
| 职责 | Android | Hyprland | 关键差别 |
|---|---|---|---|
| 绘制窗口 | HWUI + Skia(RenderThread) | Qt Quick / GTK | 都只调用 EGL、GLES 或 Vulkan |
| 帧节拍 | Choreographer,由 VSYNC-app 唤醒 | wl_surface.frame 回调 | Hyprland 在合成时就发回调,不对齐 VSYNC |
| GPU 用户态驱动 | Android 版 DDK(libGLES_mali.so) | Linux 版 DDK(libmali),以后可换 Mesa | 用户态库不能互换,共用 kbase |
| 分配 buffer | gralloc(厂商写) | GBM(DDK 自带) | Linux 没有独立的分配服务 |
| 传递 buffer | BLASTBufferQueue + Transaction | linux-dmabuf + wl_surface.commit | 底下都是 dma-buf 文件描述符 |
| 同步 | 显式:acquire / release fence | 客户端多为隐式(fence 挂在 dma-buf 上);送屏可用 syncobj 显式同步 | 隐式同步依赖 kbase 把 fence 挂到 dma-buf 上 |
| 合成 | SurfaceFlinger:能叠加的交给 plane,叠不下的用 GPU | Hyprland:全部用 GPU | 见下一节 |
| 送屏 | HWC(厂商写的 HAL,独立进程) | Aquamarine(Hyprland 自带的库,同一进程) | 两者最终都调用 KMS atomic |
| 设备权限 | init 和 SELinux 授予 SurfaceFlinger | logind 经 libseat 交给登录会话 | 从串口或 ssh 起 Hyprland 拿不到设备 |
| 上屏反馈 | present fence | presentation-time 协议 | —— |
合成策略:HWC 少用 GPU,Hyprland 每帧都用 GPU,功耗差在 DDR 读写
Section titled “合成策略:HWC 少用 GPU,Hyprland 每帧都用 GPU,功耗差在 DDR 读写”同样是播放视频、上面叠着状态栏和导航栏,两套框架的数据流完全不同:
- Android:三个图层各占一个 plane,显示控制器扫描时直接混合。GPU 不参与合成,每帧也没有额外的 DDR 写入。视频的 YUV 转 RGB 和缩放都由显示控制器完成。
- Hyprland:Mali 先把三层合成到一块 buffer,主平面再读这块 buffer。每帧多写一整帧、多读一整帧,GPU 还要做 YUV 转换。只有画面不变时才跳过;全屏单窗口可以打开直接扫描输出,绕开合成。
对手机的影响:
| 场景 | Android | Hyprland |
|---|---|---|
| 静止画面 | 不合成 | 不合成 |
| 全屏视频 | plane 直接显示 | 默认要 GPU 合成;开直接扫描输出后与 Android 接近 |
| 视频 + 状态栏、滚动列表 | 多 plane 叠加 | 每帧 GPU 合成,GPU 和 DDR 功耗明显更高 |
显示驱动的 plane 能力两边都要如实上报。Android 用得上全部 plane;Hyprland 目前只用主平面和光标平面,多出来的 plane 闲置。
分工:共用层做好一次,专有层各做三件
Section titled “分工:共用层做好一次,专有层各做三件”把两套框架都要做的工作按归属拆开:
两边共用,做好一次:
- KMS 显示驱动:按上游标准写 atomic 回调,接真实的 vblank 中断(命令模式屏要接 TE 信号),声明 syncobj 支持,如实上报 plane 的格式和 modifier。HWC 的
TEST_ONLY试分配和 Aquamarine 的启动检查,靠的都是这些。 - kbase:版本号和两版 DDK 对齐;GPU 写 dma-buf 时把 fence 挂上去。Android 用显式 fence,不依赖这一点;Linux 客户端依赖它。
- dma-buf、dma-buf heaps、sync_file:内核标准件,打开配置即可。
- 板级支持:设备树、屏驱动、电源时钟、IOMMU,和上层框架无关。
Android 专有:
- HWC:plane 分配策略,是 Android 功耗调优的主战场;
- gralloc:按显示控制器能力选格式和 modifier;
- Android 版 DDK 集成:库、固件、系统属性。
Linux 专有:
- Linux 版 DDK 打包:向 Arm 要带 GBM 和 Wayland 的交付件,打成发行版软件包;
- Hyprland 补丁:修 Mali 驱动上必现的两个 bug;
- Omarchy 平台适配:屏幕方向、触摸、启动链。
具体做法见 dev-notes 的《把手机上的 Android 换成 Omarchy》。
建议:显示驱动按上游 atomic 标准写,两套框架都能直接用
Section titled “建议:显示驱动按上游 atomic 标准写,两套框架都能直接用”- 显示驱动不要依赖 HWC 的私有约定。 只用标准 atomic 属性就能点亮屏幕,用
modetest能出彩条。这样 HWC 和 Aquamarine 都能直接用,也为将来提交主线做好准备。 - plane 能力如实上报。 格式、modifier(特别是 AFBC)、缩放能力不准,HWC 的试分配会失败,Hyprland 的直接扫描输出也会出错。
- 把 kbase 的隐式 fence 当作 Linux 迁移的验收项。 Android 上从来用不到这条路径,最容易漏测。
- 长期关注 Hyprland 的多 plane 支持。 手机上合成功耗是 Hyprland 的主要短板,上游有进展时优先跟进。