android 画面渲染gpu cpu,Android性能优化-界面渲染原理浅析

app应用作为一个离用户最近的应用,其流畅度是至关重要的。谷歌官方在每个版本的更新中都有关于流畅度的优化,其中android4.1是一个里程,在这个版本中,提出了Project Butter概念。

Project Butter对Android Display系统进行了重构,引入了三个核心元素,即VSYNC、Triple Buffer和Choreographer。

VSYNC(垂直同步):定时产生一个中断信号

Triple Buffer:当双Buffer不够时,分配第三个Buffer

Choreographer: 用来接受一个VSYNC信号来统一协调UI更新

为何是16ms

android系统每隔16ms更新一次UI,相当于每秒更新60次,这也是我们常说的60帧的由来。程序设置为60帧刷新是因为普通人的人眼与大脑之间的协作无法感知超过60fps的画面更新。

如何渲染界面的

CPU(中央处理器) :多缓存多分支,适用于复杂的逻辑运算,主要负责Measure,Layout,Record,Execute的计算操作

GPU(图像处理器):众核少缓存,适用于结构单一的数据处理,主要负责Rasterization(栅格化)操作

那么Android是如何把图像绘制到界面上的呢?

这里需要引入一个Resterization栅格化的概念。

26000db613d2

image

Resterization栅格化是绘制那些Button,Shape,Path,String,Bitmap等组件最基础的操作。它把那些组件拆分到不同的像素上进行显示。这是一个很费时的操作,GPU的引入就是为了加快栅格化的操作。

CPU负责把UI组件计算成Polygons,Texture纹理,然后交给GPU进行栅格化渲染。

整个流程如下

26000db613d2

image

然而每次从CPU转移到GPU是一件很麻烦的事情,所幸的是OpenGL ES可以把那些需要渲染的纹理Hold在GPU Memory里面,在下次需要渲染的时候直接进行操作。所以如果你更新了GPU所hold住的纹理内容,那么之前保存的状态就丢失了。

在Android里面那些由主题所提供的资源,例如Bitmaps,Drawables都是一起打包到统一的Texture纹理当中,然后再传递到GPU里面,这意味着每次你需要使用这些资源的时候,都是直接从纹理里面进行获取渲染的。当然随着UI组件的越来越丰富,有了更多演变的形态。例如显示图片的时候,需要先经过CPU的计算加载到内存中,然后传递给GPU进行渲染。文字的显示更加复杂,需要先经过CPU换算成纹理,然后再交给GPU进行渲染,回到CPU绘制单个字符的时候,再重新引用经过GPU渲染的内容。动画则是一个更加复杂的操作流程。

为了能够使得App流畅,我们需要在每一帧16ms以内处理完所有的CPU与GPU计算,绘制,渲染等等操作。

负责界面渲染的容器DisplayList

通常来说,Android需要把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。

在某个View第一次需要被渲染时,DisplayList会因此而被创建,当这个View要显示到屏幕上时,我们会执行GPU的绘制指令来进行渲染。如果你在后续有执行类似移动这个View的位置等操作而需要再次渲染这个View时,我们就仅仅需要额外操作一次渲染指令就够了。然而如果你修改了View中的某些可见组件,那么之前的DisplayList就无法继续使用了,我们需要回头重新创建一个DisplayList并且重新执行渲染指令并更新到屏幕上。

需要注意的是:任何时候View中的绘制内容发生变化时,都会重新执行创建DisplayList,渲染DisplayList,更新到屏幕上等一系列操作。这个流程的表现性能取决于你的View的复杂程度,View的状态变化以及渲染管道的执行性能。举个例子,假设某个Button的大小需要增大到目前的两倍,在增大Button大小之前,需要通过父View重新计算并摆放其他子View的位置。修改View的大小会触发整个HierarcyView的重新计算大小的操作。如果是修改View的位置则会触发HierarchView重新计算其他View的位置。如果布局很复杂,这就会很容易导致严重的性能问题。我们需要尽量减少Overdraw。

26000db613d2

image

Android Display工作方式

没有VSYNC的情况, CPU和GPU比较“任性”,当他们处于空闲状态时才会处理数据

26000db613d2

image

T0阶段:CPU处理第一帧的数据,处理完成之后,GPU紧跟着做栅格化处理

T1阶段:Display将T0阶段处理好的数据,因为第一帧的数据已经在T0阶段处理完成,所以T1阶段能正常显示界面,同时CPU和GPU需要处理第二帧的数据。

T2阶段:这个阶段正常情况下应该显示第二帧的数据,但因为在T1阶段,CPU和GPU没来的急处理完数据,导致该阶段显示不了第二帧的数据,这时只能延续第一帧的数据,这也是造成界面卡顿的主要原因.

出现上述问题的原因,究其原因还是CPU和GPU没能及时处理数据,为了解决这个问题,引入了VSYNC

加入VSYNC(垂直同步)之后,Display的工作方式

文章开头说过,VSYNC的作用是定时产生一个中断信号,用来提醒CPU和GPU要开始工作了。

26000db613d2

image

如图所图,在每个时间间隔的开始,CPU和GPU开始工作,处理下一帧的数据,用于Display显示.正常情况下界面都能正常且平滑的显示.

但如果在一个时间间隔即(16ms)内CPU和GPU处理不完下一帧的数据,还是会出现卡顿的情况,如下图

26000db613d2

image

T0,T1显示的都是第一帧的数据,T2,T3显示的都是第二帧的数据,刷新率从16ms变大到了32ms,从而引起卡顿感觉。另外在T1阶段时,CPU并没有进入工作,这是因为早期系统设计的是双Buffer,此刻A Buffer被Display使用,B Buffer被GPU在用,所以CPU无事可做,只能闲置,另外由于VSYNC的存在,过了VSYNC的时间点,就算有多余的Buffer存在,CPU也不会重新进入工作.

于是就有了Triple Buffer,Triple Buffer的作用是当双Buffer不够时,分配第三个Buffer。

26000db613d2

image

在加入C Buffer之后,在T1这个时间点CPU就不至于处于闲置状态,虽然在T1时间,Display绘制的还是第一帧的数据,不过接下来的几帧就显得比较顺畅了。

既然多Buffer的作用明显,那为什么不多加几个Buffer呢?实际上Buffer并不是越多越好的,由上图可知,三个Buffer已经能解决大部分问题了,追加更多的Buffer并不能有效的提高效率,反而会因为多加的Buffer影响效率。

Choreographer: 用来接受一个VSYNC信号来统一协调UI更新

Choreographer在Project Butter也十分重要,除了统一协调UI更新之外,还可以用来监测UI是否发生卡顿,

Android系统每隔16.6ms发出VSYNC信号,来通知界面进行输入、动画、绘制等动作,每一次同步的周期为16.6ms,代表一帧的刷新频率,理论上来说两次回调的时间周期应该在16.6ms,如果超过了16.6ms我们则认为发生了卡顿,利用两次回调间的时间周期来判断是否发生卡顿 这个方案的原理主要是通过Choreographer类设置它的FrameCallback函数,当每一帧被渲染时会触发回调FrameCallback, FrameCallback回调void doFrame (long frameTimeNanos)函数。一次界面渲染会回调doFrame方法,如果两次doFrame之间的间隔大于16.6ms说明发生了卡顿。

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值