3. 软件架构

在 SA8155 这类高通座舱平台上,显示链路并不是单一驱动,而是一条从应用层一直贯穿到硬件显示控制器的纵向软件栈。理解这条栈的分层职责与数据流向,是后续调试黑屏、撕裂、掉帧、多屏不同步等问题的前提。本节从 Android 显示框架出发,逐层向下梳理 SurfaceFlinger、HWC、DRM/KMS、GPU 驱动栈以及内核模块之间的关系。

一、整体分层与数据流

Android 显示系统的核心思想是“合成与提交分离”:应用只负责把内容画到自己的图形缓冲区(GraphicBuffer),由系统统一决定这些缓冲区如何被合成、在哪个显示设备上、以什么时序呈现。SA8155 上这条链路大致分为五层:应用与图形 API 层、SurfaceFlinger 合成层、HWC 硬件抽象层、DRM/KMS 内核显示框架、以及 MSM 显示控制器与 GPU 硬件。

SA8155 Android 显示软件栈分层与数据流 应用 / 图形 API 层 SurfaceView · TextureView · OpenGL ES / Vulkan · EGL · BufferQueue 生产者 SurfaceFlinger 合成层(system_server 进程) Layer 管理 · 事务处理 · 合成策略 · VSYNC 调度 · 输出到 HWC Client 合成(GPU)与 Device 合成(HWC)决策 HWC 硬件合成抽象层(HAL) hwcomposer.msmnile.so · validate / present / getDisplayConfigs 图层叠加能力查询 · 多屏配置 · 提交到 DRM/KMS DRM/KMS 内核显示框架 CRTC · Plane · Encoder · Connector · Atomic Modeset · Framebuffer msm_atomic.c · sde_crtc.c · dpu 驱动 · 提交到显示控制器 MSM 显示控制器 / DPU 与 GPU 硬件 DPU 图层混合 · DSI/DP 输出 · Adreno GPU 渲染 · 显示时序发生器 VSYNC 驱动 合成节奏控制 掉帧/撕裂定位点 /dev/dri/card0 modetest 验证 dmesg 看 SDE 日志

二、各层核心职责

SurfaceFlinger 是 Android 显示的中枢,运行在 system_server 进程中。它维护所有 Layer 的 Z 序、可见性、裁剪与变换,接收应用通过 BufferQueue 提交的图形缓冲区,并在每个 VSYNC 周期决定如何合成。合成方式有两种:Client 合成由 GPU 把多个 Layer 渲染到一个缓冲区,Device 合成则把 Layer 直接交给 HWC 硬件叠加。SA8155 的 DPU 支持多层硬件叠加,因此优先走 Device 合成以降低 GPU 负载与功耗。

HWC(Hardware Composer) 是 SurfaceFlinger 与内核显示驱动之间的 HAL。它向上暴露 validate、present、getDisplayConfigs 等接口,向下通过 DRM/KMS ioctl 提交图层。HWC 的关键能力是“能力查询”:告诉 SurfaceFlinger 当前显示设备能同时叠加多少层、支持哪些格式与缩放。若图层数超过硬件能力,SurfaceFlinger 会回退到 GPU 合成。

DRM/KMS 是 Linux 内核的标准显示框架。KMS 把显示管线抽象为 CRTC(扫描输出控制器)、Plane(图层)、Encoder(编码器)、Connector(物理接口)四类对象。高通在此基础上实现了 msm_atomic、sde_crtc、sde_plane 等驱动,通过 Atomic Modeset 一次性提交整帧配置,保证多图层切换的原子性。

GPU 驱动栈 由内核态 KGSL(Kernel Graphics Support Layer)与用户态 Adreno 驱动组成。KGSL 负责 GPU 命令提交、内存管理、电源与频率调节;用户态驱动把 OpenGL ES / Vulkan 命令翻译为 Adreno 微码。当 SurfaceFlinger 选择 Client 合成时,正是通过 EGL 调用这条栈完成渲染。

三、关键内核模块与设备节点

模块/节点所属层作用
msm_drm.koDRM/KMS高通 DRM 显示驱动主体,注册 card0
msm_atomic.cDRM/KMSAtomic 提交路径,处理 plane/crtc 状态
sde_crtc.cDRM/KMSSDE/DPU 扫描输出与混合配置
dsi_display.ko显示输出MIPI DSI 面板初始化与时序
kgsl.koGPUAdreno GPU 内核驱动,/dev/kgsl-3d0
/dev/dri/card0用户接口DRM 主设备节点,HWC 通过它提交
/sys/class/drm用户接口查看 connector、CRTC 状态与 EDID

四、典型场景:一帧从应用到屏幕

  1. 应用在 UI 线程完成绘制,通过 EGL 把内容渲染到 BufferQueue 的 GraphicBuffer,并 queue 给 SurfaceFlinger。
  2. SurfaceFlinger 收到 VSYNC 信号后,遍历所有可见 Layer,调用 HWC 的 validate 询问硬件能否直接叠加。
  3. HWC 返回每个 Layer 的合成方式;若可 Device 合成,则把 Layer 的 buffer handle 与位置信息打包。
  4. HWC 通过 DRM Atomic ioctl 提交 plane 更新,内核 msm_atomic 校验后写入 SDE 寄存器。
  5. DPU 在下一个 VSYNC 扫描输出,把混合结果经 DSI/DP 送到屏幕。
调试提示:当出现掉帧时,可先用 dumpsys SurfaceFlinger 查看合成方式与 Layer 数量,再用 dmesg | grep sde 确认内核是否报 plane 分配失败,最后用 modetest -M msm_drm 验证 CRTC/Connector 状态。

五、常见误区与复盘

误区一:认为所有合成都由 GPU 完成。实际上 SA8155 优先使用 DPU 硬件叠加,GPU 只在图层超出硬件能力时才参与 Client 合成。
误区二:把 HWC 当成独立进程。HWC 是加载进 SurfaceFlinger 进程的 HAL 库,崩溃会直接导致显示服务重启。
误区三:忽略 DRM 的原子性。多图层必须通过一次 Atomic 提交,分多次提交会造成中间态闪烁。
# 查看当前显示管线状态
adb shell dumpsys SurfaceFlinger --display-id
adb shell dumpsys SurfaceFlinger | grep -A5 "Composition"
# 查看 DRM 设备与连接器
adb shell ls /dev/dri/
adb shell cat /sys/class/drm/card0-DSI-1/status
# 查看 GPU 驱动加载情况
adb shell dmesg | grep -i kgsl