跳转到内容

两套框架对照:内核以下做一次,用户态各做三件

结论:内核以下的部件两边共用,包括 KMS 显示驱动、kbase、dma-buf 和板级支持,做好一次两套框架都受益。用户态两边各有三件事要做:Android 是 HWC、gralloc、DDK 集成;Linux 是 DDK 打包、Hyprland 补丁、Omarchy 适配。 两套框架行为上最大的差别是合成策略:HWC 尽量让显示控制器叠加图层,Hyprland 全部交给 GPU。这决定了同一台手机在两个系统上的功耗。

逐层对照:每个 Android 部件在 Hyprland 里都有对应物

Section titled “逐层对照:每个 Android 部件在 Hyprland 里都有对应物”

熟悉 Android 显示栈的同事,可以按下表换算:

职责AndroidHyprland关键差别
绘制窗口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
分配 buffergralloc(厂商写)GBM(DDK 自带)Linux 没有独立的分配服务
传递 bufferBLASTBufferQueue + Transactionlinux-dmabuf + wl_surface.commit底下都是 dma-buf 文件描述符
同步显式:acquire / release fence客户端多为隐式(fence 挂在 dma-buf 上);送屏可用 syncobj 显式同步隐式同步依赖 kbase 把 fence 挂到 dma-buf 上
合成SurfaceFlinger:能叠加的交给 plane,叠不下的用 GPUHyprland:全部用 GPU见下一节
送屏HWC(厂商写的 HAL,独立进程)Aquamarine(Hyprland 自带的库,同一进程)两者最终都调用 KMS atomic
设备权限init 和 SELinux 授予 SurfaceFlingerlogind 经 libseat 交给登录会话从串口或 ssh 起 Hyprland 拿不到设备
上屏反馈present fencepresentation-time 协议——

合成策略:HWC 少用 GPU,Hyprland 每帧都用 GPU,功耗差在 DDR 读写

Section titled “合成策略:HWC 少用 GPU,Hyprland 每帧都用 GPU,功耗差在 DDR 读写”

同样是播放视频、上面叠着状态栏和导航栏,两套框架的数据流完全不同:

合成策略对照:HWC 多 plane 叠加与 Hyprland 单 plane GPU 合成

  • Android:三个图层各占一个 plane,显示控制器扫描时直接混合。GPU 不参与合成,每帧也没有额外的 DDR 写入。视频的 YUV 转 RGB 和缩放都由显示控制器完成。
  • Hyprland:Mali 先把三层合成到一块 buffer,主平面再读这块 buffer。每帧多写一整帧、多读一整帧,GPU 还要做 YUV 转换。只有画面不变时才跳过;全屏单窗口可以打开直接扫描输出,绕开合成。

对手机的影响:

场景AndroidHyprland
静止画面不合成不合成
全屏视频plane 直接显示默认要 GPU 合成;开直接扫描输出后与 Android 接近
视频 + 状态栏、滚动列表多 plane 叠加每帧 GPU 合成,GPU 和 DDR 功耗明显更高

显示驱动的 plane 能力两边都要如实上报。Android 用得上全部 plane;Hyprland 目前只用主平面和光标平面,多出来的 plane 闲置。

分工:共用层做好一次,专有层各做三件

Section titled “分工:共用层做好一次,专有层各做三件”

把两套框架都要做的工作按归属拆开:

分工:Android 专有、Linux 专有和两边共用的工作

两边共用,做好一次:

  • 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 标准写,两套框架都能直接用”
  1. 显示驱动不要依赖 HWC 的私有约定。 只用标准 atomic 属性就能点亮屏幕,用 modetest 能出彩条。这样 HWC 和 Aquamarine 都能直接用,也为将来提交主线做好准备。
  2. plane 能力如实上报。 格式、modifier(特别是 AFBC)、缩放能力不准,HWC 的试分配会失败,Hyprland 的直接扫描输出也会出错。
  3. 把 kbase 的隐式 fence 当作 Linux 迁移的验收项。 Android 上从来用不到这条路径,最容易漏测。
  4. 长期关注 Hyprland 的多 plane 支持。 手机上合成功耗是 Hyprland 的主要短板,上游有进展时优先跟进。