3. 显示通路软件栈
在SA8155平台中,MDP显示通路从应用程序绘制像素到最终在物理显示屏上呈现,需要经过一个严格分层的软件栈。理解每一层的职责、接口以及数据流传递方式,是进行显示故障定位的基础。本章将详细剖析从应用层到Kernel Mode Driver (KMD) 的完整软件分层架构,重点讲解SurfaceFlinger、HWC (Hardware Composer) 和 DRM/KMS 框架。
3.1 软件分层总览
SA8155的显示软件栈自上而下可分为四个主要层级:应用层、框架层 (Framework)、HAL层 (Hardware Abstraction Layer) 和内核层 (Kernel)。每一层都通过定义良好的接口与相邻层通信。
3.2 应用层与SurfaceFlinger
应用层通过Android的窗口系统与显示框架交互。每个应用窗口对应一个 Surface,应用通过 Canvas 或 OpenGL ES / Vulkan 将内容渲染到该Surface上。渲染完成后,Surface通过 BufferQueue 机制将图形缓冲区传递给 SurfaceFlinger。
3.2.1 BufferQueue 机制
BufferQueue 是连接应用生产者与SurfaceFlinger消费者的核心组件。它采用生产者-消费者模型,通过 dequeueBuffer()、queueBuffer() 和 acquireBuffer() 三个关键方法实现缓冲区的流转。
| 方法 | 调用者 | 功能描述 |
|---|---|---|
dequeueBuffer() |
应用 (生产者) | 从BufferQueue中请求一个空闲缓冲区用于写入新帧 |
queueBuffer() |
应用 (生产者) | 将已填充数据的缓冲区放回队列,通知消费者处理 |
acquireBuffer() |
SurfaceFlinger (消费者) | 从队列中取出一个已就绪的缓冲区用于合成 |
3.2.2 SurfaceFlinger 合成流程
SurfaceFlinger 作为Android显示系统的核心服务,负责收集所有可见窗口的缓冲区,并根据Z-order、透明度、裁剪区域等属性进行合成。其工作流程如下:
- VSync 信号触发: 硬件VSync信号驱动SurfaceFlinger开始新一轮合成。
- 收集可见Surface: 遍历所有窗口,确定当前帧需要参与合成的Surface列表。
- 准备合成: 调用HWC的
prepare()方法,将合成任务分发给硬件或GPU。 - 执行合成: 调用HWC的
set()方法,提交最终的显示帧。 - 提交显示: 通过DRM/KMS将合成后的帧缓冲区提交给显示控制器。
3.3 HWC (Hardware Composer)
HWC 是连接SurfaceFlinger与底层显示硬件的关键HAL模块。它的核心职责是决定如何最高效地合成多个图形层(Layer)并输出到显示设备。HWC通过 IComposer 接口与SurfaceFlinger通信。
3.3.1 HWC 合成策略
HWC 支持两种合成方式:硬件合成 (Device Composition) 和 GPU合成 (GLES Composition)。HWC会根据硬件能力和当前场景动态选择最优策略。
| 合成方式 | 执行者 | 优势 | 劣势 |
|---|---|---|---|
| 硬件合成 (Device) | MDP硬件 | 低功耗、高性能、支持多图层叠加 | 受硬件图层数限制(SA8155 MDP支持最多8个硬件图层) |
| GPU合成 (GLES) | GPU (Adreno) | 灵活、无图层数限制、支持复杂特效 | 功耗高、占用GPU带宽、可能引入额外延迟 |
3.3.2 HWC 关键接口
HWC 的核心接口定义在 hardware/libhardware/include/hardware/hwcomposer.h 中。以下是几个关键方法:
// HWC 核心接口 (简化)
typedef struct hwc_composer_device_1 {
hwc_device_t common;
// 准备合成:传入图层列表,HWC决定哪些图层由硬件合成
int (*prepare)(struct hwc_composer_device_1 *dev,
size_t numDisplays,
hwc_display_contents_1_t **displayContents);
// 执行合成:提交最终的显示帧
int (*set)(struct hwc_composer_device_1 *dev,
size_t numDisplays,
hwc_display_contents_1_t **displayContents);
// 事件回调:注册VSync、热插拔等事件
void (*registerProcs)(struct hwc_composer_device_1 *dev,
hwc_procs_t const *procs);
} hwc_composer_device_1_t;
prepare() 返回 HWC_OVERLAY 或 HWC_GEOMETRY_CHANGED 标志,SurfaceFlinger会重新评估合成策略。频繁的合成策略切换可能导致帧率抖动,这是排查显示卡顿问题的重要线索。
3.4 DRM/KMS 框架
DRM (Direct Rendering Manager) 和 KMS (Kernel Mode Setting) 是Linux内核中用于管理显示输出的标准框架。在SA8155平台上,MDP驱动作为DRM驱动的一部分,通过KMS接口向用户空间暴露显示控制能力。
3.4.1 DRM/KMS 核心对象
DRM/KMS 框架定义了四个核心对象,用于描述和管理显示通路:
| 对象 | 描述 | 对应MDP硬件单元 |
|---|---|---|
| CRTC | 显示控制器,负责从内存中读取像素数据并生成显示时序 | MDP接口 (如 DPU0, DPU1) |
| Encoder | 编码器,将CRTC输出的并行数据转换为特定接口格式 (如DSI, DP) | DSI控制器、DP控制器 |
| Connector | 连接器,代表物理显示接口,负责检测显示器状态 (热插拔、EDID) | DSI端口、DP端口、HDMI端口 |
| Plane | 硬件图层,对应MDP的Pipe (如VIG, DMA, Cursor) | MDP Pipe (VIG0, VIG1, DMA0, DMA1, Cursor) |
3.4.2 DRM/KMS 数据流
从HWC提交帧到最终显示,数据流经过以下步骤:
- 帧缓冲区提交: HWC通过
drmModePageFlip()或drmModeAtomicCommit()将帧缓冲区 (FB) 提交给DRM。 - Plane绑定: 将FB绑定到指定的Plane上,并设置Plane的显示属性(位置、大小、透明度)。
- CRTC配置: 配置CRTC的显示模式(分辨率、刷新率)和扫描参数。
- Encoder/Connector联动: 将CRTC与Encoder、Connector连接,形成完整的显示链路。
- 硬件执行: MDP硬件根据配置从内存中读取像素数据,经过合成、处理,最终输出到显示面板。
3.5 各层交互与数据流总结
为了更清晰地理解整个显示通路的数据流,下表总结了从应用层到KMD的完整交互过程:
| 步骤 | 发起层 | 目标层 | 关键操作 | 传递的数据 |
|---|---|---|---|---|
| 1 | 应用 | SurfaceFlinger | queueBuffer() |
图形缓冲区 (GraphicBuffer) |
| 2 | SurfaceFlinger | HWC | prepare() |
图层列表 (hwc_layer_list) |
| 3 | HWC | SurfaceFlinger | 返回合成策略 | 合成标志 (HWC_OVERLAY / HWC_GEOMETRY_CHANGED) |
| 4 | SurfaceFlinger | HWC | set() |
最终帧缓冲区 (FB) |
| 5 | HWC | DRM/KMS | drmModeAtomicCommit() |
FB ID, Plane配置, CRTC配置 |
| 6 | DRM/KMS | MDP KMD | IOCTL调用 | 硬件寄存器配置 |
| 7 | MDP KMD | MDP硬件 | 寄存器写入 | 像素数据流 |
modetest 确认DRM/KMS层是否正常工作,然后通过 dumpsys SurfaceFlinger 检查SurfaceFlinger的合成状态,最后再定位应用层的问题。这种分层排查方法可以快速缩小问题范围。