显示驱动基础:从像素到画面的第一公里

做显示驱动这么多年,我始终觉得一个道理颠扑不破——基础不牢,地动山摇。很多工程师一上来就追高刷、追低延迟,结果连帧缓冲区怎么工作的都说不清楚。今天咱们就把这第一块基石夯实了。

1.1 显示系统架构:谁在指挥谁?

先看一张图,这是我手绘的显示系统整体框架。你想想看,从你点开一个视频到屏幕上出现画面,中间经历了什么?

显示系统架构概览 应用/游戏引擎 生成渲染指令 GPU/图形驱动 渲染 → 帧缓冲区 帧缓冲区 VRAM / DDR 显示控制器 (DSI/eDP/HDMI) 时序生成 + 数据打包 显示面板 (LCD/OLED) 像素点亮 / 刷新 VSync/Back Porch 反馈 注:实线为数据流,虚线为同步信号反馈

这张图我画了好几个版本才满意。说白了,整个显示系统就是一条流水线:应用层生成内容 → GPU渲染 → 帧缓冲区暂存 → 显示控制器读取 → 面板点亮。每个环节都有坑,我一个个说。

1.2 像素与分辨率:不是点越多越好

像素是什么?就是屏幕上最小的发光单元。一个像素通常由R、G、B三个子像素组成。分辨率呢,就是横向×纵向的像素数量。

但这里有个误区——分辨率高不等于显示效果好。我在项目里遇到过一块4K屏,色彩表现被1080p的OLED屏吊打。为什么?因为像素密度只是一方面,像素的响应时间、色域覆盖、子像素排列方式同样关键。

常见分辨率对照表(我整理项目时常用的)

名称分辨率宽高比总像素数
HD1280×72016:9921,600
FHD1920×108016:92,073,600
QHD2560×144016:93,686,400
4K UHD3840×216016:98,294,400
8K UHD7680×432016:933,177,600

嗯,这里要注意:分辨率翻倍,像素数翻4倍。这意味着带宽需求、功耗、发热都会指数级增长。我见过有人硬上8K@60Hz,结果面板驱动芯片直接过热保护——这就是没算好账。

1.3 刷新率概念:60Hz真的够用吗?

刷新率,就是屏幕每秒更新画面的次数。单位Hz。60Hz就是每秒刷新60次,每帧约16.67ms。

但这里有个容易混淆的点:刷新率 ≠ 帧率。帧率是GPU能生成多少帧,刷新率是屏幕能显示多少帧。两者不匹配时,就会出现撕裂(tearing)或者卡顿(stutter)。

我的经验之谈:

  • 60Hz:日常办公、视频够用。但快速滚动网页时能感觉到闪烁
  • 90Hz:人眼能感知到「流畅」的起点。我用过90Hz的手机后,再回60Hz就觉得卡
  • 120Hz:游戏玩家的甜点频率。延迟从16.7ms降到8.3ms,体感明显
  • 144Hz及以上:专业电竞。说实话,普通人肉眼很难分辨144Hz和240Hz的差别

为什么会这样?因为人眼的视觉暂留效应大约在10ms左右。刷新间隔低于10ms,人眼就很难感知到闪烁了。所以120Hz(8.3ms)已经超过了人眼的感知极限。

1.4 帧缓冲区原理:显存里的那点事

帧缓冲区(Frame Buffer),说白了就是显存里的一块区域,专门存放一帧画面的像素数据。GPU渲染完一帧,写到帧缓冲区;显示控制器从帧缓冲区读数据,送给面板。

这里有个经典问题:双缓冲和三缓冲

单缓冲时代,GPU写和显示控制器读共用同一块内存。结果就是——GPU还没写完,显示控制器就开始读了,画面出现撕裂。我早期做嵌入式显示时就被这个坑过,画面一半是上一帧一半是下一帧,看着像鬼影。

解决方案?双缓冲:

// 伪代码:双缓冲交换机制
while(1) {
    // 前缓冲区:显示控制器正在读取
    // 后缓冲区:GPU正在渲染
    render_to_back_buffer();
    
    // 等待垂直消隐期(VBlank)
    wait_for_vblank();
    
    // 交换前后缓冲区指针
    swap_buffers();
    
    // 此时前缓冲区变成后缓冲区,继续渲染
}

双缓冲解决了撕裂,但引入了新问题——如果GPU渲染速度跟不上刷新率,画面会卡顿。比如屏幕60Hz,GPU只能跑30fps,那同一帧会被显示两次,看起来一卡一卡的。

三缓冲就是再加一个缓冲区,让GPU可以提前渲染下一帧。代价是延迟增加一帧,但画面更平滑。

我曾经踩过的坑:

在做一款高刷平板时,我用了三缓冲,结果触控延迟从20ms飙升到45ms。用户反馈「点下去半天才有反应」。后来发现是三缓冲引入了额外的帧排队延迟。解决方案是:在游戏模式下切回双缓冲,牺牲一点平滑度换响应速度。

所以,没有银弹。双缓冲、三缓冲各有适用场景,得根据产品定位来选。

1.5 时序基础:HBlank和VBlank

显示控制器读取帧缓冲区时,不是一口气读完的。它是一行一行扫描的,从左到右,从上到下。

每扫完一行,需要一段水平消隐期(HBlank)——让电子枪(或者TFT扫描线)回到下一行的起点。每扫完一帧,需要一段垂直消隐期(VBlank)——让扫描回到左上角起点。

这段消隐期不是浪费,恰恰相反:

  • VBlank期间可以安全交换缓冲区,不会产生撕裂
  • HBlank期间可以传输音频或辅助数据(比如eDP的AUX通道)
  • 消隐期也是面板自刷新(PSR)的窗口——静态画面时,显示控制器可以休眠,省电

我习惯用这个公式估算实际带宽需求:

实际带宽 = 分辨率宽 × 分辨率高 × 刷新率 × 每像素位数 × (1 + 消隐开销)

举例:4K@60Hz,8bit RGB
= 3840 × 2160 × 60 × 24 × 1.15
≈ 13.7 Gbps

注意:这是原始数据率,还没算编码开销(比如DSI的8b/9b编码会额外增加12.5%)

你看,算下来4K@60Hz就需要将近14Gbps的带宽。如果上到4K@120Hz,直接翻倍到27Gbps。这就是为什么高刷需要更高速的接口——HDMI 2.1或者DP 1.4起步。

1.6 小结:基础决定上限

这一章的内容,说白了就是显示驱动的「ABC」。像素怎么算、刷新率怎么理解、帧缓冲区怎么管理、时序怎么估算——这些搞不清楚,后面做高刷设计就是空中楼阁。

我个人习惯是,每接手一个新项目,先把这几项参数列个表算一遍。算完心里就有底了:带宽够不够、延迟能不能接受、要不要上双缓冲还是三缓冲。这些决策,都建立在对基础概念的透彻理解上。

下一章咱们会深入具体的时序参数——HFP、HBP、VFP、VBP这些缩写到底代表什么,怎么配置才能让面板正常工作。嗯,到时候再细聊。


公众号:蓝海资料掘金营,微信deep3321