Android VSYNC与图形系统中的撕裂、双缓冲、三缓冲浅析

简介: Android VSYNC与图形系统中的撕裂、双缓冲、三缓冲浅析

先接触两个图形概念: 帧率(Frame Rate,单位FPS)--GPU显卡生成帧的速率,也可以认为是数据处理的速度), 屏幕刷新频率 (Refresh Rate单位赫兹/HZ):是指硬件设备刷新屏幕的频率。屏幕刷新率一般是固定的,比如60Hz的每16ms就刷一次屏幕,可以类比一下黑白电视的电子扫描枪,每16ms电子枪从上到下从左到右一行一行逐渐把图片绘制出来,如果GPU显卡性能非常强悍,帧率可以非常高,甚至会高于屏幕刷新频率。


单缓存画面撕裂与(垂直同步+双缓冲)


什么是画面撕裂?如下:用两帧的部分数据合成一帧。


image.png

The display (LCD, AMOLED, whatever) gets each frame from the graphics chip, and starts drawing it line by line. Ideally, you want the display to get a new frame from the graphics chip after it is finished drawing the previous frame. Tearing occurs when the graphics chip loads a new frame in the middle of the LCD draw, so you get half of one frame and half of another.


如果只有一块缓存,在没有加锁的情况下,容易出现。即:在屏幕更新的时候,如果显卡输出帧率很高,在A帧的数据上半部分刚更新完时,B帧就到了,如果没采取同步锁机制,可以认为帧到了就可用,在继续刷新下半部分时,由于只有一块存储,A被B覆盖,绘制用的数据就是B帧,此时就会出现上半部分是A下半部分是B,这就是屏幕撕裂,个人觉得描述成显卡瞬时帧率过高也许更好。同正常帧绘制相比,正常的帧给时间才就能完整绘制一帧,但撕裂的帧没有机会补全。

image.png

相比较画面撕裂场景如下:

image.png

不过按照Android官方指导的说法,屏幕撕裂还有另外一种解释,那就是显示器用了半成品的帧,不过我是不太理解他说的这点。参考视频


以上说的是只有一块显示存储的情况,其实只要加锁就能解决。那么如果多增加一块显示存储区能解决吗?显卡绘制成功后,先写入BackBuffer,不影响当前正在展示的FrameBuffer,这就是双缓冲,但是理论上其实也不行,因为BackBuffer毕竟也是要展示的,也要”拷贝“到FrameBuffer,在A帧没画完,BackBuffer如果不加干预,直接”拷贝“到FrameBuffer同样出现撕裂。所以同步锁的机制才是关键,必须有这么一个机制告诉GPU显卡,要等待当前帧绘完整,才能替换当前帧。但如果仅仅单缓存加锁的话GPU显卡会被挂啊?这就让效率低了,那就一边加同步锁,同时再多加一个缓存,垂直同步(VSYNC)就可看做是这么个东西,其实两者是配合使用的。

image.png

再来看下VSYNC,屏幕刷新从左到右水平扫描(Horizontal Scanning),从上到下垂直扫描Vertical Scanning,垂直扫描完成则整个屏幕刷新完毕,这便是告诉外界可以绘制下一帧的时机,在这里发出VSync信号,通知GPU给FrameBuffer传数据,完成后,屏幕便可以开始刷新,所以或许称之为帧同步更合适。VSYNC强制帧率和显示器刷新频率同步,如果当前帧没绘制完,即使下一帧准备好了,也禁止使用下一帧,直到显示器绘制完当前帧,等下次刷新的时候,才会用下一帧。比如:如果显示器的刷新频率是60HZ显示器,开了垂直同步后,显示帧率就会被锁60,即使显卡输出高,也没用。对Android系统而言,垂直同步信号除了强制帧率和显示器刷新频率同步外,还有其他很多作用,VSYNC是APP端重绘、SurfaceFlinger图层合成的触发点,只有收到VSYNC信号,它们才会工作,以上便是个人对引入VSYNC与双缓冲的见解。


双缓冲的进阶:三缓冲


在Android系统里,除了双缓冲,还有个三缓冲,不过这个三缓冲是对于屏幕硬件刷新之外而言,它关注的是整个Android图形系统的消费者模型,跟Android自身的VSYNC用法有关系,在 Jelly Bean 中Android扩大了VSYNC使用场景与效果,不仅用在屏幕刷新防撕裂,同时也用在APP端绘制及SurfaceFlinger合成那,此时对VSYNC利用有点像Pipeline流水线,贯穿整个绘制流程,对比下VSYNC扩展使用的区别:

image.png

如果想要达到60FPS的流畅度,每16毫秒必须刷新一帧,否则动画、视频就没那么丝滑,扩展后:

image.png

对于没采用VSYNC做调度的系统来说,比如Project Butter之前的系统(4.1以下),CPU的对于显示帧的处理是凌乱的,优先级也没有保障,处理完一帧后,CPU可能并不会及时处理下一帧,可能会优先处理其他消息,等到它开始处理UI生成帧的时候,可能已经处于VSYNC的中间,这样就很容易跨两个VYSNC信号,导致掉帧。在Jelly Bean中,下一帧的处理被限定在VSync信号到达时,并且看Android的处理UI重绘消息的优先级是比较高的,其他的同步消息均不会执行,从而保证每16ms处理一帧有序进行,同时由于是在每个VSYNC信号到达时就处理帧,可以尽量避免跨越两帧的情况出现。


上面的流程中,Android已经采用了双缓冲,双缓冲不仅仅是两份存储,它是一个概念,双缓冲是一条链路,不是某一个环节,是整个系统采用的一个机制,需要各个环节的支持,从APP到SurfaceFlinger、到图像显示都要参与协作。对于APP端而言,每个Window都是一个双缓冲的模型,一个Window对应一个Surface,而每个Surface里至少映射两个存储区,一个给图层合成显示用,一个给APP端图形处理,这便是应于上层的双缓冲。Android4.0之后基本都是默认硬件加速,CPU跟GPU都是并发处理任务的,CPU处理完之后就完工,等下一个VSYNC到来就可以进行下一轮操作。也就是CPU、GPU、显示都会用到Buffer,VSYNC+双缓冲在理想情况下是没有问题的,但如果某个环节出现问题,那就不一样了如下(帧耗时超过16ms):

image.png

可以看到在第二个阶段,存在CPU资源浪费,为什么呢?双缓冲Surface只会提供两个Buffer,一个Buffer被DisPlay占用(SurfaceFlinger用完后不会释放当前的Buffer,只会释放旧的Buffer,直观的想一下,如果新Buffer生成受阻,那么肯定要保留一个备份给SF用,才能不阻碍合成显示,就必定要一直占用一个Buffer,新的Buffer来了才释放老的),另一个被GPU处理占用,所以,CPU就无法获取到Buffer处理当前UI,在Jank的阶段空空等待。一般出现这种场景都是连续的:比如复杂视觉效果每一帧可能需要20ms(CPU 8ms +GPU 12ms),GPU可能会一直超负荷,CPU跟GPU一直抢Buffer,这样带来的问题就是滚雪球似的掉帧,一直浪费,完全没有利用CPU与GPU并行处理的效率,成了串行处理,如下所示


image.png

如何处理呢?让多增加一个Buffer给CPU用,让它提前忙起来,这样就能做到三方都有Buffer可用,CPU跟GPU不用争一个Buffer,真正实现并行处理。如下:

image.png


如上图所示,虽然即使每帧需要20ms(CPU 8ms +GPU 12ms),但是由于多加了一个Buffer,实现了CPU跟GPU并行,便可以做到了只在开始掉一帧,后续却不掉帧,双缓冲充分利用16ms做到低延时,三缓冲保障了其稳定性,为什么4缓冲没必要呢?因为三个既可保证并行,四个徒增资源浪费。


双缓冲保证低延时,三缓冲保证稳定性,双缓冲不在16ms中间开始,有足够时间绘制 三缓冲增加其韧性。


总结


  • 同步是防止画面撕裂的关键,VSYNC同步能防止画面撕裂
  • VSYNC+双缓冲在Android中能有序规划渲染流程,降低延时
  • Android已经采用了双缓冲,双缓冲不仅仅是两份存储,它是一个概念,双缓冲是一条链路,不是某一个环节,是整个系统采用的一个机制,需要各个环节的支持,从APP到SurfaceFlinger、到图像显示都要参与协作
  • 三缓冲在UI复杂情况下能保证画面的连续性,提高柔韧性


相关实践学习
在云上部署ChatGLM2-6B大模型(GPU版)
ChatGLM2-6B是由智谱AI及清华KEG实验室于2023年6月发布的中英双语对话开源大模型。通过本实验,可以学习如何配置AIGC开发环境,如何部署ChatGLM2-6B大模型。
目录
相关文章
|
23天前
|
Linux 测试技术 语音技术
【车载Android】模拟Android系统的高负载环境
本文介绍如何将Linux压力测试工具Stress移植到Android系统,用于模拟高负载环境下的CPU、内存、IO和磁盘压力,帮助开发者优化车载Android应用在多任务并发时的性能问题,提升系统稳定性与用户体验。
145 6
|
22天前
|
Java 数据库 Android开发
基于Android的电子记账本系统
本项目研究开发一款基于Java与Android平台的开源电子记账系统,采用SQLite数据库和Gradle工具,实现高效、安全、便捷的个人财务管理,顺应数字化转型趋势。
|
11月前
|
人工智能 搜索推荐 物联网
Android系统版本演进与未来展望####
本文深入探讨了Android操作系统从诞生至今的发展历程,详细阐述了其关键版本迭代带来的创新特性、用户体验提升及对全球移动生态系统的影响。通过对Android历史版本的回顾与分析,本文旨在揭示其成功背后的驱动力,并展望未来Android可能的发展趋势与面临的挑战,为读者呈现一个既全面又具深度的技术视角。 ####
|
6月前
|
安全 搜索推荐 Android开发
Android系统SELinux安全机制详解
如此看来,SELinux对于大家来说,就像那位不眠不休,严阵以待的港口管理员,守护我们安卓系统的平安,维护这片海港的和谐生态。SELinux就这样,默默无闻,却卫士如山,给予Android系统一份厚重的安全保障。
223 18
|
11月前
|
算法 JavaScript Android开发
|
11月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
12月前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
422 15
Android 系统缓存扫描与清理方法分析
|
11月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
411 16
|
10月前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
10月前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。

热门文章

最新文章