4. DRM与KMS核心机制
在Linux图形栈中,DRM(Direct Rendering Manager)负责GPU与显存管理,而KMS(Kernel Mode Setting)负责显示管线的配置与输出。理解KMS的对象模型,是分析显示性能、排查花屏/闪烁/撕裂、以及优化多屏合成路径的基础。KMS把一条显示通路拆成若干可独立配置的对象:Framebuffer、Plane、CRTC、Encoder、Connector,它们通过原子提交(Atomic Commit)一次性生效,避免中间状态导致的显示异常。
核心对象与职责
- Framebuffer(FB):描述一块显存中的像素数据,包含格式、宽高、pitch、修饰符(modifier)等信息。它是“图像源”,本身不决定显示位置。
- Plane:图层,负责把某个Framebuffer按裁剪、缩放、旋转、混合等属性叠加到CRTC上。常见类型有Primary、Cursor、Overlay。Plane决定“怎么合成”。
- CRTC:显示控制器通道,负责扫描输出时序(mode)、刷新率、背景色,并把若干Plane合成后的结果送出。CRTC决定“以什么时序扫描”。
- Encoder:把CRTC的像素流转换为特定接口信号,如HDMI、DisplayPort、DSI、eDP。Encoder决定“用什么编码/接口”。
- Connector:物理输出端口及其连接状态,如HDMI-A-1、DP-1,包含EDID、DPMS、链路训练状态等。Connector决定“接到什么显示器”。
原子提交模式:从“逐步设置”到“一次事务”
传统KMS使用一系列独立ioctl分别设置CRTC、Plane、Connector,容易在切换过程中出现中间状态,例如先改了Plane再改CRTC,导致一帧内出现旧图层与新时序混用。原子提交把一次显示更新抽象为属性集合:每个对象(CRTC/Plane/Connector)都有一组可读写属性,用户态通过drmModeAtomicReq把属性变更逐条加入请求,最后调用一次drmModeAtomicCommit()提交。
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_x, 0);
drmModeAtomicAddProperty(req, plane_id, prop_src_y, 0);
drmModeAtomicAddProperty(req, plane_id, prop_src_w, width << 16);
drmModeAtomicAddProperty(req, plane_id, prop_src_h, height << 16);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_x, 0);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_y, 0);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_w, width);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_h, height);
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK |
DRM_MODE_PAGE_FLIP_EVENT |
DRM_MODE_ATOMIC_ALLOW_MODESET;
int ret = drmModeAtomicCommit(fd, req, flags, user_data);
drmModeAtomicFree(req);
其中DRM_MODE_ATOMIC_TEST_ONLY只做校验不生效,适合在正式提交前探测配置是否合法;DRM_MODE_ATOMIC_NONBLOCK表示异步提交,配合DRM_MODE_PAGE_FLIP_EVENT可在页面翻转完成时收到事件,用于精确控制帧节奏;DRM_MODE_ATOMIC_ALLOW_MODESET允许修改时序等需要重新训练链路的属性。
典型场景:多图层合成与页面翻转
假设一个视频播放器要在4K屏幕上同时显示视频层和字幕层。视频帧放入Overlay Plane,字幕放入另一个Overlay或Primary Plane,两者都绑定到同一个CRTC。用户态每帧更新视频Plane的FB_ID,同时保持字幕Plane不变,然后一次原子提交。内核在垂直消隐期完成Plane切换,避免撕裂。若使用TEST_ONLY先校验,还能提前发现缩放比例超出硬件能力、格式不支持等问题,避免提交失败导致黑屏。
| 对象 | 关键属性 | 性能关注点 |
|---|---|---|
| Framebuffer | format、modifier、pitch | 带宽、压缩、tiling 是否匹配显示控制器 |
| Plane | fb_id、crtc_id、src/dst、zpos、alpha | 缩放单元能力、图层数量上限、混合开销 |
| CRTC | mode、active、background_color | 刷新率、时序裕量、多屏共享资源 |
| Encoder | possible_crtcs、possible_clones | 接口带宽、时钟限制、克隆模式 |
| Connector | CRTC_ID、DPMS、EDID、link_status | 热插拔响应、链路训练失败、带宽协商 |
误区与复盘
TEST_ONLY,直接提交复杂配置,失败后难以定位是哪个属性不合法。误区三:在多线程中共享同一个drmModeAtomicReq,导致属性交叉污染。建议每个提交线程独立分配请求对象,提交后立即释放。
排查显示异常时,可以按对象链路逐级检查:先用modetest或drm_info确认Connector是否连接、CRTC是否active、Plane是否绑定到正确CRTC;再检查Framebuffer的modifier是否被当前硬件支持;最后确认原子提交的flags是否包含必要的ALLOW_MODESET。理解这五个对象与原子提交的关系,就能把“显示不正常”拆解为可验证的属性问题,而不是盲目调整分辨率或刷新率。