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

VSYNC与画面撕裂

VSYNC即vertical sync,也称为垂直同步,是一种图形技术,主要就是强制将帧速率与显示器的刷新率同步,最初由 GPU 制造商提出,主要用来处理屏幕撕裂。首先了解下两个名词:FPS与屏幕刷新频率

  • 帧率[Frame Rate,单位FPS]-显卡生成帧的速率,也可以认为是数据处理的速度

  • 屏幕刷新频率 [Refresh Rate单位赫兹/HZ]:是指硬件设备刷新屏幕的频率,值一般是固定的,以黑白电视的电子扫描枪类比,比如60Hz的显示屏,每16ms电子枪从上到下从左到右一行一行逐渐把图片绘制出来。

两者要同步配合好才能高效的显示图像,可以人为帧率对应的是图像数据的输出,刷新率对应的是图像数据的屏幕展示,如果帧率同设备的刷新率不一致,而又没有采用合适的同步技术,会出现什么问题呢?可能会出现上述的屏幕撕裂[多帧的局部数据共同组成了一个完整帧],示意如下:

理论上来讲,只要没做到读/写线性同步就有几率发生撕裂, 只有帧数据完整更新+显示设备完整渲染才能阻止撕裂,相对应的撕裂的复现场景有两种:

  • 1:显示设备未完整渲染: 假设显示设备只有一块显存存放显示数据,在没有同步加锁的情况下,帧数据由CPU/GPU处理完可随时写入到显存,如果恰好在上一帧A还没100%在屏幕显示完的时候,B帧到达,并且覆盖了A,那么在继续刷新下半部分时,绘制的就是B帧数据,此时就会出现上半部分是A下半部分是B,即发生屏幕撕裂:如下
  • 2 帧数据未完整更新 :依旧假设显示设备只有一块显存存放显示数据,如果在GPU覆盖旧帧的间隙,也就是显存数据没有100%刷新的时候,通知渲染到屏幕,这个时候同样会发生上述事情,即使用了半成品的帧:撕裂帧。

所以同步锁的机制是撕裂的关键,必须有这么一个机制告诉GPU显卡,要等待当前帧绘完整,才能替换当前帧,即VSYNC,VSYNC强制帧率和显示器刷新频率同步,如果当前帧没绘制完,即使下一帧准备好了,也禁止使用下一帧,直到显示器绘制完当前帧,即:60HZ显示器,开了垂直同步后,显示帧率就会限定最高60,即使显卡输出高达90FPS也没用,甚至可以认为他是一种妥协性优化,一定程度上还会降低性能。以上都是针对一块显示存储的情况,理论上只要加锁就能解决,读的时候禁止写,但这么做无疑会大大降低效率,所以不能简单依靠单纯加锁解决问题。

双缓冲+垂直同步

如何解决单缓冲+同步的性能问题呢?多增加一块显示存储区能解决吗?假定显示设备有两块显存,BackBuffer与FrontBuffer,可以简单的认为CPU/GPU占据一个缓冲、当前呈现的数据占据一个缓冲,GPU/CPU 绘制更新BackBuffer,不需要关心正在展示的FrontBuffer,这就是双缓冲,相比于单缓存,双缓冲可让写与读分离,提高效率。但紧靠双缓冲理论上解决不了撕裂的问题,BackBuffer毕竟也是要展示的,也要”拷贝“到FrontBuffer,如果不对拷贝操作添加干预,也可能出现撕裂,VSYNC机制必须兼具禁止在刷新的过程中更新FrontBuffer的功能,所有的COPY或者说是Page flipping操作都要等待上一帧完全渲染完才可以,渲染完成之后,显示设备就按节奏可以发出下一个VSYNC信号,通知BackBuffer与FrontBuffer间进行拷贝,拷贝结束后,接着进行下一帧屏幕渲染,这样就能避免屏幕撕裂,当然,如果BackBuffer还未来得及完成帧更新也是需要阻断拷贝过程,否则就是渲染了半成品的帧,所以个人人为,Vsync解决撕裂、双缓冲来解决性能。

It does this by preventing the GPU from doing anything to the display memory until the monitor has concluded its current refresh cycle — effectively not feeding it any more information until it’s ready for it. Through a combination of double buffering and page flipping, VSync synchronizes the drawing of frames onto the display only when it has finished a refresh cycle, so you shouldn’t ever see tears when VSync is enabled.

对Android系统而言,VSYNC除了强制帧率和显示器刷新频率同步外,还有其他很多作用,在Android Jelly Bean之前VSYNC使用的场景比较少,只用在最后缓冲区切换,系统的其他环节没用,这种做法可能会让CPU浪费在其他低优先级的业务上,如下图:

如此情况就是一次jank

Jelly Bean之前VSYNC仅用在最后的图像显示阶段,防止屏幕撕裂,但是并未协调UI的绘制,CPU对于显示帧的处理是凌乱的,VSYNC到达后,如果CPU被其他任务占据,UI绘制的执行就会延迟,等到它开始处理UI生成帧的时候,可能已经处于16ms的中间,这样就很容易跨两个VYSNC信号,导致掉帧。在Jelly Bean中,下一帧的处理被限定在VSync信号到达时,并且依赖Android的消息屏障机制,将UI重绘消息的优先级是提高,其他的同步消息均不会执行,由于是在每个VSYNC信号到达时就处理帧,可以让UI绘制充分使用16ms耗时,可以尽量避免跨越两帧的情况出现

这种做法保证了UI绘制的执行时间,虽然不能完全解决jank【比如本身绘制就超过16ms】,但是对于本来就小于16ms的任务是能保证的,从而降低jank的概率,因此VSYNC+双缓冲能够很好降低单缓冲的性能问题,降低延时。

双缓冲的进阶:三缓冲

之前的VSYNC+双缓冲流程图示都是用1、2、3代表第帧来表示更新流程,接线来用缓冲区代表,看一下双缓冲的数据流向,理想情况下,16ms内CPU处理完数据,将缓冲区A交给GPU,GPU接着处理A,结束后,等下个VSYNC与前面展示缓冲区B交换,A进行屏幕渲染,B回收用来继续生成下一帧,如下图所示:

在这种模型下,CPU与GPU其实是一种串行处理的操作,存在资源的浪费,因为两者其一必空闲,毕竟没有多余的缓冲区让其处理数据,理想情况下其实双缓冲并未有什么不妥,但是一旦CPU或者GPU处理超时,jank就很容易发生。

VSYNC+双缓冲保证低延时,三缓冲保证稳定性:让闲置的资源动起来

双缓冲模型中显示、CPU、GPU处理都会用到Buffer,VSYNC+双缓冲在理想情况下是没有问题的,但如果某个环节出现问题,那就不一样了,比如某些帧耗时是[CPU 8ms +GPU 12ms],超过了16ms,如下:

可以看到在第二个阶段,存在CPU资源浪费,双缓冲只会提供两个Buffer,B被GPU处理占用,A正在用显示,那么在第二个16ms里面,CPU就无法获取到Buffer处理UI更新,在Jank的阶段空空等待。而且,一般出现这种场景都是连续的:比如复杂视觉效果,那么GPU可能会一直超负荷,CPU一直跟GPU抢Buffer,这样带来的问题就是滚雪球似的掉帧,一直浪费,完全没有利用CPU与GPU并行处理的效率,成了串行处理,如下所示

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

如上图所示,虽然即使每帧需要20ms【CPU 8ms +GPU 12ms】,但是由于多加了一个Buffer,实现了CPU跟GPU并行,便可以做到了只在开始掉一帧,后续却不掉帧,双缓冲充分利用16ms做到低延时,三缓冲保障了其稳定性,为什么4缓冲没必要呢?因为三个既可保证并行,四个徒增资源浪费。 在Android系统中,双缓冲不仅仅是两份存储,它是一个概念,双缓冲是一条链路,不是某一个环节,是整个系统采用的一个机制,需要各个环节的支持,从APP到SurfaceFlinger、到图像显示都要参与协作。对于APP端而言,每个Window都是一个双缓冲的模型,一个Window对应一个Surface,而每个Surface里至少映射两个存储区,一个给图层合成显示用,一个给APP端图形处理,这便是应于上层的双缓冲。

总结

  • 同步是防止画面撕裂的关键,VSYNC同步能防止画面撕裂

  • VSYNC+双缓冲在Android中能有序规划渲染流程,降低延时

  • Android已经采用了双缓冲,双缓冲不仅仅是两份存储,它是一个概念,双缓冲是一条链路,不是某一个环节,是整个系统采用的一个机制,需要各个环节的支持,从APP到SurfaceFlinger、到图像显示都要参与协作

  • 三缓冲在UI复杂情况下能保证画面的连续性,提高柔韧性

作者:看书的小蜗牛
链接:https://juejin.cn/post/7125675253707046926
来源:稀土掘金 如有侵权,请联系删除!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容