帧缓冲Framebuffer)是存放一帧图像像素数据的缓冲区;显示控制器、合成器或 GPU 可以按约定的像素格式、尺寸和步幅读取它,从而生成屏幕上的画面。

它回答的是“这一帧最终要显示的像素数据放在哪里,以及硬件应该按什么格式读它”。

核心问题

屏幕显示需要连续像素流,但应用程序生成的是内存中的图像数据。系统必须约定:

  • 图像宽高是多少。
  • 每个像素如何编码,例如 RGB、BGR、RGBA、YUV。
  • 每一行像素在内存中占多少字节。
  • 多平面格式的亮度和色度数据分别在哪里。
  • 硬件何时可以读取,应用何时可以写入。

帧缓冲就是这些约定和数据存储的组合。它既可以是简单线性内存,也可以是带硬件 tiling、压缩或多平面布局的图形资源。

核心对象

对象含义关注点
pixel format单个像素或采样点的编码方式RGB/BGR、alpha、YUV、bit depth
width / height图像尺寸与显示模式、缩放和裁剪关系
stride / pitch一行图像在内存中的字节跨度不一定等于 width * bytes_per_pixel
memory plane图像格式中的一个内存平面RGB 常单平面,YUV 可能多平面,不等同于 DRM Plane
buffer object实际承载数据的缓冲可由 [[GEM(Graphics Execution Manager)
damage region本帧发生变化的区域合成器优化刷新范围

核心机制

最简单的线性 RGBA 帧缓冲可以用下面的地址计算理解:

address(x, y) = base + y * stride + x * bytes_per_pixel
  • base 是帧缓冲起始地址。
  • (x, y) 是像素坐标,x 向右增加,y 向下增加。
  • stride 是从一行开头到下一行开头的字节数。
  • bytes_per_pixel 是每个像素占用的字节数,例如 RGBA8888 通常是 4。
  • 这个公式只适用于线性布局;如果硬件使用 tiled layout 或压缩布局,地址映射会更复杂,不能按普通二维数组直觉访问。

显示输出时,显示控制器按 扫描输出 时序一行一行读取帧缓冲。双缓冲或三缓冲则会准备多个帧缓冲:

  1. 应用或合成器绘制到 back buffer。
  2. GPU 完成渲染并发出同步信号。
  3. KMS 在 vblank 时把 front buffer 切换为新的 back buffer。
  4. 旧 front buffer 变成可复用缓冲。

这样做的目的是避免显示控制器正在读一块缓冲时,应用又在同一块缓冲上写入,造成画面撕裂或半帧更新。

工程用途

  • 桌面合成器把多个窗口合成为最终帧缓冲,再交给 KMS 显示。
  • 游戏引擎把渲染目标作为帧缓冲,完成后交换到屏幕。
  • 嵌入式设备可以用 framebuffer 或 DRM/KMS 调试最基础的出图路径。
  • 视频播放器常用 YUV 多平面缓冲,交给硬件 overlay 或 GPU 进行显示。

调试时要重点看像素格式、stride、buffer 生命周期、是否等待 GPU fence、显示模式是否匹配,以及多平面格式的 plane 顺序是否正确。

边界与常见坑

  • 帧缓冲不是显示器:它只是内存中的图像数据;显示器是否出图还取决于显示控制器、接口、线缆、背光和模式设置。
  • stride 不一定等于宽度乘像素字节数:硬件常要求行对齐,忽略 stride 会导致图像倾斜、错行或花屏。
  • 像素格式名不能凭颜色顺序猜:内存字节序、通道顺序和 API 命名可能不同。
  • 多缓冲不是自动同步:必须配合 fence、vblank 或页面翻转事件,才能知道哪块缓冲可安全复用。
  • 旧 Linux framebuffer API 和 DRM framebuffer 不是同一层:前者是较老的 /dev/fb* 接口,后者是 DRM/KMS 中用于显示扫描的对象。

相关术语