VSYNC 的概念
VSYNC(Vertical Synchronization)是一个相当古老的概念,对于游戏玩家,它有一个更加大名鼎鼎的中文名字—-垂直同步。“垂直同步(vsync)”指的是显卡的输出帧数和屏幕的垂直刷新率相同,这完全是一个CRT显示器上的概念。其实无论是VSYNC还是垂直同步这个名字,因为LCD根本就没有垂直扫描的这种东西,因此这个名字本身已经没有意义。但是基于历史的原因,这个名称在图形图像领域被沿袭下来。在当下,垂直同步的含义我们可以理解为,使得显卡生成帧的速度和屏幕刷新的速度的保持一致。举例来说,如果屏幕的刷新率为60Hz,那么生成帧的速度就应该被固定在1/60 s。
Android中的VSYNC — 黄油计划
从Android 4.1开始,谷歌致力于解决Android系统中最饱受诟病的一个问题,滑动不如iOS流畅。因谷歌在4.1版本引入了一个重大的改进—Project Butter,也即是黄油计划。Project Butter对Android Display系统进行了重构,引入了三个核心元素,即VSYNC、Triple Buffer和Choreographer。关于后面两个概念我们会在后面开专题讲解,这里我们重点讲解VSYNC的作用。玩过大型PC游戏的玩家都知道,VSYNC最重要的作用是防止出现画面撕裂(screentearing)。所谓画面撕裂,就是指一个画面上出现了两帧画面的内容,如下图。
为什么会出现这种情况呢?这种情况一般是因为显卡输出帧的速度高于显示器的刷新速度,导致显示器并不能及时处理输出的帧,而最终出现了多个帧的画面都留在了显示器上的问题。这也就是我们所说的画面撕裂。
回到正文中来,那么VSync除了可以解决画面的撕裂的问题,还可以解决别的什么问题吗?我们来看下图:提到垂直同步这里就多提一句,其实我认为对于PC上的大型游戏来说,只有配置足够高,高到显卡输出帧率可以稳定的高于显示器的刷新频率,才有开启垂直同步的必要。因为只有这个时候,画面撕裂才会真正成为一个问题。而对于很多情况下主机性能不足导致游戏输出帧率低于显示器的刷新频率的情况下,尤其是帧率稳定在40~60之间时,开启垂直同步可能会导致帧率倍数级的下降(具体原因我们在Graphic架构一文中提到过,当帧生成速度不及VSync速度时,帧率的下降不是平缓的,而且很可能是倍数级的。当然这在android系统上并非严重问题,因为android上很少有高速的复杂场景的频繁切换。事实上,在Android的普通应用场景下,VSync的使用不仅不会降低帧率,还可以有效解决卡顿问题)。
这个图中有三个元素,Display是显示屏幕,GPU和CPU负责渲染帧数据,每个帧以方框表示,并以数字进行编号,如0、1、2等等。VSync用于指导双缓冲区的交换。以时间的顺序来看下将会发生的异常:
Step1. Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,而且赶在Display显示下一帧前完成。
Step2. 因为渲染及时,Display在第0帧显示完成后,也就是第1个VSync后,正常显示第1帧。
Step3. 由于某些原因,比如CPU资源被占用,系统没有及时地开始处理第2帧,直到第2个VSync快来前才开始处理
Step4. 第2个VSync来时,由于第2帧数据还没有准备就绪,显示的还是第1帧。这种情况被Android开发组命名为“Jank”。
Step5. 当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync。所以总的来说,就是屏幕平白无故地多显示了一次第1帧。原因大家应该都看到了,就是CPU没有及时地开始着手处理第2帧的渲染工作,以致“延误军机”。
其实总结上面的这个情况之所以发生,首先的原因就在于第二帧没有及时的绘制(当然即使第二帧及时绘制,也依然可能出现Jank,这就是同时引入三重缓冲的作用。我们将在三重缓冲一节中再讲解这种情况)。那么如何使得第二帧及时被绘制呢?这就是我们在Graphic系统中引入VSYNC的原因,考虑下面这张图:
如上图所示,一旦VSync出现后,立刻就开始执行下一帧的绘制工作。这样就可以大大降低Jank出现的概率。另外,VSYNC引入后,要求绘制也只能在收到VSYNC消息之后才能进行,因此,也就杜绝了另外一种极端情况的出现—-CPU(GPU)一直不停的进行绘制,帧的生成速度高于屏幕的刷新速度,导致生成的帧不能被显示,只能丢弃,这样就出现了丢帧的情况—-引入VSYNC后,绘制的速度就和屏幕刷新的速度保持一致了。