iOS圆角性能问题

离屏渲染学习笔记

1、概念理解

OpenGL中,GPU屏幕渲染有如下两种方式:html

  • On-Screen Rendering

意为当前屏幕渲染,指的是GPU的渲染操做是在当前用于显示的屏幕缓冲区中进行。ios

  • Off-Screen Rendering

意为离屏渲染,指的是GPU在当前屏幕缓冲区之外新开辟一个缓冲区进行渲染操做。git

2、离屏渲染的是是非非

相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体如今两个方面:github

  • 建立新缓冲区

要想进行离屏渲染,首先要建立一个新的缓冲区。缓存

  • 上下文切换

离屏渲染的整个过程,须要屡次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束之后,将离屏缓冲区的渲染结果显示到屏幕上有须要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。异步

3、离屏渲染触发方式

设置了如下属性时,都会触发离屏绘制:工具

  • shouldRasterize(光栅化)
  • masks(遮罩)
  • shadows(阴影)
  • edge antialiasing(抗锯齿)
  • group opacity(不透明)

须要注意的是,若是shouldRasterize被设置成YES,在触发离屏绘制的同时,会将光栅化后的内容缓存起来,若是对应的layer及其sublayers没有发生改变,在下一帧的时候能够直接复用。这将在很大程度上提高渲染性能。post

而其它属性若是是开启的,就不会有缓存,离屏绘制会在每一帧都发生。性能

4、另外一种特殊的“离屏渲染”

按照以前的说法,若是将不在GPU的当前屏幕缓冲区中进行的渲染都称为离屏渲染,那么就还有另外一种特殊的“离屏渲染”方式:CPU渲染学习

若是咱们重写了drawRect方法,而且使用任何Core Graphics的技术进行了绘制操做,就涉及到了CPU渲染。整个渲染过程由CPU在App内同步地完成,渲染获得的bitmap最后再交由GPU用于显示。

5、Instruments

Instruments的Core Animation工具中有几个和离屏渲染相关的检查选项:

  • Color Offscreen-Rendered Yellow

开启后会把那些须要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。

  • Color Hits Green and Misses Red

若是shouldRasterize被设置成YES,对应的渲染结果会被缓存,若是图层是绿色,就表示这些缓存被复用;若是是红色就表示缓存会被重复建立,这就表示该处存在性能问题了。

6、如何抉择

如今摆在咱们面前得有三个选择:当前屏幕渲染、离屏渲染、CPU渲染,该用哪一个呢?这须要根据具体的使用场景来决定。

  • 尽可能使用当前屏幕渲染

鉴于离屏渲染、CPU渲染可能带来的性能问题,通常状况下,咱们要尽可能使用当前屏幕渲染。

  • 离屏渲染 VS CPU渲染

因为GPU的浮点运算能力比CPU强,CPU渲染的效率可能不如离屏渲染;但若是仅仅是实现一个简单的效果,直接使用CPU渲染的效率又可能比离屏渲染好,毕竟离屏渲染要涉及到缓冲区建立和上下文切换等耗时操做。

总之,具体的选择应该由性能测试结果来决定。

通常咱们在iOS开发的过程当中设置圆角都是以下这样设置的。

avatarImageView.clipsToBounds = YES; [avatarImageView.layer setCornerRadius:50]; 这样设置会触发离屏渲染,比较消耗性能。好比当一个页面上有十几头像这样设置了圆角 会明显感受到卡顿。 注意:png图片UIImageView处理圆角是不会产生离屏渲染的。(ios9.0以后不会离屏渲染,ios9.0以前仍是会离屏渲染)。

全部若是要高性能的设置圆角就须要找另外的方法了。下面是我找到的一些方法并写了一个例子。github源码

IMG_1802.PNG

设置圆角的方法
直接使用setCornerRadius
这种就是最经常使用的,也是最耗性能的。

setCornerRadius设置圆角以后,shouldRasterize=YES光栅化
avatarImageView.clipsToBounds = YES;
[avatarImageView.layer setCornerRadius:50];
avatarImageView.layer.shouldRasterize = YES;
avatarImageViewUrl.layer.rasterizationScale=[UIScreen mainScreen].scale;

//UIImageView不加这句会产生一点模糊shouldRasterize=YES设置光栅化,可使离屏渲染的结果缓存到内存中存为位图,使用的时候直接使用缓存,节省了一直离屏渲染损耗的性能。可是若是layer及sublayers经常改变的话,它就会一直不停的渲染及删除缓存从新建立缓存,因此这种状况下建议不要使用光栅化,这样也是比较损耗性能的。
//UIImageView不加这句会产生一点模糊shouldRasterize=YES设置光栅化,可使离屏渲染的结果缓存到内存中存为位图,使用的时候直接使用缓存,节省了一直离屏渲染损耗的性能。可是若是layer及sublayers经常改变的话,它就会一直不停的渲染及删除缓存从新建立缓存,因此这种状况下建议不要使用光栅化,这样也是比较损耗性能的。

直接覆盖一张中间为圆形透明的图片(推荐使用)
这种方法就是多加了一张透明的图片,GPU计算多层的混合渲染blending也是会消耗一点性能的,但比第一种方法仍是好上不少的。

Core Graphics绘制圆角
这种方式GPU损耗最低,可是UIButton上不知道怎么绘制,能够用UIimageView添加个点击手势当作UIButton使用。
UIGraphicsBeginImageContextWithOptions(avatarImageView.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:avatarImageView.bounds cornerRadius:50] addClip];[image drawInRect:avatarImageView.bounds];
avatarImageView.image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();
这段方法能够写在SDWebImage的completed回调里,在主线程异步绘制。也能够封装到UIImageView里,写了个DSRoundImageView。后台线程异步绘制,不会阻塞主线程。

问题:这种方法图片不少的话CUP消耗会高,内存占用也会暴增,并且后台线程绘制会比在主线程绘制占用更多的内存,不知道怎么解决?求大神指教!

使用Instruments的Core Animation查看性能
Color Offscreen-Rendered Yellow开启后会把那些须要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。

Color Hits Green and Misses Red若是shouldRasterize被设置成YES,对应的渲染结果会被缓存,若是图层是绿色,就表示这些缓存被复用;若是是红色就表示缓存会被重复建立,这就表示该处存在性能问题了。

用Instruments测试得
第一种方法,UIImageView和UIButton都高亮为黄色。

第二种方法,UIImageView和UIButton都高亮为绿色

第三种方法,无任何高亮,说明没离屏渲染。这种圆片覆盖的方法通常只用在底色为纯色的时候,若是圆角图片的父View是张图片的时候就没办法了,并且底色若是是多种颜色的话那要作多张不一样颜色的圆片覆盖。(能够用代码取底色的颜色值给圆片着色)

第四种方法无任何高亮,说明没离屏渲染(可是CPU消耗和内存占用会很大)

问题回复:
有回复提到还有一种mask方法。这种方法比第一种方法其实更卡顿。一次mask发生了两次离屏渲染和一次主屏渲染。 具体能够参考当心别让圆角成了你列表的帧数杀手

@nerozhao说第四种比第一种更卡。我刚在demo里加了个例子测试了一下,第一种能明显的感受到卡顿(ios9.0以上仍是不卡的,由于没有离屏渲染了),第四种仍是挺顺畅的,有兴趣的能够本身试试看。第四种是解决了离屏渲染GPU的问题。

测试例子

能够用Instruments的 GPU Driver进行测试:
Renderer Utilization若是这个值超过了~50%,就意味着你的动画可能对帧率有所限制,极可能由于离屏渲染或者是重绘致使的过分混合。
Tiler Utilization若是这个值超过了~50%,就意味着你的动画可能限制于几何结构方面,也就是在屏幕上有太多的图层占用了。

Instruments

图上面一部分是第一种方法的数据,下面一部分是第四种方法的数据。第一种方法的Renderer Utilization 和 Tiler Utilization 基本在90%左右。帧率在20左右。第四种方法的Renderer Utilization 和 Tiler Utilization 基本在20%左右。帧率接近60。帧率越接近60滑动越顺畅。
可是通过跟@nerozhao的讨论发现第四种Core Graphics绘制圆角会有大量的内存占用,并且每次绘制的时候CUP消耗会很大。
因为@nerozhao使用了UITableView进行测试,由于UITableView滚动的时候是一直在复用的,UIImageView会重复绘制,因此会一直消耗CUP,而后你就能看的明显的卡顿。@nerozhao在UITableView里图片的绘制在后台线程进行绘制,解决了卡顿问题,可是因为是在后台线程的异步绘制因此在滚动的时候会看到图片先是正方形而后再变成圆形。
而我使用的是UIScrollerView进行的测试,只有第一次绘制的时候会占用CUP资源,因此滑动的时候仍是挺流畅的,可是内存消耗仍是很大。若是是主线程绘制的话会阻塞一点时间的主线程,然后台线程绘制的话内存消耗会更大,特别容易崩溃。
因此第四种方法当图片特别多的时候很容易Received memory warning致使崩溃

相关文章
相关标签/搜索