Android刷新机制

《Android应用性能优化最佳实践》

  • 作为严重影响 Android 口碑问题之一的 UI 流畅性差的问题,首先在 Android 4.1 版本中得到了有效处理。其解决方法即在 4.1 版本推出的 Project Butter。Project Butter 对 Android Display系统进行了重构,引入三个核心元素:VSYNC、Triple Buffer和 Choreographer。 其中,VSYNC 是理解 Project Buffer 的核心。VSYNC 是 Vertical Synchronization(垂直同步)的 缩写,是一种在 PC 上已经很早就广泛使用的技术,读者可简单地把它认为是一种定时中断。 Choreographer 起调度的作用,将绘制工作统一到 VSYNC 的某个时间点上,使应用的绘制工 作有序。接下来,本文将围绕 VSYNC 来介绍 Android Display 系统的工作方式。 在讲解刷新机制之前,先介绍几个名词以及 VSYNC 和 Choreographer 主要功能及工作方式。
  • 双缓冲:显示内容的数据内存,为什么要用双缓冲,我们知道在 Linux 上通常使用 Framebuffer 来做显示输出,当用户进程更新 Framebuffer 中的数据后,显示驱动会把 Framebuffer 中每个像素点的值更新到屏幕,但这样会带来一个问题,如果上一帧的数据还 没有显示完,Framebuffer 中的数据又更新了,就会带来残影的问题,给用户直观的感觉就 会有闪烁感,所以普遍采用了双缓冲技术。双缓冲意味着要使用两个缓冲区(在 SharedBufferStack 中),其中一个称为 Front Buffer,另外一个称为 Back Buffer。UI 总是先在 Back Buffer 中绘制,后台绘制好,然后再和 Front Buffer 交换,渲染到显示设备中。即只有 当另一个 buffer 的数据准备好后,通过 io_ctrl 来通知显示设备切换 Buffer
  • VSYNC:从前面的双缓冲介绍中可以了解到,只有当另一个 buffer 准备好后,才能 通知刷新,这就需要 CPU 以主动查询的方式来保证数据是否准备好,因为这种机制效率很 低,所以引入了 VSYNC。VSYNC 是 Vertical Synchronization(垂直同步)的缩写,可以简单地 把它认为是一种定时中断,一旦收到 VSYNC 中断,CPU 就开始处理各帧数据。
  • Choreographer:收到 VSYNC 信号时,调用用户设置的回调函数。一共有以下三种类型 的回调:
    ·CALLBACK_INPUT:优先级最高,与输入事件有关。
    ·CALLBACK_ANIMATION:第二优先级,与动画有关。 ·CALLBACK_TRAVERSAL:最低优先级,与 UI 控件绘制有关。

接下来通过时序图来分析刷新的过程,这些时序图是 Google 在 2012 Google I/O 讲解新 的显示系统提供的,图 2-7 所示的时序图有三个元素:Display(显示设备),CPU-CPU 准备 数据,GPU-GPU 准备数据。最下面的时间为显示时间,根据理想的 60FPS,以 16ms 为一个 显示周期。
在这里插入图片描述
(1)没有 VSync 信号同步
我们以 16ms 为单位来进行分析:

  1. 从第一个 16ms 开始看,Display 显示第 0 帧,CPU 处理完第一帧后,GPU 紧接其后 处理继续第一帧。三者都在正常工作。
  2. 时间进入第二个 16ms:因为在上一个 16ms 时间内,第 1 帧已经由 CPU、GPU 处 理完毕。所以 Display 可以正常显示第 1 帧。显示没有问题,但在本 16ms 期间,CPU 和 GPU 并未及时绘制第 2 帧数据(前面的空白区在忙别事情去了),而是在本周期快结束时, CPU/GPU 才去处理第 2 帧数据。
  3. 时间进入第 3 个 16ms,此时 Display 应该显示第 2 帧数据,但由于 CPU 和 GPU 还 没有处理完第 2 帧数据,故 Display 只能继续显示第一帧的数据,结果使得第 1 帧多画了一 次(对应时间段上标注了一个 Jank),这就导致错过了显示第二帧。 通过上述分析可知,在第二个 16ms 时,发生 Jank 的关键问题在于,为何在第 1 个 16ms 段内,CPU/GPU 没有及时处理第 2 帧数据?从第二个 16ms 开始有一段空白的时间,可以说 明原因所在,那就是 CPU 可能是在忙别的事情,不知道该到处理 UI 绘制的时间了。可 CPU 一旦想起来要去处理第 2 帧数据,时间又错过了。为解决这个问题,4.1 版本推出了 Project Butter,核心目的就是解决刷新不同步的问题。

(2)有 VSync 信号同步
加入 VSync 后,从下图可以看到,一旦收到 VSync 中断,CPU 就开始处理各帧的数 据。大部分的 Android 显示设备刷新率是 60Hz,这也就意味着 每一帧最多只能有 1/60=16ms 左右的准备时间。假如 CPU/GPU 的 FPS 高于这个值,显示效 果将更好。但是,这时又出现了一个新问题:CPU 和 GPU 处理数据的速度都能在 16ms 内完 成,而且还有时间空余,但必须等到 VSYNC 信号到来后,才处理下一帧数据,因此 CPU/GPU 的 FPS 被拉低到与 Display 的 FPS 相同。

在这里插入图片描述

从下图采用双缓冲区的显示效果来看:在双缓冲下,CPU/GPU FPS 大于刷新频率同 时采用了双缓冲技术以及 VSync,可以看到整个过程还是相当不错的,虽然 CPU/GPU 处理所 用的时间时短时长,但总体来说都在 16ms 以内,因而不影响显示效果。A 和 B 分别代表两 个缓冲区,它们不断交换来正确显示画面。
在这里插入图片描述
但如果 CPU/GPU 的 FPS 小于 Display 的 FPS,情况又不同了,如下图所示。
在这里插入图片描述
从上图可以看到,当 CPU/GPU 的处理时间超过 16ms 时,第一个 VSync 就已经到来, 但缓冲区 B 中的数据却还没有准备好,这样就只能继续显示之前 A 缓冲区中的内容。而后 面 B 完成后,又因为还没有 VSync 信号,CPU/GPU 这个时候只能等待下一个 VSync 的来临才 开始处理下一帧数据。因此在整个过程中,有一大段时间被浪费。总结这段话就是:
1)在第二个 16ms 时间段内,Display 本应显示 B 帧,但因为 GPU 还在处理 B 帧,导 致 A 帧被重复显示。
2)同理,在第二个 16ms 时间段内,CPU 无所事事,因为 A Buffer 由 Display 在使用。 B Buffer 由 GPU 使用。注意,一旦过了 VSYNC 时间点,CPU 就不能被触发以及处理绘制工作 了。 为什么 CPU 不能在第二个 16ms 处即 VSync 到来就开始工作呢?很明显,原因就是只 有两个Buffer。如果有第三个Buffer存在,CPU就可以开始工作,而不至于空闲。于是在Andoird 4.1 以后,引出了第三个缓冲区:Triple Buffer。
Triple Buffer 利用 CPU/GPU 的空闲等待时间 提前准备好数据,并不一定会使用。 [注意 在大部分情况下,只使用到双缓存,只有在需要时,才会用三缓冲来增强,这时可以把输入的延迟降到最少,保持画面的流畅。

  • 引入 Triple Buffer 后的刷新时序如下图所示。
    在这里插入图片描述
    在第二个 16ms 时间段,CPU 使用 C Buffer 绘图。虽然还是会多显示一次 A 帧,但后续 显示就比较顺畅了。是不是 Buffer 越多越好呢?回答是否定的。由图 2-11 可知,在第二个 时间段内,CPU 绘制的第 C 帧数据要到第四个 16ms 才能显示,这比双缓存情况多了 16ms批注 [KG1]: CPU 的第三个 A->B 延迟。所以缓冲区不是越多越好,要做到平衡到最佳效果。
发布了119 篇原创文章 · 获赞 28 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ldxlz224/article/details/98651164