3. DRM架构与核心对象

理解 DRM(Direct Rendering Manager)的关键,不是记住一堆缩写,而是建立一张“对象关系图”。在 Linux 内核中,DRM 把显示管线抽象为若干可枚举、可配置、可共享的内核对象:DRM 设备是总入口,Framebuffer 是像素来源,Plane 负责图层合成,CRTC 负责扫描输出,Encoder 负责信号编码,Connector 负责物理接口,而 Property 则是把这些对象变成可原子配置的“参数面板”。Wayland 合成器正是通过这套对象模型,把客户端表面最终送到屏幕上。

DRM 显示管线:从像素到物理接口 DRM Device /dev/dri/card0 Framebuffer 像素缓冲 + 格式 宽高 / 像素格式 Plane Primary / Overlay / Cursor 图层合成与缩放 CRTC 扫描输出控制器 时序 / 刷新率 / 模式 Encoder TMDS / DP / DSI 编码 像素流 → 信号流 Connector HDMI / DP / eDP / VGA EDID / 连接状态 / 模式列表 Property 原子属性键值对 不可变 / 可变 / 枚举 绑定 扫描 编码 输出 配置 Wayland 合成器通过 DRM 原子接口(Atomic API)一次性提交 Plane、CRTC、Connector 的 Property 变更, 由内核校验后同步生效,避免撕裂并支持多图层直出(Direct Scanout)。

一、核心对象的分工与数据流

DRM 设备是用户空间打开 /dev/dri/card0 后获得的总句柄,它管理着显示、渲染、内存管理等多个子系统。显示管线上的对象并不是孤立的,而是通过“绑定关系”串成一条链:

  • Framebuffer:一块带有宽、高、像素格式和内存句柄的像素缓冲,是显示的“画布”。
  • Plane:图层抽象,负责把 Framebuffer 的某块区域合成到 CRTC 的扫描输出中。常见类型有 Primary、Overlay、Cursor。
  • CRTC:阴极射线管控制器(历史名称),现代指扫描输出引擎,决定分辨率、刷新率、时序和输出模式。
  • Encoder:把 CRTC 产生的像素流转换为特定物理接口所需的信号格式,如 TMDS、DP 链路、DSI 等。
  • Connector:物理接口抽象,代表 HDMI、DisplayPort、eDP、VGA 等端口,负责读取 EDID、上报连接状态和可用模式。
  • Property:附加在所有对象上的键值对属性,使对象可以被原子地查询和修改,是 Atomic API 的基础。

数据流方向可以概括为:Framebuffer → Plane → CRTC → Encoder → Connector → 物理屏幕。Wayland 合成器的工作,就是不断把客户端提交的 buffer 包装成 Framebuffer,再通过 Plane 和 CRTC 配置送到 Connector 对应的屏幕上。

二、Property 与原子提交:让配置可验证、可回滚

传统 DRM 模式设置使用 drmModeSetCrtc 等函数,一次只能改一个对象,容易出现中间状态和撕裂。现代 DRM 引入 Atomic API,把所有变更打包成一次原子提交:

drmModeAtomicReq *req = drmModeAtomicAlloc();

drmModeAtomicAddProperty(req, plane_id, prop_fb_id, fb_id);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_id, crtc_id);
drmModeAtomicAddProperty(req, plane_id, prop_src_w, width);
drmModeAtomicAddProperty(req, plane_id, prop_src_h, height);
drmModeAtomicAddProperty(req, crtc_id, prop_mode_id, mode_id);
drmModeAtomicAddProperty(req, connector_id, prop_crtc_id, crtc_id);

drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
drmModeAtomicFree(req);

内核会在提交时检查所有 Property 的组合是否合法,例如 Plane 的格式是否被 CRTC 支持、Connector 是否已连接、模式是否在 EDID 列表中。只有全部通过,配置才会在下一个垂直消隐期生效。这种“先验证、后生效”的机制,是 Wayland 合成器实现无撕裂、低延迟显示的关键。

提示:Property 分为不可变(immutable)、可变(mutable)和枚举(enum)三类。不可变属性在对象创建后只读,如 Connector 的 EDID;可变属性如 CRTC_ID、FB_ID 可在原子提交中修改;枚举属性如 type 用于标识 Plane 类型。

三、典型场景:Wayland 合成器如何选择显示路径

假设一个 Wayland 合成器要显示一个全屏客户端表面和一个鼠标光标。它会:

  1. 把客户端 buffer 导入为 DRM Framebuffer,格式为 ARGB8888 或 XRGB8888。
  2. 查询 CRTC 上可用的 Plane,优先使用 Primary Plane 承载全屏表面。
  3. 把光标 buffer 导入为另一个 Framebuffer,绑定到 Cursor Plane。
  4. 通过 Atomic 提交同时设置两个 Plane 的 FB_ID、CRTC_ID、SRC_X/Y/W/H 和 CRTC_X/Y/W/H。
  5. 若硬件支持,合成器可让客户端表面直接扫描输出(Direct Scanout),跳过 GPU 合成,降低功耗和延迟。

如果某个 Plane 不被支持,合成器会回退到 GPU 合成,把多个图层渲染到一个 Framebuffer,再交给 Primary Plane 显示。这种“硬件直出优先、GPU 合成兜底”的策略,正是 DRM 对象模型灵活性的体现。

四、常见误区与排查清单

误区:把 Connector 当成“屏幕本身”。Connector 只是接口抽象,真正的显示模式来自 EDID,而实际扫描输出由 CRTC 和 Encoder 完成。一个 Connector 可以热插拔,但 CRTC 和 Plane 是常驻的。
对象 核心职责 常用调试命令/接口
DRM Device 总入口,管理显示与渲染资源 ls /dev/dri/
Framebuffer 像素缓冲与格式描述 drmModeAddFB2
Plane 图层合成、缩放、位置 drmModeGetPlaneResources
CRTC 扫描输出、时序、刷新率 drmModeGetCrtc
Encoder 像素流到物理信号的编码 drmModeGetEncoder
Connector 物理接口、EDID、连接状态 drmModeGetConnector
Property 原子配置的键值对 drmModeObjectGetProperties

排查显示问题时,可以按“Connector 是否连接 → CRTC 是否启用 → Plane 是否绑定 → Framebuffer 是否有效”的顺序逐层检查。使用 modetest 可以快速枚举所有对象和 Property:

modetest -M rockchip -c   # 查看 Connector
modetest -M rockchip -p   # 查看 CRTC 和 Plane
modetest -M rockchip -a   # 查看所有 Property

掌握这些对象之间的关系,就掌握了 DRM 显示管线的“骨架”。后续无论是实现 Wayland 后端、调试黑屏问题,还是优化多屏与高刷新率场景,都可以从这张对象图出发,定位到具体环节。