Android 黄油计划和显示刷新机制学习笔记

前置硬件知识

  1. 刷新率(Refresh Rate):代表了屏幕在一秒内刷新屏幕的次数,这取决于硬件的固定参数,例如 60Hz。
  2. 帧率(Frame Rate):代表了 GPU 在一秒内绘制操作的帧数,例如 30fps,60fps。
  3. GPU(Graphics Processing Unit)主要负责 Rasterization(栅格化)操作。栅格化是指将向量图形格式表示的图像转换成位图(像素)以用于显示设备输出的过程,简单来说就是将我们要显示的视图,转换成用像素来表示的格式。栅格化是一个非常耗时的工作。


    图片一

    4.画面撕裂(Tearing) GPU 会获取图形数据进行渲染,然后硬件负责把渲染后的内容呈现到屏幕上,他们两者不停的进行协作。如果刷新率和帧率,各自做自己的事,不相互协调工作,那么刷新频率和帧率并不总能够保持相同的节奏。如果发生帧率与刷新频率不一致的情况,就会容易出现画面撕裂(Tearing)的现象,也就是画面上下两部分显示内容发生断裂,来自不同的两帧数据发生重叠。

Android 黄油计划

为了解决 Android 系统中最饱受诟病的一个问题,滑动不如 iOS 流畅。因此谷歌在 4.1 版本引入了一个重大的改进—Project Butter——黄油计划
Project Butter 对 Android Display 系统进行了重构,引入了三个核心元素,即 VSYNC、Triple Buffer 和 Choreographer,
我们接下来一一介绍

VSYNC(Vertical Synchronization)

垂直同步(vsync)指的是显卡的输出帧数和屏幕的垂直刷新率相同。在当下,垂直同步的含义我们可以理解为,使得显卡生成帧的速度和屏幕刷新的速度的保持一致。举例来说,如果屏幕的刷新率为 60Hz,那么生成帧的速度就应该被固定在 16ms。
上文中我们知道了tearing现象以及产生原因而 VSYNC 最重要的作用是防止出现画面撕裂。

VSYNC 信号是由屏幕(显示设备)产生的,并且以 60fps 的固定频率发送给 Android 系统,Android 系统中的 SurfaceFlinger 接收发送的 VSYNC 信号。VSYNC 信号表明可对屏幕进行刷新而不会产生撕裂。当 SurfaceFlinger 接收到 VSYNC 信号后,SurfaceFlinger 会遍历其层列表,以查找新的缓冲区。如果 SurfaceFlinger 找到新的缓冲区,SurfaceFlinger 会获取缓冲区;否则,SurfaceFlinger 会继续使用上一次获取的那个缓冲区。SurfaceFlinger 必须始终显示内容,因此它会保留一个缓冲区。如果在某个层上没有提交缓冲区,则该层会被忽略。
通常来说,帧率超过刷新频率只是一种理想的状况,在超过 60fps 的情况下,GPU 所产生的帧数据会因为等待 VSYNC 的刷新信息而被 Hold 住,这样能够保持每次刷新都有实际的新的数据可以显示。但是我们遇到更多的情况是帧率小于刷新频率。


图片二

在这种情况下,某些帧显示的画面内容就会与上一帧的画面相同。糟糕的事情是,帧率从超过 60fps 突然掉到 60fps 以下,卡顿掉帧的不顺滑的情况。这也是用户感受不好的原因所在。
接下来,我们以具体示例来看 VSYNC 的作用。

没有 VSYNC 的情况

图片三
  • 这个图中有三个元素,Display 是显示屏幕,GPU 和 CPU 负责渲染帧数据,每个帧以方框表示,并以数字进行编号,如0、1、2等等。
  • CPU 正常执行帧1,GPU 正常渲染帧1,所以帧1正常显示。
    但,CPU 由于被占用等原因,等到即将显示帧2时,它才开始处理第二帧的内容,这显然完不成了,所以等到第二帧显示的时候,只能使用上一帧的内容显示了,也即是丢帧了。
  • 上面丢帧的原因,我们可以从图中看出,是因为新的一帧开始的时候,CPU 在处理其他任务,并没有马上执行下一帧的任务。

使用VSYNC 的情况

图片四
  1. 第0帧显示时,CPU 和 GPU 准备好了第一帧的内容。
  2. 第1帧刚开始显示时,CPU 放下手中的任务,立马处理第2帧显示相关的任务,这样,在第二帧显示之前, CPU 和 GPU 也提前完成了显示任务的处理,第二帧正常显示。

可以看到,使用 VSYNC 信号机制,提升了渲染任务的优先级,优化了渲染性能,可有效的减少了丢帧、卡顿等问题。
但是上图中仍然存在一个问题:CPU 和 GPU 处理数据的速度似乎都能在 16ms 内完成,而且还有时间空余,也就是说,CPU 和 GPU 的帧率要高于 Display 的帧率。由于 CPU/GPU 只在收到 VSYNC 时才开始数据处理,故它们的帧率被拉低到与 Display 相同。但这种处理并没有什么问题,因为 Android 设备的 Display FPS 一般是 60,其对应的显示效果非常平滑。
但如果 CPU/GPU 的帧率小于 Display 的帧率,情况又不同了,将会发生如下图的情况:

图片五

在第二个 16ms 时间段,Display 本应显示 B 帧,但却因为 GPU 还在处理 B 帧,导致 A 帧被重复显示。
同理,在第二个 16ms 时间段内,CPU 无所事事,因为 A Buffer 被 Display 在使用。B Buffer 被 GPU 在使用。注意,一旦过了 VSYNC 时间点,CPU 就不能被触发以处理绘制工作了。
以上是使用双重缓存机制时产生的问题,那么又如何来解决呢?
为了解决这个问题,Android 引入了 Triple Buffer 机制。

三重缓存机制(Triple Buffer)

一般我们在绘制 UI 的时候,都会采用一种称为“双缓存”的技术(例如,上面几个例子)。双缓存意味着要使用两个缓存区,其中一个称为 Front Buffer,另外一个称为 Back Buffer。UI 总是先在 Back Buffer 中绘制,然后再和 Front Buffer 交换,渲染到显示设备中。理想情况下,这样一个刷新会在 16ms 内完成,下图就是描述的这样一个刷新过程:Display 处理前 Front Buffer,CPU、GPU 处理 Back Buffer。

只有两个 Buffer(Android 4.1之前)时,CPU 在空闲时,如果 Back Buffer 被占用了,它也只能等待 GPU 使用之后再次进行写入。我们可以想想,如果有第三个 Buffer 的存在,CPU 是不是就可以提前工作,而不至于空闲了?所以,Google 在 Android4.1 以后,引入了三重缓存机制:Tripple Buffer。Tripple Buffer 利用 CPU/GPU 的空闲等待时间提前准备好数据,并不一定会使用。

引入 Triple Buffer 效果如下图所示:


图片六

上图中,第二个 16ms 时间段,CPU 使用 C Buffer 绘图。虽然还是会多显示 A 帧一次,但后续显示就比较顺畅了。
那么,是不是 Buffer 越多越好呢?回答是否定的。由上图可知,在第二个时间段内,并且大量的缓存数据也会导致内存增大,以及显示数据是否失效等问题。所以,Buffer 三个足矣。

Choreographer

  • Android系统从4.1(API 16)开始加入 Choreographer 这个类来协调动画(animations)、输入(input)、绘制(drawing)三个UI相关的操作

  • Choreographer 中文翻译过来是”编舞者“,字面上的意思就是优雅地指挥以上三个UI操作一起跳一支舞。Choreographer 从显示子系统接收定时脉冲(例如垂直同步——VSYNC 信号),然后安排工作以渲染下一个显示帧。

  • 每个线程都有自己的 Choreographer,其他线程也可以发布回调以在 Choreographer 上运行,但它们是运行在 Choreographer 所属的 Looper 上。

  • 通过 Choreographer 可以注册5种类型的回调:CALLBACK_INPUT(输入回调)、CALLBACK_INPUT(动画回调)、CALLBACK_INSETS_ANIMATION(inset updates 相关的 Animation回调)、CALLBACK_TRAVERSAL(遍历回调)和 CALLBACK_COMMIT(提交回调)。

  • Choreographer 提供了单例调用模式,系统源码中或者我们在开发中都是通过单例调用来使用 Choreographer 的。

  • Android系统通过 Choreographer 的 postCallback 方法,添加回调处理,发起信号同步流程。

  • FrameDisplayEventReceiver(父类: DisplayEventReceiver) 的 scheduleVsync 调用了 native 层方法 nativeScheduleVsync 来实现 VSYNC 信号的请求(向 SurfaceFlinger 服务请求 Vsync 信号)。

  • 下一次 VSYNC 信号接收后会调用 DisplayEventReceiver 的 dispatchVsync 方法。

  • 最终,doFrame 方法顺序执行所有的事件回调。

总结

  1. 刷新率(Refresh Rate):代表了屏幕在一秒内刷新屏幕的次数,这取决于硬件的固定参数,例如 60Hz。

  2. 帧率(Frame Rate):代表了 GPU 在一秒内绘制操作的帧数,例如 30fps,60fps。

  3. GPU 会获取图形数据进行渲染,然后硬件负责把渲染后的内容呈现到屏幕上,他们两者不停的进行协作。

  4. 如果刷新率和帧率,各自做自己的事,不相互协调工作,那么刷新频率和帧率并不总能够保持相同的节奏。如果发生帧率与刷新频率不一致的情况,就会容易出现画面撕裂(Tearing)的现象。

  5. 从 Android 4.1 开始,谷歌在黄油计划中,引入了了三个核心元素,即 VSYNC、Triple Buffer 和 Choreographer。

  6. VSYNC 信号是由屏幕(显示设备)产生的,并且以 60fps 的固定频率发送给 Android 系统,Android 系统中的 SurfaceFlinger 接收发送的 VSYNC 信号。VSYNC 信号表明可对屏幕进行刷新而不会产生撕裂。

  7. 使用 VSYNC 信号机制,提升了渲染任务的优先级,优化了渲染性能,可有效的减少了丢帧、卡顿等问题。

  8. 三重缓存机制(Triple Buffer) 利用 CPU/GPU 的空闲等待时间提前准备好数据,有效的提升了渲染性能

  9. Choreographer 这个类来协调动画(animations)、输入(input)、绘制(drawing)三个UI相关的操作

你可能感兴趣的:(Android 黄油计划和显示刷新机制学习笔记)