2. Linux图形显示基础

要理解 DRM 与 Wayland 的集成,必须先建立一条完整的“像素从内存到屏幕”的链路认知。Linux 图形栈并不是单一组件,而是由帧缓冲、显示控制器、GPU、KMS、GEM 等多个层次协作构成的系统。本节从最底层的帧缓冲概念出发,逐层向上梳理各组件职责,并回顾 DRM 的历史演进,为后续深入 KMS 与 GEM 打下基础。

一、核心原理:从帧缓冲到显示控制器

帧缓冲(Framebuffer)是一块内存区域,保存了一帧图像中每个像素的颜色值。显示控制器(Display Controller)会周期性地从帧缓冲中读取像素数据,按照扫描顺序输出到显示接口(如 HDMI、DP、MIPI DSI),最终在屏幕上呈现图像。帧缓冲的关键参数包括:

  • 分辨率:宽 × 高,例如 1920×1080。
  • 像素格式:如 XRGB8888、ARGB8888、NV12,决定每个像素占用的字节数与通道排列。
  • stride(行跨距):每行像素在内存中占用的字节数,通常大于“宽度 × 每像素字节数”,用于对齐。
  • 修饰符(modifier):描述内存布局,如线性、平铺(tiled)、压缩等,影响 GPU 与显示控制器的读取效率。

显示控制器内部通常包含扫描引擎、图层合成器(Blender)、色彩管理单元和输出编码器。它支持多个图层(Plane)叠加,例如主图层显示桌面、叠加图层显示鼠标光标或视频,从而减少 GPU 的合成负担。

二、GPU 与显示控制器的分工

GPU 负责图形渲染与计算,显示控制器负责扫描输出。两者通过内存中的缓冲区交换数据。典型流程是:GPU 将渲染结果写入缓冲区,然后通过 KMS 将该缓冲区设置为扫描输出源,显示控制器在下一个垂直同步(VBlank)时切换过去,避免画面撕裂。

关键区分:GPU 决定“画什么”,显示控制器决定“怎么显示”。GPU 渲染到内存,显示控制器从内存读取并输出到屏幕。

三、KMS 与 GEM:DRM 的两大子系统

KMS(Kernel Mode Setting)负责显示模式设置与图层管理,核心对象包括:

  • CRTC:显示控制器实例,代表一条扫描输出通道。
  • Encoder:将 CRTC 的像素流转换为显示接口信号。
  • Connector:物理连接器,如 HDMI-A-1,负责探测连接状态与 EDID。
  • Plane:图层,用于叠加多个帧缓冲。
  • Framebuffer:与具体缓冲区绑定的帧缓冲对象。

GEM(Graphics Execution Manager)负责显存与缓冲区管理,提供缓冲对象的创建、映射、共享和同步机制。GEM 对象可以被 GPU 渲染、被 KMS 扫描输出,也可以导出为 dma-buf 在进程间共享。

# 查看当前 DRM 设备与连接器状态
ls /dev/dri/
cat /sys/class/drm/card0-HDMI-A-1/status
# 输出示例:connected

# 使用 modetest 查看 KMS 资源
modetest -M i915 -c

四、DRM 的历史与演进

在 DRM 出现之前,用户空间通过 FBDEV(framebuffer device)直接操作 /dev/fb0。FBDEV 接口简单,但只支持单图层、无模式设置、无 GPU 同步,无法满足现代合成需求。随着 X11 和 GPU 的发展,内核需要一套统一的图形子系统,DRM 应运而生。

阶段 代表技术 主要能力 局限
早期 FBDEV 直接映射帧缓冲,简单显示 无模式设置、无多图层、无 GPU 同步
DRM 初期 DRM legacy GPU 命令提交、显存管理 模式设置仍在用户态,接口不统一
KMS 引入 DRM + KMS 内核态模式设置、多图层、原子提交 早期驱动适配复杂
现代 Atomic KMS + GEM + dma-buf 原子提交、跨设备共享、Wayland 原生支持 驱动实现门槛高

DRM 的演进可以概括为三个阶段:从 FBDEV 的简单帧缓冲,到 DRM legacy 的 GPU 管理,再到 KMS/GEM 的现代显示与内存管理。原子提交(Atomic Commit)的引入,使得多个图层的状态可以在一次系统调用中整体生效,避免了中间状态导致的闪烁。

五、案例场景:一次页面翻转的完整路径

假设一个 Wayland 合成器要显示新一帧画面,典型路径如下:

  1. 合成器通过 EGL/GLES 调用 GPU 渲染到 GEM 缓冲区。
  2. 渲染完成后,合成器通过 DRM ioctl 将缓冲区绑定为 Framebuffer。
  3. 合成器提交 Atomic Commit,设置 CRTC 的扫描输出源为新 Framebuffer。
  4. 内核在 VBlank 时切换扫描源,显示控制器开始读取新缓冲区。
  5. 旧缓冲区通过 dma-fence 同步后释放或复用。
Linux 图形显示链路:从应用到屏幕 应用 / 合成器 Wayland / X11 GPU 渲染 EGL / GLES / Vulkan GEM 缓冲区 dma-buf / 显存 KMS 内核模式设置 CRTC · Encoder · Connector · Plane · Framebuffer Atomic Commit 显示控制器 扫描输出 / VBlank 屏幕 HDMI / DP / DSI 用户空间 内核空间

六、常见误区与复盘

误区一:认为“帧缓冲就是屏幕”。帧缓冲只是内存中的一块数据,屏幕是显示控制器扫描输出的结果,两者通过 KMS 关联。
误区二:认为“GPU 直接驱动屏幕”。GPU 只负责渲染到内存,真正驱动屏幕的是显示控制器。
误区三:忽略 stride 与 modifier。错误的 stride 会导致图像错位,错误的 modifier 会导致显示花屏或性能下降。

复盘建议:调试显示问题时,先确认 Connector 状态,再检查 CRTC 是否绑定正确的 Framebuffer,最后核对像素格式与 stride。使用 modetest、drm_info 等工具可以快速定位问题所在层次。

掌握本节的关键在于建立“内存 → GPU → GEM → KMS → 显示控制器 → 屏幕”的完整链路认知。后续学习 Wayland 合成器时,你会反复看到这条链路上的各个对象如何被调用与协调。