3. DRM架构与核心对象
理解 DRM(Direct Rendering Manager)的关键,不是记住一堆缩写,而是建立一张“对象关系图”。在 Linux 内核中,DRM 把显示管线抽象为若干可枚举、可配置、可共享的内核对象:DRM 设备是总入口,Framebuffer 是像素来源,Plane 负责图层合成,CRTC 负责扫描输出,Encoder 负责信号编码,Connector 负责物理接口,而 Property 则是把这些对象变成可原子配置的“参数面板”。Wayland 合成器正是通过这套对象模型,把客户端表面最终送到屏幕上。
一、核心对象的分工与数据流
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 合成器实现无撕裂、低延迟显示的关键。
EDID;可变属性如 CRTC_ID、FB_ID 可在原子提交中修改;枚举属性如 type 用于标识 Plane 类型。
三、典型场景:Wayland 合成器如何选择显示路径
假设一个 Wayland 合成器要显示一个全屏客户端表面和一个鼠标光标。它会:
- 把客户端 buffer 导入为 DRM Framebuffer,格式为
ARGB8888或XRGB8888。 - 查询 CRTC 上可用的 Plane,优先使用 Primary Plane 承载全屏表面。
- 把光标 buffer 导入为另一个 Framebuffer,绑定到 Cursor Plane。
- 通过 Atomic 提交同时设置两个 Plane 的
FB_ID、CRTC_ID、SRC_X/Y/W/H和CRTC_X/Y/W/H。 - 若硬件支持,合成器可让客户端表面直接扫描输出(Direct Scanout),跳过 GPU 合成,降低功耗和延迟。
如果某个 Plane 不被支持,合成器会回退到 GPU 合成,把多个图层渲染到一个 Framebuffer,再交给 Primary Plane 显示。这种“硬件直出优先、GPU 合成兜底”的策略,正是 DRM 对象模型灵活性的体现。
四、常见误区与排查清单
| 对象 | 核心职责 | 常用调试命令/接口 |
|---|---|---|
| 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 后端、调试黑屏问题,还是优化多屏与高刷新率场景,都可以从这张对象图出发,定位到具体环节。