一、Core Animation 的作用
通过时间分析测量应用的图形性能以及进程的CPU使用率。
用来监测Core Animation性能。它提供了周期性的FPS,并且考虑到了发生在程序之外的动画。
- FPS是什么?
任何屏幕总是有一个刷新率,比如iPhone推荐的刷新率是60Hz,也就是说GPU(图形处理器)每秒钟刷新屏幕60次,因此两次刷新之间的间隔为16.67ms。这段时间内屏幕内容保持不变,称为一帧(frame), fps表示frames per second,也就是每秒钟显示多少帧画面。对于静止不变的内容,我们不需要考虑它的刷新率,但在执行动画或滑动时,fps的值直接反映出滑动的流程程度。
二、Core Animation 的调试选项
Core Animation的一系列复选框选项帮助调试渲染瓶颈。
- Color Blended Layers
这个选项基于渲染程度对屏幕中混合区域进行绿到红的高亮(也就是多个半透明图层的叠加)。由于重绘的原因,混合对GPU性能会有影响,同时也是滑动或者动画帧率下降的罪魁祸首之一。
(1)图层混合
首先我们要明白像素的概念,屏幕上每一个点都是一个像素,像素有R、G、B三种颜色构成(有时候还带有alpha值)。如果某一块区域覆盖了多个layer,最后的显示效果受到了这些layer的共同影响。举个例子,上层是蓝色(RGB=0,0,1),透明度为50%,下层是红色(RGB=1,0,0)。那么最终的显示效果是紫色(RGB=0.5,0,0.5)。这种颜色的混合(blending)需要消耗一定的GPU资源,因为实际上可能不只有两层。如果只想显示最上面的蓝色,可以把它的透明度设置为100%,这样GPU会忽略下面所有的layer,从而节约了很多不必要的运算。
假如最上层的view是不透明的,那直接使用这个view的对应颜色之就可以,但如果view是透明的,在计算像素的颜色值时就需要计算它下面图层,透明的视图越多,计算量就越大,因此也会对图形的性能产生一定的影响,所以可以的话也尽量减少透明图层的数目。
(2)调试Color Blended Layers
Color Blended Layers 正是用于检测哪里发生了图层混合,并用红色标记出来。因此,我们需要尽可能减少看到红色区域。一旦发现应该想方设法消除它。开始调试后勾选这个选项,我们在真机上便可以看到效果。
- Color Hits Green and Misses Red
(1)光栅化
光栅化是将layer预先渲染成位图(bitmap),然后加入缓存中。如果对于阴影效果这样比较消耗资源的静态库内容进行缓存,可以得到一定幅度的性能提升。demo中的这一行代码表示将label的layer光栅化:
label.layer.shouldRasterize = YES;
(2)调试Color Hits Green and Misses Red
它表示如果命中缓存则表示为绿色,否则显示为红色,显然绿色越多越好,红色越少越好。
注意:光栅化的核心在于缓存的思想。我们动手把玩一下,可以发现以下几个有意思的现象:
上下微小幅度滑动时,一直是绿色上下大幅度滑动,新出现的label一开始是红色,随后变成绿色,如果静止一秒钟,刚开始滑动会变红。
这是因为layer进行光栅化后渲染成位图放在缓存中。当屏幕出现滑动时,我们之间从缓存中读取而不必渲染,所以会看到绿色。当新的label出现时,缓存中没有这label的位图,所以会变成红色。第三点比较关键,缓存中的对象有效期只有100ms,即使如果在 0.1s 内没有被使用就会自动从缓存中清理出去。这就是为什么停一会再滑动就会看到红色的原因。
光栅化的缓存机制是一把双刃剑,先写入缓存再读取有可能销毁较多的时间。因此光栅化仅适用于较复杂的、静态的效果。通过Instrument的调试发现,这里使用光栅化经常出现未命中缓存的情况,如果没有特殊需要则可以关闭光栅化,所以我们做的第二个优化是注释掉下面这行代码:
//label.layer.shouldRasterize = YES;
- Color Copied Images
(1)颜色格式
像素在内存中的布局和它在磁盘中的存储方式并不相同。考虑一种简单的情况:每个像素有R、G、B和alpha四个值,每个值占有1个字节,因此每个像素占有4字节的内存空间。一张1920*1080的照片(iPhone6Plus的分辨率)一共有2,073,600个像素,因此占用了超过8Mb的内存。但是一张同样分辨率的PNG格式或JPEG格式的图片一般情况下不会有这么大。这是因为JPEG将像素数据进行了非常复杂且可逆的转化。
CPU主要处理两件事:
- 把图片从PNG或JPEG等格式中解压处理,得到像素数据。
- 如果GPU不支持这种颜色格式,CPU需要进行格式转换。
比如应用中有一些从网络下载的图片,而GPU恰好不支持这个格式,这就需要CPU预先进行格式转化。** 如果在主线程做这个操作对性能会有一定的影响。**
(2)调试Color Copied Images
Color Copied Images 就用来监测这种实时的格式转化,如果有则会将图片标记会蓝色。
有时候寄宿图片的生成意味着Core Animation被强制生成一些图片,然后发送到渲染服务器,而不是简单指向原始指针。复制图片对内存和CPU使用来说都是一项非常昂贵的操作,所以应该尽可能的避免。
Color Misaligned Images
(1)图片大小
(2)调试Color Misaligned Images
这里会高亮那些被缩放或者拉伸以及没有正确对齐到像素边界的图片(也就是非整型坐标)。这些中的大多数通常会导致图片的不正常缩放,如果把一张大图当缩略图显示,或者不正确的模糊图像,那么这个选择将会帮你识别出问题的所在。
被缩放的图片会被标记为黄色,像素不齐则会标记为紫色Color Offscreen-Rendered Yellow
(1)offscreen-render
什么是offscreen-render?
offscreen-render涉及的内容比较多,有offscreen-render那就有onscreen render,onscreen render指的是GPU在当前用于显示的屏幕缓冲区进行渲染,相反offscreen-render就是不在当前的屏幕缓存区,而在另外的缓冲区进行渲染,offscreen-render有两种形式:
- CPU的offscreen-render
使用CPU来完成渲染操纵,通常在你使用:- drawRect (如果没有自定义绘制的任务就不要在子类中写一个空的drawRect方法,因为只要实现了该方法,就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以 contentsScale的值,造成资源浪费)
- 使用Core Graphics
首先分配一块内存,然后进行渲染操作生成一份bitmap位图,整个渲染过程会在你的应用中同步的进行,接着再将位图打包发送到iOS里一个单独的进程--render server,理想情况下,render server将内容交给GPU直接显示到屏幕上。
- GPU的offscreen-render
使用GPU在当前屏幕缓冲区以外开辟一个新的缓冲区进行绘制,通常发生的情况有:
设置cornerRadius, masks, shadows,edge antialiasing等
设置layer.shouldRasterize = YES
offscreen-render对性能到底有什么影响?
通常大家说的离屏渲染指的是GPU这块(当然CPU这块也会有影响,也需要消耗一定的资源),比如修改了layer的阴影或者圆角,GPU需要做额外的渲染操作。通常GPU在做渲染的时候是很快的,但是涉及到offscreen-render的时候情况就可能有些不同,因为需要额外开辟一个新的缓冲区进行渲染,然后绘制到当前屏幕的过程需要做onscreen跟offscreen上下文之间的切换,这个过程的消耗会比较昂贵,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一帧都会涉及到,因此处理不当肯定会对性能产生一定的影响,所以可以的话尽量减少offscreen-render的图层。
(2)调试Color Offscreen-Rendered Yellow
标示哪些layer需要做离屏渲染(offscreen-render)。这里会把哪些需要离屏渲染的图层高亮成黄色。这些图层很有可能需要用 'shadowPath' 或者 'shouldRasterize'来优化。
Color OpenGL Fast Path Blue
这个选项会对任何直接使用OpenGL绘制的图层进行高亮。如果仅仅使用UIKit或者Core Animation的API,那么不会有任何效果。如果使用GLKView
或者CAEAGLLayer
,那如果不显示蓝色块的话就意味着你正在强制CPU渲染额外的纹理,而不是绘制到屏幕。Flash Updated Regions
这个选项会对重绘的内容高亮成黄色(也就是任何在软件层面使用Core Graphics绘制的图层)。这种绘图的速度很慢。如果频繁发生这种情况的话,这意味着有一个隐藏的bug或者说通过增加缓存或者使用替代方案会有提升性能的空间。
当碰到性能问题时,可以思考以下方面:
- 是否受到CPU或者GPU的限制?
- 是否有不必要的CPU渲染?
- 是否有太多的离屏渲染操作?
- 是否有太多的图层混合操作?
- 是否有奇怪的图片格式或者尺寸?
- 是否涉及到昂贵的view或者效果?
- view的层次结构是否合理?
那么哪些是你最该开始考虑的方向呢?通常发生图形性能问题的时候,比如列表滑动不顺畅、动画卡顿等,大部分都是由于Offscreen Rendering(离屏渲染)或者blending导致的,因为这在动画的每一帧都会涉及到。