Android的图形之美(二)

上文我们提到,窗口绘图表面进行绘制的时候,会在画布上(非硬件加速如Canvas)的一个图形缓冲区中,然后SurfaceFlinger通过OpenGL图像块API来将这个图形缓冲区渲染到帧缓冲区中。我们对这个过程用图形来进行抽象分析。如下图,上层为生产,下层为消费,每一层有buffer为数据载体。其中,上层的buffer我们称为图形缓冲区、下层的buffer为帧缓冲区。

下层的帧缓冲区如下,显示屏上的内容是从帧缓冲区读取的,读取的属性从buffer的起始地址开始,从上往下读取,从左往右扫描整个buffer,将内容映射到显示屏上。若只在一个buffer中进行合成和读取,实际上这个buffer可能就不是包含当前帧的内容,因此,需要一个帧用于读取,一个帧用于合成,所以如下我们所说的双缓冲。前缓冲区为显示内容到屏幕的缓冲区,后缓冲区用于合成下个图形的帧缓冲区。实际上当如下所示的前置缓冲区渲染到屏幕之后,后缓冲区已经合成了,显示屏准备显示下一屏的时候,后帧缓冲区变为前缓冲区,前缓冲区变为后缓冲区。

前后更替进行渲染是在理想情况下进行的,既当前帧显示完毕,后一帧合成完毕是处于理想的状态下,实际上,可能后缓冲区合成还没开始或没合成完毕或合成很快,导致正在显示的一帧含有其他的,这样就造成看起来丢帧或撕裂(显示多帧)。这种情况和两个帧率有关。一个为HZ(屏幕刷新率),一个为FPS(系统帧率速率)。其中HZ和屏幕相关,一般我们常说的1秒展示60帧,既60HZ,既大概16ms刷新一帧,而FPS代表1秒内的合成帧数。那么这样我就可以知道了,若HZ>FPS,那么就会导致到屏幕一直刷新的仍然是相同的帧数,给人的感觉就是静止了一会儿,既卡顿;若HZ<FPS, 则合成的速率过快,缓冲区有限,导致把钱缓冲区的数据给后面的帧数据覆盖,造成一个屏幕有多帧的想象,造成撕裂。如何解决两个速率同步问题?

VSYNC——垂直同步,解决帧同步问题。当屏幕从缓冲区扫完一帧到屏幕上之后,开始扫下一秒之前,发出一个同步信号,该信号用来切换前缓冲区和后缓冲区。实际上,屏幕的速率是固定的,HZ固定,因此,需要配合的是FPS,既系统硬件需要配合,怎么配合呢?就是通过VSYNC来实现两者同步。

在分析垂直同步之前,先分析下SurfaceFlinger的作用——图形合成者。我们从上面的图中可以看出,SurfaceFlinger是上层的消费者,下层的生产者,其实一个服务。和众多AMS、WMS服务类似,在System_server启动的时候启动。

如上图所示,上层应用程序我们看到的包括导航栏,通知栏、悬浮窗以及微信界面,都会对应到SurfaceFlinger一个个Surface对象,上层会进行绘制到Surface的Layer上,如下图所示

SurfaceFlinger将多个Surface进行合成之后放到后缓冲区,然后等待屏幕读取展示。那么Surface的内容从哪里来呢?结合上图微信界面和上文中分析的Activity启动的时候会进行窗口绘制表面绘制的分析,我们知道,所有内容在画布(画板)Canvas绘制之后(绘制到Surface(图层)中,对应SurfaceFlinger的Surface的Layer对象上)就会被SurfaceFlinger服务消费然后合成到帧缓冲区中。如下图所示,多个surface对应多个Buffer Queue(下面会讲到),每个surface都会产生buffer,多个surface最终经过SurfaceFlinger进入到硬件合成形成最终的画面。

我们在第一个图中已看到,生产者的数据也是以buffer为载体,那么对应的Surface实际上是一个多缓冲区,对应BufferQueue。那么第一个图的关系再继续深入之后就如下图所示:

Surface内部提供一个BufferQueue,应用程序的绘图表面为上层,图形的提供者,既生产者(Producer),而消费者(Comsumer)既为SurfaceFlinger,用于合成生产者生产的图形数据。

其状态变化过程如下:

从BufferQueue转移到上层(Free->Dequeued),上层绘制完成回到BufferQueue中(Dequeued -> Queued), 接着SurfaceFlinger拿去合成(Queued -> Acquired),合成完成又放回队列中(Acquired -> Free)。

我们分析了生产者和消费者的模型、以及潜在的存在卡顿或裂屏的情况存在,那么Android系统解决这一问题的过程是怎样的呢?

在分析解决过程的时候,以硬件处理来分析,我们先区分哪些是在CPU中进行的,哪些又是在GPU进行的。在CPU进行的有包括应用窗口图形测量,应用窗口图形布局以及应用窗口图形绘制、纹理和多边形生产,发送纹理和多边形到GPU;而GPU主要是将CPU生产的纹理和多边形进行栅格化以及合成操作。

Android4.1之前,在绘制的时候没有加入垂直同步,只是在前缓冲区和后缓冲区切换的时候加入的情况下,如下图

我们知道,有生产有消费,一般是希望两者保持一个平衡状态,一方不能太快,而另一方太慢的情况。若像上图没在上层既生产端也加入垂直同步的话,那么可能存在如下图的问题:

从上图我看出,在第0帧的时候,上层CPU开始产生第一帧的纹理,计算完成交给GPU进行栅格化合成,于是在第1个VSync信号到来时,屏幕正确显示第1帧,在下一个VSync到来之前,CPU开始处理第2帧的纹理,但是在第2个VSync到来的时候,GPU还没栅格化合成第2帧,因此,屏幕智能继续展示第1帧,这样就造成卡顿的想象发生。

因为上层不知道垂直同步信号什么时候发出,导致CPU本来可以提前在合适的实际进行绘制的也没进行,导致后面GPU合成没有在下一个垂直同步到来之前合成完成。因此,谷歌在4.1系统上绘制也加上了垂直同步,既生产和消费保持一个同步状态,如下所示:

那么这个是如何进行垂直同步的呢,这个时候,我们就要搬出我们在很多很多中间件(如性能上报这种中间件)需要用到的一个类——Choreographer。该类实现了我们需要的VSync垂直同步,主要是向SurfaceFlinger注册一个VSync信号接收器DisplayEventReceiver,同时在Choreographer的CallbackQueue中注册有需要同步信号的组件(如ViewRoot, ViewRootImpl), 收到这个信号之后CallbackQueue对应的组件就开始执行刷新操作,进而开始绘制,完成绘制同步的操作。这样,加上垂直同步的相关帧表现情况如下图:

从第0帧开始,CPU开始处理第1帧的纹理,处理完成之后GPU进行合成,在第1帧到来时候,显示屏显示第1帧,接着同步的CPU可以开始处理第2帧的纹理,GPU在CPU处理完成之后栅格化合成,在第2个垂直同步信号到来的时候,显示屏可以展示第2帧。

然而上面的情况是理想情况,如果发生下图所示的情况,那么仍然会出现丢帧卡顿的情况。

如上图,若CPU绘制纹理完成而GPU没能在下一个VSync信号到来的时候完成合成,那么就存在在下一个16.6ms中展示新的一帧,此时仍然展示的前缓冲区的帧数据, 本来在下一个VSync到来之后,CPU也要开始进行绘制纹理的,因为没有多余的缓冲区可以用于操作,所以CPU只能空闲,在下下个VSync信号到来的时候,前面绘制的帧数据已经合成,可以被显示屏展示了,此时前置缓冲区就变成后置缓冲区,从而CPU就可以有缓冲区进行绘制处理了。

出现上面的问题是因为只有两个缓冲区,从而导致CPU还空闲了一段时间,假设在第一个VSync信号到来的时候有多余的缓冲区,那么CPU就可以同步进行,然后为GPU争取更多的时间去合成,从而避免影响后面接着卡顿的情况发生。因此就有了三缓冲区,如下图。

SurfaceFlinger占据一个缓冲区,GPU合成的时候占据一个缓冲区,此时第一个VSync信号到来的时候,会发生一次卡顿,然而CPU可以利用第三个缓冲区进行纹理绘制,从而加速GPU可以第一时间进行合成操作。减少后面发生卡顿的情况的发生。

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

推荐阅读更多精彩内容