Android 显示刷新机制、VSYNC和三重缓存机制

Android 显示刷新机制、VSYNC和三重缓存机制Android显示刷新机制、VSYNC和三重缓存机制为了理解APP是如何进行渲染的,我们就必须了解手机硬件是如何工作的,也必须理解什么是VSYNC。首先,我们需要了解2个相关概念:刷新率(RefreshRate):代表了屏幕在一秒内刷新屏幕的次数,这取决于硬件的固定参数,例如60Hz。帧率(FrameRate):代表了GPU在一秒内绘制操作的帧数,例如30fps,60fps。GPU会获取图形数据进行渲染,然后硬件负责把渲染后的内容呈现到屏幕上,他们两者不停的进行协作。

大家好,又见面了,我是你们的朋友全栈君。

Android 显示刷新机制、VSYNC和三重缓存机制


为了理解 APP 是如何进行渲染的,我们就必须了解手机硬件是如何工作的,也必须理解什么是 VSYNC。

首先,我们需要了解2个相关概念:

  • 刷新率(Refresh Rate):代表了屏幕在一秒内刷新屏幕的次数,这取决于硬件的固定参数,例如 60Hz。
  • 帧率(Frame Rate):代表了 GPU 在一秒内绘制操作的帧数,例如 30fps,60fps。

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

在这里插入图片描述

如果刷新率和帧率,各自做自己的事,不相互协调工作,那么刷新频率和帧率并不总能够保持相同的节奏。如果发生帧率与刷新频率不一致的情况,就会容易出现画面撕裂(Tearing)的现象,也就是画面上下两部分显示内容发生断裂,来自不同的两帧数据发生重叠。

在这里插入图片描述
在这里插入图片描述

为了解决 Tearing 问题,Android 引入了 VSYNC 信号以及双重与三重缓存机制。

Android 黄油计划

从 Android 4.1 开始,谷歌致力于解决 Android 系统中最饱受诟病的一个问题,滑动不如 iOS 流畅。因谷歌在 4.1 版本引入了一个重大的改进—Project Butter,也即是黄油计划。

Project Butter 对 Android Display 系统进行了重构,引入了三个核心元素,即 VSYNC、Triple Buffer 和 Choreographer。Choreographer 在之前的文章《从源码分析Choreographer是如何实现VSYNC信号的请求及帧的刷新处理?(Android Q)》中已经分析过了,三重缓存机制我们后面介绍,这里我们重点讲解 VSYNC 的作用。

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 以下,这样就会发生 LAG,JANK,HITCHING 等卡顿掉帧的不顺滑的情况。这也是用户感受不好的原因所在。

接下来,我们以具体示例来看 VSYNC 的作用。

没有使用 VSYNC 时

我们来看没有 VSYNC 的情况:

在这里插入图片描述

这个图中有三个元素,Display 是显示屏幕,GPU 和 CPU 负责渲染帧数据,每个帧以方框表示,并以数字进行编号,如0、1、2等等。

  1. CPU 正常执行帧1,GPU 正常渲染帧1,所以帧1正常显示。
  2. 但,CPU 由于被占用等原因,等到即将显示帧2时,它才开始处理第二帧的内容,这显然完不成了,所以等到第二帧显示的时候,只能使用上一帧的内容显示了,也即是丢帧了。

上面丢帧的原因,我们可以从图中看出,是因为新的一帧开始的时候,CPU 在处理其他任务,并没有马上执行下一帧的任务,那么如何让 CPU 在新的一帧开始的时候立即处理显示内容呢?答案就在 VSYNC 身上!

使用 VSYNC 信号

我们来看,Android 引入 VSYNC 之后的帧执行示意图:

在这里插入图片描述

  1. 第0帧显示时,CPU 和 GPU 准备好了第一帧的内容。
  2. 第1帧刚开始显示时,CPU 放下手中的任务,立马处理第2帧显示相关的任务(这里使用了消息屏障机制,可以参考前文《Android消息循环的同步屏障机制及UI渲染性能的提升(Android Q)》),这样,在第二帧显示之前, 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 越多越好呢?回答是否定的。由上图可知,在第二个时间段内,CPU 绘制的第 C 帧数据要到第四个 16ms 才能显示,这比双 Buffer 情况多了 16ms 延迟,并且大量的缓存数据也会导致内存增大,以及显示数据是否失效等问题。所以,Buffer 三个足矣。

总结


  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 的空闲等待时间提前准备好数据,有效的提升了渲染性能。


**PS:更多精彩内容,请查看 –> 《Android 性能优化》
**PS:更多精彩内容,请查看 –> 《Android 性能优化》
**PS:更多精彩内容,请查看 –> 《Android 性能优化》

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/143501.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 牛站_牛客网站

    牛站_牛客网站链接https://www.acwing.com/problem/content/submission/code_detail/1207146/题目给定一张由T条边构成的无向图,点的编号为1~1

  • getelementbyid属性与用法[通俗易懂]

    getelementbyid属性与用法[通俗易懂]语法:oElement=document.getElementById(sID)参数:sID――必选项。字符串 (String) 。返回值:oElemen――对象 (Element) 。说明:根据指定的 id 属性值得到对象。返回 id 属性值等于 sID 的第一个对象的引用。假如对应的为一组对象,则返回该组对象中的第一个。 如果无符合条件的对象,则返回 nul

  • AVL树—-java

    AVL树—-java

  • 将SOP进行到底 (转载)

    将SOP进行到底 (转载)将SOP进行到底(转载)前段时期,为了强化公司中高层经理人员的“职业经理人”意识和提升大家的“职业经理人”能力,公司组织了中高层经理人员进行学习余世维先生“职业经理人”讲座的演讲交流活动,并组织相关经理人员参加了余世维先生在南京丁山香格里拉举行的“经理人常犯的11种错误”

  • @Mapper 与 @MapperScan 的区别[通俗易懂]

    @Mapper 与 @MapperScan 的区别[通俗易懂]文章目录1、@Mapper注解2、@MapperScan2.1、@MapperScan扫描多个包2.2、@MapperScan使用表达式,来扫描的包和其子包下面的类3、总结:1、@Mapper注解作用:用在接口类上,在编译之后会生成相应的接口实现类位置:对应的某个接口类上面@MapperpublicinterfaceEmployeeMapper{publicEm…

  • 类文件介绍

    类文件介绍

    2020年11月20日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号