4. Framebuffer 与显示流水线:Framebuffer 的创建、格式配置、与 Plane 的绑定,以及显示刷新流程(Page Flip)

Framebuffer 是显示子系统的“画布”,用户空间通过它提交渲染好的像素数据。在 DRM 框架中,Framebuffer 并不直接参与扫描输出,而是作为 内存中的像素数据容器,通过 Plane(图层) 与 CRTC(显示控制器) 协同工作,最终形成完整的显示流水线。本章将深入 Framebuffer 的创建、格式配置、与 Plane 的绑定机制,以及关键的显示刷新流程——Page Flip。

4.1 Framebuffer 的核心概念与生命周期

Framebuffer 本质上是一个 内存对象,它描述了一块或多块物理连续或非连续的内存区域(通常由 GEM 或 DMA-BUF 分配),并携带像素格式、分辨率、行跨度(pitch)等元数据。用户空间通过 drmModeAddFB2() 或 drmModeAddFB() 创建,通过 drmModeRmFB() 销毁。

关键区别: 传统 FBDEV 的 Framebuffer 是内核直接管理的线性显存,而 DRM 的 Framebuffer 是抽象的内存描述符,可以指向 GEM 对象、DMA-BUF 或物理连续内存,灵活性更高。

4.1.1 Framebuffer 创建流程

  1. 分配内存: 用户空间通过 drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB) 或 GEM 分配器创建 GEM 对象。
  2. 填充元数据: 调用 drmModeAddFB2(),传入 GEM handle、宽度、高度、像素格式(如 DRM_FORMAT_XRGB8888)、每个平面的 pitch 和偏移。
  3. 内核校验: DRM 核心调用 fb_create 回调,驱动校验格式、内存对齐、硬件限制。
  4. 返回 FB ID: 成功后返回一个全局唯一的 Framebuffer ID,用户空间后续通过此 ID 引用该 Framebuffer。
参数 说明 典型值
width Framebuffer 的像素宽度 1920
height Framebuffer 的像素高度 1080
pixel_format 四字符编码格式 DRM_FORMAT_NV12
handles[4] 每个平面的 GEM handle handle[0] = 0x1234
pitches[4] 每行字节数(行跨度) 7680 (1920×4)
offsets[4] 每个平面在 GEM 对象中的偏移 0

4.1.2 像素格式配置

DRM 使用四字符编码(FourCC)标识像素格式,常见格式如下:

FourCC 描述 位深 典型用途
DRM_FORMAT_XRGB8888 32位 RGB,无 Alpha 32 桌面 UI、视频叠加
DRM_FORMAT_ARGB8888 32位 RGB,带 Alpha 32 窗口合成、透明叠加
DRM_FORMAT_NV12 YUV 420 半平面 12 视频解码、摄像头预览
DRM_FORMAT_RGB565 16位 RGB 16 低功耗显示、嵌入式设备
格式选择建议: 对于 MIPI DSI 小屏,优先使用 RGB565 或 XRGB8888 以减少带宽;对于视频播放,使用 NV12 可节省内存带宽约 50%。

4.2 Framebuffer 与 Plane 的绑定

Framebuffer 本身不参与显示,必须绑定到 Plane 上才能被 CRTC 扫描输出。Plane 是 DRM 中的图层抽象,每个 Plane 可以绑定一个 Framebuffer,并配置裁剪、缩放、位置等属性。

4.2.1 Plane 类型与绑定关系

  • Primary Plane: 每个 CRTC 必须有一个,通常绑定主 Framebuffer(如桌面背景)。
  • Overlay Plane: 可选,用于叠加视频、光标等,支持 Alpha 混合。
  • Cursor Plane: 专用硬件光标,通常尺寸小(64×64),更新频率高。

绑定操作通过 drmModeAtomicAddProperty() 或 drmModeSetPlane() 完成,核心是设置 FB_ID 属性:

// 使用 Atomic API 绑定 Framebuffer 到 Plane
drmModeAtomicReq *req = drmModeAtomicAlloc();
drmModeAtomicAddProperty(req, plane_id, plane_fb_id_prop, fb_id);
drmModeAtomicAddProperty(req, plane_id, plane_crtc_id_prop, crtc_id);
drmModeAtomicAddProperty(req, plane_id, plane_src_x_prop, 0 << 16);
drmModeAtomicAddProperty(req, plane_id, plane_src_y_prop, 0 << 16);
drmModeAtomicAddProperty(req, plane_id, plane_src_w_prop, width << 16);
drmModeAtomicAddProperty(req, plane_id, plane_src_h_prop, height << 16);
drmModeAtomicAddProperty(req, plane_id, plane_crtc_x_prop, 0);
drmModeAtomicAddProperty(req, plane_id, plane_crtc_y_prop, 0);
drmModeAtomicAddProperty(req, plane_id, plane_crtc_w_prop, display_width);
drmModeAtomicAddProperty(req, plane_id, plane_crtc_h_prop, display_height);
drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
drmModeAtomicFree(req);

4.2.2 绑定关系示意图

Framebuffer 与 Plane 绑定关系 Framebuffer 与 Plane 绑定关系 Framebuffer A FB ID: 0x1001 Framebuffer B FB ID: 0x1002 Framebuffer C FB ID: 0x1003 Primary Plane Plane ID: 0x2001 Overlay Plane Plane ID: 0x2002 Cursor Plane Plane ID: 0x2003 CRTC 扫描输出引擎 每个 Plane 在同一时刻只能绑定一个 Framebuffer,但一个 Framebuffer 可被多个 Plane 引用(如共享内存)。

4.3 显示刷新流程:Page Flip

Page Flip 是 DRM 中 无撕裂(tear-free) 显示更新的核心机制。它通过交换前后缓冲区的指针,在 VBlank 期间完成切换,避免屏幕撕裂。

4.3.1 Page Flip 的两种模式

模式 API 特点 适用场景
Legacy Page Flip drmModePageFlip() 简单,仅支持单个 CRTC,无原子校验 简单桌面、单屏
Atomic Page Flip drmModeAtomicCommit() + DRM_MODE_ATOMIC_NONBLOCK 支持多 CRTC、多 Plane、状态校验、异步提交 现代桌面、Wayland、复杂合成

4.3.2 Page Flip 完整流程

  1. 用户空间提交: 应用程序调用 drmModePageFlip() 或 Atomic Commit,指定新的 Framebuffer ID。
  2. 内核校验: DRM 核心检查新 Framebuffer 的格式、尺寸是否与当前 CRTC 配置兼容。
  3. 等待 VBlank: 驱动注册一个 VBlank 事件回调,等待下一个垂直消隐期。
  4. 硬件切换: 在 VBlank 中断中,驱动更新 CRTC 的扫描缓冲区指针,指向新 Framebuffer。
  5. 事件通知: 驱动通过 drm_crtc_send_vblank_event() 通知用户空间 Page Flip 完成。
  6. 旧缓冲区释放: 用户空间收到完成事件后,可以安全地复用或释放旧 Framebuffer。

4.3.3 Page Flip 时序图

Page Flip 时序流程 Page Flip 时序流程 时间 VBlank VBlank VBlank 用户提交 Page Flip 收到完成事件 内核校验 & 等待 VBlank 硬件切换缓冲区指针 扫描旧 Framebuffer 扫描新 Framebuffer 关键点: 1. 硬件切换发生在 VBlank 期间,保证画面完整。 2. 用户空间必须等待完成事件后才能复用旧缓冲区,否则可能导致撕裂。

4.4 显示流水线完整视图

将 Framebuffer、Plane、CRTC、Encoder、Connector 串联起来,形成完整的显示流水线:

DRM 显示流水线 DRM 显示流水线 Framebuffer Plane CRTC Encoder Connector 像素数据 裁剪/缩放/混合 时序生成 编码转换 物理接口 显示流水线数据流 1. Framebuffer 提供像素数据(内存 → Plane) 2. Plane 进行裁剪、缩放、Alpha 混合(Plane → CRTC) 3. CRTC 生成时序信号并读取混合后的数据(CRTC → Encoder) 4. Encoder 将数据编码为 DSI/LVDS/HDMI 信号(Encoder → Connector)

4.5 调试与常见问题

常见问题:
  • Page Flip 超时: 检查 VBlank 中断是否正常,使用 cat /sys/kernel/debug/dri/0/vblank 查看计数。
  • Framebuffer 格式不匹配: 确保用户空间设置的格式与硬件支持的格式一致,使用 modetest -M msm -e 查看支持的格式。
  • Plane 绑定失败: 检查 Plane 的 possible_crtcs 掩码,确保 Plane 与目标 CRTC 兼容。
调试工具:
  • modetest:测试显示模式、Framebuffer 创建、Page Flip。
  • drm_info:查看 DRM 设备拓扑、Plane 属性、Framebuffer 列表。
  • ftrace:跟踪 drm_atomic_commit、drm_vblank_event 等关键函数。

4.6 本章小结

Framebuffer 是显示流水线的数据源头,通过 Plane 绑定到 CRTC,最终由 Encoder 和 Connector 输出到显示设备。Page Flip 机制保证了无撕裂的帧切换,是高性能显示的核心。理解 Framebuffer 的创建、格式配置、绑定关系以及 Page Flip 流程,是调试 DRM 显示驱动的基础。