【Android】从垂直同步到屏幕刷新机制(一)

发布于:2025-05-20 ⋅ 阅读:(23) ⋅ 点赞:(0)

【Android】从垂直同步到屏幕刷新机制

本文参考以及部分图片来源:

垂直同步_小科普:“垂直同步”究竟是什么?-CSDN博客

“终于懂了” 系列:Android屏幕刷新机制—VSync、Choreographer 全面理解!-腾讯云开发者社区-腾讯云(这篇博客超级详细)

打瓦的时候图像设置这么多,经常在网上看到有提升画面流畅度的设置参考/最佳性能设置参考等等。

img

image-20250518010044893

抄袭网络上各路大佬的设置配置,但是并不理解为什么开关某些设置会对画面造成这么大的影响。今天就从最常见的垂直同步说起,讲讲屏幕刷新机制。

什么是帧?什么是帧率?什么是刷新率?

首先来解释一些基础概念。

1. 帧(Frame)

帧就是一张静态画面,类似翻页动画书中的一页纸。视频、游戏中的动态效果,本质上是通过快速连续播放多帧画面实现的。

关键点

  • 每一帧包含当前时刻画面的完整信息(比如角色位置、颜色等)。
  • 人眼的视觉暂留效应(约0.1秒)让连续帧看起来是连贯的动作。
  • 举例:电影通常每秒播放24帧,游戏可能每秒生成60帧甚至更多。

2. 帧率(Frame Rate / FPS)

每秒生成的帧数,单位是 FPS(Frames Per Second),由内容源(如游戏、视频)决定。

表示 GPU 在一秒内绘制操作的帧数,单位 fps。例如在电影界采用 24 帧的速度足够使画面运行的非常流畅。而 Android 系统则采用更加流程的 60 fps,即每秒钟GPU最多绘制 60 帧画面。帧率是动态变化的,例如当画面静止时,GPU 是没有绘制操作的,屏幕刷新的还是buffer中的数据,即GPU最后操作的帧数据。

3. 刷新率(Refresh Rate)

显示器每秒刷新画面的次数,单位是 Hz(赫兹),由显示器硬件决定。

  • 例如:60Hz显示器每秒刷新60次,144Hz则刷新144次。
  • 每次刷新时,显示器从显卡获取最新帧并显示。

关键点

  • 刷新率是显示器的“物理上限”。
  • 举例:即使游戏生成100FPS,60Hz显示器也只能每秒显示60帧,多余帧会被丢弃或导致画面撕裂。

画面撕裂与掉帧

刚刚图片帖子中贴主提到,在调整完设置之后,不会有画面撕裂和掉帧的现象了。这两个词汇我们平时经常提到,不妨思考两个问题:

掉帧,是说 这一帧延迟显示 还是 丢弃不再显示 ?为什么?

显示器为什么会出现画面撕裂的问题?为什么两个帧会同时显示在屏幕上?

带着这个问题我们来探寻一下,首先依然是给出结论。

1. 画面撕裂(Screen Tearing)

是什么?

显示器在刷新过程中,同时显示了多个帧的部分内容,导致画面被“撕裂”成上下两部分或多块不一致的图像(如下图)。

img

为什么发生?

  • 帧率(FPS) ≠ 刷新率(Hz):当显卡输出帧的速度(FPS)与显示器刷新率不同步时,显示器可能在一次刷新中途切换到新帧,导致新旧帧混杂。
  • 举例:显卡每秒生成100帧(100FPS),显示器是60Hz。显卡生成帧的速度远快于显示器刷新,显示器在刷新到一半时可能突然收到新帧,导致画面撕裂。

如何解决?

  • 开启垂直同步(V-Sync):强制显卡等待显示器刷新完成再输出新帧。
  • 使用自适应同步技术(如G-Sync/FreeSync):让显示器动态调整刷新率,匹配显卡输出的帧率。
  • 限制帧率:手动将游戏帧率限制在显示器刷新率以内(如60FPS配60Hz显示器)。

2. 掉帧(Frame Drops)

是什么?

内容源(如游戏、视频)实际生成的帧数低于预期帧率,导致画面卡顿、不连贯。例如:

  • 游戏标称60FPS,但因性能不足,实际只能生成40FPS → 每秒少了20帧,动作变得卡顿。
  • 视频播放时因解码或带宽问题,部分帧未能及时加载 → 画面突然“跳帧”。

为什么发生?

  • 硬件性能不足:显卡/CPU无法及时渲染足够帧(常见于高画质游戏或复杂场景)。
  • 软件优化差:程序代码效率低,导致帧生成不稳定。
  • 外部干扰:网络延迟(在线视频/直播)、硬盘读取速度慢(加载贴图卡顿)等。

写到这里我们基本上了解了,画面撕裂是由于多个帧的内容被同时显示在屏幕上,而掉帧是实际生成的帧数低于预期帧率。而垂直同步可以强制显卡等待显示器刷新完成再输出新帧,从而帮助我们解决画面撕裂的问题,但可能导致掉帧的问题。

但是开头的两个问题仍未得到解决,并且我们依旧没有真正了解垂直同步和画面撕裂的内部原因。或许我们需要更加深入地学习一下显示系统的基础知识。

显示系统基础知识

基础架构与组件角色

  • CPU:负责生成帧的逻辑数据。处理内容包括应用状态(如游戏角色坐标、UI控件位置)、物理计算、动画逻辑等。输出为图形渲染所需的非像素化数据(如顶点坐标、纹理坐标、材质属性)。
  • GPU:接收CPU传递的图形数据,通过渲染管线(顶点着色→光栅化→像素着色)生成最终像素图像,写入图形缓冲区(Graphic Buffer)。渲染速度取决于着色复杂度(如光照、抗锯齿、分辨率)。
  • 显示器:以固定刷新率(如60Hz)从缓冲区读取像素数据,通过逐行扫描将图像显示到屏幕。每次刷新周期包含有效扫描期(Active)和垂直空白期(VBlank)。

逐行扫描

定义:逐行扫描指显示器从上到下、从左到右依次绘制每一行像素,完整显示一帧画面后,再开始下一帧的扫描。

关键特征

  • 顺序性:每一帧的所有行(如1080p的1920×1080=2073600行)按物理顺序连续扫描。
  • 完整性:每次刷新周期(如16.67ms@60Hz)内,屏幕显示完整的单一帧画面。
  • 同步信号:依赖**水平同步(HSync)垂直同步(VSync)**信号控制扫描节奏。

单缓存

早期的显示系统采用单缓存模式(Single Buffering),即仅使用一个缓冲区(Frame Buffer)供GPU写入渲染数据,同时显示器从中读取数据刷新屏幕。这种设计虽然简单,但存在以下核心问题:

画面撕裂(Screen Tearing)

显示器在逐行扫描过程中,若GPU在屏幕刷新中途更新缓冲区数据,会导致屏幕上半部分显示旧帧、下半部分显示新帧,画面被“撕裂”为不连贯的两部分。这主要是因为GPU和显示器异步访问同一缓冲区,缺乏同步机制。显示器无法感知GPU写入时机,可能读取到部分更新的帧。

示例:假设显示器以60Hz刷新(16.6ms/帧),GPU每10ms渲染一帧:

  • T=0ms:显示器开始扫描第N帧。
  • T=8ms:GPU完成第N+1帧并写入缓冲区。
  • T=8~16.6ms:显示器继续扫描,下半部分显示第N+1帧,上半部分仍为第N帧,画面撕裂。
读写冲突(Read-Write Contention)

GPU和显示器同时访问同一缓冲区时,可能导致数据损坏或需要复杂的锁机制,降低系统效率。

  • GPU写入速度与显示器读取速度不匹配(如GPU写入速度远快于显示器刷新速度)。
  • 单缓存无读写分离机制,硬件需频繁仲裁缓冲区访问权。

双缓存

为克服单缓存缺陷,现代显示系统引入**双缓存(Double Buffering)垂直同步(VSync)**机制:

双缓存结构

  • Front Buffer:仅用于显示器读取(当前显示帧)。
  • Back Buffer:仅用于GPU写入(下一渲染帧)。
  • 缓冲区交换:在显示器垂直空白期(VBlank)交换两者内存地址(非数据拷贝)。

VSync同步

  • 显示器在VBlank期间发送同步信号,强制GPU在此时完成缓冲区交换。
  • 确保显示器每次刷新都读取完整帧,消除撕裂与读写冲突。

双缓存结构

双缓存的核心思想是通过两个独立的缓冲区实现渲染与显示的物理隔离。**Front Buffer(前缓冲区)专门用于显示器逐行扫描输出当前帧,而Back Buffer(后缓冲区)**则留给GPU自由写入下一帧的渲染数据。

这两个缓冲区各司其职——显示器在刷新周期内持续从前缓冲区读取像素,而GPU无需等待显示器的进度,可以全速向后缓冲区填充新帧。这种分离彻底避免了单缓存模式下读写冲突的问题,GPU和显示器不再争夺同一块内存的访问权,数据损坏和锁竞争的风险也随之消失。

垂直同步

然而,双缓存本身并不能完全解决画面撕裂。如果缓冲区交换的时机不当,显示器仍可能扫描到部分更新的帧。此时,垂直同步(VSync)便成为关键的同步机制。其工作原理基于显示器硬件的物理特性:每一帧扫描完成后,显示器会从屏幕右下角回到左上角,开始下一帧的扫描,这段短暂的返回时间称为垂直空白期(VBlank)

VSync信号正是由显示器在VBlank开始时发出的一个脉冲,它像交通信号灯一样,强制GPU在此时间窗口内完成缓冲区的交换。具体来说,当VSync信号触发时,系统会将Front Buffer和Back Buffer的内存地址瞬间互换(而非拷贝数据),确保显示器下一帧扫描的始终是完整的、已渲染好的图像。这种同步机制从根本上消除了画面撕裂的可能,因为显示器每次刷新都从全新的完整帧开始,新旧帧的切换被严格限制在屏幕无输出的“安全期”内。

双缓存带来的问题

尽管双缓存+VSync的组合解决了撕裂问题,但也引入了新的挑战。**输入延迟(Input Lag)**是其中最显著的影响——由于GPU必须等待VSync信号才能开始渲染下一帧,玩家的操作(如点击鼠标或触摸屏幕)需要多等待一个刷新周期(如16.6ms@60Hz)才能反映到画面上,这对竞技类游戏或实时交互场景是致命的。

另一个问题是帧率锁死:若GPU无法在下一个VSync信号到来前完成渲染(例如复杂场景下渲染耗时超过16.6ms),显示器只能重复显示上一帧,导致帧率骤降(如60Hz→30Hz),形成卡顿(Jank)。这种“全有或全无”的机制使得性能波动被放大,帧率只能以刷新率的整数倍(1/2、1/3)跌落,用户体验呈现明显的阶梯式劣化。

借用“终于懂了” 系列:Android屏幕刷新机制—VSync、Choreographer 全面理解!-腾讯云开发者社区-腾讯云博客中的图。

img

在第三帧时,GPU未完成计算,故而发生了重复显示,只能将第二帧显示两次,而后等到下一次VSync再显示。

输入延迟

在双缓存+VSync的架构下,输入延迟的累积源自同步机制对渲染管线的强制阻塞。具体流程如下:

  1. 操作触发:玩家点击鼠标或触摸屏幕(T=0ms)。
  2. CPU处理:游戏引擎接收到输入事件,更新角色位置或射击逻辑(耗时1ms)。
  3. GPU渲染:根据新数据生成下一帧(耗时10ms),但渲染完成时(T=11ms),若距离下一个VSync信号(T=16.6ms)还有5.6ms,GPU必须等待。
  4. 缓冲区交换:直到T=16.6ms(VSync触发),新帧才被提交至显示器。
  5. 显示延迟:显示器从T=16.6ms开始扫描,到T+16.6ms=33.2ms完成显示。

总延迟:从操作触发到画面反馈需33.2ms(操作1ms + 渲染10ms + 等待5.6ms + 显示16.6ms)。

  • 对比无VSync:若关闭VSync,GPU完成渲染后立即交换缓冲区,延迟可降至27.6ms(操作1ms + 渲染10ms + 显示16.6ms)。

下次打瓦的时候输了就可以说自己不小心把垂直同步打开了,导致机器反应慢了(

帧率锁死

当GPU渲染速度无法匹配显示器刷新率时,VSync的“全有或全无”规则导致帧率呈离散式下降:

案例:60Hz显示器 + GPU渲染耗时20ms/帧

  1. VSync 1(T=0ms):GPU开始渲染第1帧至Back Buffer。
  2. VSync 2(T=16.6ms):第1帧未完成(仅渲染16.6ms),无法交换缓冲区,显示器继续显示旧帧(第0帧)。
  3. T=20ms:GPU完成第1帧,但需等待至VSync 3(T=33.3ms)才能交换。
  4. VSync 3(T=33.3ms):显示第1帧,GPU开始渲染第2帧。
  5. VSync 4(T=50ms):第2帧耗时20ms,同样未完成,显示器重复显示第1帧。

结果:实际帧率降至30Hz(1帧/33.3ms),而非理论上的50Hz(1000ms/20ms=50FPS)。帧率被强制对齐到刷新率的分数倍(60/2=30Hz、60/3=20Hz等),形成“阶梯式卡顿”。

三缓存

双缓存与垂直同步(VSync)这一设计通过物理隔离的缓冲区与精准的时序控制,确实解决了撕裂问题,却也带来了输入延迟与帧率锁死的新挑战。

为了突破这些限制,现代技术转向了更灵活的协同机制,其中最具代表性的便是三缓存(Triple Buffering)自适应同步(G-Sync/FreeSync)

依然借用一下这张图。

img

三缓存在双缓存的Front Buffer和Back Buffer基础上,增加一个备用缓冲区(Extra Back Buffer),形成三个缓冲区的环形队列。其核心逻辑是允许GPU在等待VSync期间继续渲染后续帧,而非强制空闲。

优势

减少连续卡顿:单帧超时不会阻塞后续帧的渲染,避免Jank链式反应。

硬件利用率提升:GPU在等待VSync期间可继续工作,算力浪费减少。

代价

内存占用增加:1080p@32位色深下,单缓冲区需8MB,三缓存额外消耗8MB。

潜在延迟增加:帧可能多等待一个VSync周期显示(如第1帧在33.3ms而非16.6ms显示)。

自适应同步

自适应同步的核心思想是让显示器的刷新率实时匹配GPU的输出帧率。这一过程依赖三个关键技术组件:

  1. 可变刷新率显示器:硬件支持刷新率在特定范围内连续变化(如FreeSync的30-144Hz、G-Sync的1-240Hz)。
  2. 同步控制器:集成于显示器或显卡中的芯片,负责监测GPU的帧提交状态,并动态调整显示器的时序信号。
  3. 通信协议:通过DisplayPort或HDMI接口传递同步信号,确保GPU与显示器的指令实时互通。

当GPU完成一帧渲染时,同步控制器立即通知显示器:“这一帧已就绪,现在开始扫描!”显示器随即启动逐行刷新,并在扫描完成后立即准备接收下一帧,而非等待固定的VSync周期。例如,若GPU以45FPS的速度渲染,显示器会将刷新率精准调整为45Hz,每22.2ms刷新一次;若GPU突然加速到90FPS,显示器也瞬间切换至90Hz,每11.1ms刷新一次。这种“帧到帧”的同步彻底消除了传统VSync的等待期,使得每一帧的显示时机与GPU的输出完全契合。

结语

虽然标题带了个Android,但是实际上本文并未特地解析Andorid屏幕刷新机制,这些留到下一期再说~

下一期我们将解析Choreographer的源码,了解一下Android Display系统!


网站公告

今日签到

点亮在社区的每一天
去签到