1、双缓冲基础概念:什么是屏幕撕裂、帧缓冲与显存、单缓冲的痛点
各位同学,咱们今天聊点实在的。双缓冲这个概念,说白了就是解决「屏幕显示卡顿、撕裂」的一套经典方案。我最早接触它是在做单片机GUI的时候,那时候屏幕小、刷新慢,撕裂现象特别明显。后来做嵌入式Linux、做游戏引擎,发现这玩意儿无处不在。
嗯,咱们先别急着上代码。先把几个最基础的概念掰扯清楚。
1.1 屏幕撕裂:你看到的「半张图」
先问个问题:你有没有在手机或电脑上快速滚动网页时,看到屏幕中间出现一条横线,上半张图和下半张图错位了?
那就是屏幕撕裂(Screen Tearing)。
为什么会这样?
我举个例子。假设你正在用单缓冲模式播放一段60fps的视频。显示器每秒刷新60次,每次刷新时直接从显存里读数据。但你的CPU/GPU也在往显存里写数据——下一帧的画面。
问题来了:如果显示器读到一半,CPU刚好把新数据写进去了。那显示器下半部分读到的就是新一帧,上半部分还是旧一帧。一条撕裂线就出现了。
核心原因:显示器的读取操作和CPU/GPU的写入操作,在同一个缓冲区上发生了「竞态」。没有同步机制,必然撕裂。
我在一个工业HMI项目里遇到过这问题。客户说屏幕偶尔闪一下,我排查了三天,最后发现就是单缓冲导致的撕裂。换双缓冲,问题直接消失。
1.2 帧缓冲与显存:画布和颜料
帧缓冲(Frame Buffer),你可以把它理解成一块内存区域。里面存的是屏幕上每个像素的颜色值。
显存(Video Memory / VRAM),就是存放帧缓冲的物理存储器。在嵌入式设备里,它可能是SDRAM里划出来的一块区域,也可能是独立的显存颗粒。
我习惯把帧缓冲比作「画布」,显存比作「颜料仓库」。画布有多大?取决于分辨率。比如一个800×480的屏幕,每个像素用16位色(RGB565),那帧缓冲大小就是:
800 × 480 × 2 = 768,000 字节 ≈ 750 KB
嗯,750KB。在今天的MCU上不算什么,但在十年前,很多芯片内部RAM才几十KB,根本放不下。那时候就得用外部SDRAM。
| 分辨率 | 色深 | 帧缓冲大小 | 典型场景 |
|---|---|---|---|
| 320×240 | 16位 | 150 KB | 小尺寸TFT、智能穿戴 |
| 800×480 | 16位 | 750 KB | 工业HMI、车载仪表 |
| 1024×600 | 24位 | 1.8 MB | 嵌入式Linux平板 |
| 1920×1080 | 32位 | 8.3 MB | 高性能嵌入式、游戏机 |
避坑指南:我曾经在一个项目里用了24位色深的帧缓冲,结果MCU的DMA带宽不够,刷新率上不去。后来换成16位色深,视觉上几乎看不出差别,但性能翻了一倍。选色深时,别盲目追求高位数。
1.3 单缓冲的痛点:为什么它不够用?
单缓冲(Single Buffering),就是整个系统只有一个帧缓冲。CPU往里面画,显示器从里面读。
听起来简单,但痛点非常明显:
- 撕裂:上面说过了,读写冲突。
- 闪烁:你画图的时候,显示器也在读。如果你先清空缓冲区再画新内容,显示器就会读到「空白」或「半成品」,屏幕会闪。
- 性能瓶颈:CPU必须等显示器完成一次刷新,才能开始画下一帧。否则就会撕裂。这种等待浪费了大量时间。
我举个例子你就明白了。假设你用手绘动画:
- 单缓冲就像你在一张纸上画画,画完一张翻给观众看。但观众看的时候你还在画——他们能看到你的笔迹。
- 双缓冲就像你准备了两张纸。一张给观众看,另一张你在后台偷偷画。画完了,瞬间交换。
观众永远看不到「画到一半」的状态。
注意:单缓冲在极低刷新率或静态画面下还能凑合用。但只要画面动起来,撕裂和闪烁几乎是必然的。我见过有人用单缓冲做菜单切换动画,结果客户反馈「屏幕像在抽风」。后来改成双缓冲,客户说「这才像个正经产品」。
1.4 一张图看懂:单缓冲 vs 双缓冲
下面这张SVG图,把单缓冲的撕裂问题和双缓冲的解决思路画出来了。我建议你仔细看看箭头走向。
你看左侧,单缓冲里CPU和显示器同时操作同一个缓冲区,箭头撞在一起——这就是撕裂的根源。右侧双缓冲,CPU只写后台,显示器只读前台,互不干扰。等后台画完了,在垂直消隐期(VSync)瞬间交换指针,显示器下一帧读到的就是完整的新画面。
1.5 小结:这一章你该记住什么?
- 屏幕撕裂:读写冲突导致的画面错位,单缓冲的典型问题。
- 帧缓冲:一块存像素数据的内存,大小由分辨率和色深决定。
- 显存:存放帧缓冲的物理存储器,嵌入式里常用SDRAM或内部SRAM。
- 单缓冲痛点:撕裂、闪烁、性能浪费。动起来基本没法用。
嗯,基础概念就这些。下一章咱们会深入双缓冲的具体实现——怎么分配缓冲区、怎么交换、VSync怎么用。但今天先把这些根扎牢。
个人习惯:我每次评估一个新平台能不能做流畅UI,第一件事就是看它能不能支持至少两个帧缓冲。如果硬件只给了一个缓冲区,我会直接跟硬件工程师沟通加内存。因为单缓冲的坑,我踩过一次就不想再踩第二次。