离屏渲染学习笔记

一、概念理解

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

  • On-Screen Rendering

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

  • Off-Screen Rendering

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

二、离屏渲染的是是非非

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

  • 创建新缓冲区

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

  • 上下文切换

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

三、离屏渲染触发方式

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

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

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

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

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

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

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

五、Instruments

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

  • Color Offscreen-Rendered Yellow

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

  • Color Hits Green and Misses Red

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

六、如何抉择

现在摆在我们面前得有三个选择:当前屏幕渲染、离屏渲染、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导致崩溃

离屏渲染学习笔记 /iOS圆角性能问题的更多相关文章

  1. 淘宝网触屏版 - 学习笔记(1 - 关于meta)

    注:本文是学习笔记,并不是教程,所以会有很多我不理解或猜测的问题,也会有不尽详实之处,望见谅. <meta charset="utf-8"> <meta cont ...

  2. 淘宝网触屏版 - 学习笔记(0 - 关于dpr)

    注:本文是学习笔记,并不是教程,所以会有很多我不理解或猜测的问题,也会有不尽详实之处,望见谅. 对于pc端网页设计师来说,移动端的网页制作,我之前只是简单的加了一个 <meta name=&qu ...

  3. iOS 防止离屏渲染为 image 添加圆角

        // image 分类 - (UIImage *)circleImage{ // NO 代表透明 UIGraphicsBeginImageContextWithOptions(self.siz ...

  4. iOS学习笔记——iOS高级控件

    UITableView UITableView的样式有两种,一种是Grouped(左图),另一种是Plain(右图),如下图,它的属性是style,类型为UITableViewStyle,枚举值分别是 ...

  5. RHCA学习笔记:RH442-Unit6 磁盘性能调整

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://alansky.blog.51cto.com/634963/654451 UNIT ...

  6. JVM学习笔记:虚拟机性能监控

    JDK中除了包含与开发密切相关的jar包外,还包含了很多非常实用的工具.在%JAVA_HOME%\bin\目录下面除了命令行工具外,还包括了几个强大的可视化工具.这些工具可以辅助我们开发.调试应用程序 ...

  7. RHCA442学习笔记-Unit13网络性能调整

    UNIT 13 Essential Network Tuning 网络性能调整        目标:1. 应用队列技术最大化网络吞吐量            2. 调整TCP和non-TCP网络soc ...

  8. android开发学习笔记:圆角的Button

    转自:http://www.cnblogs.com/gzggyy/archive/2013/05/17/3083218.html 在res目录下的drawable-mdpi建立xml文件shape.x ...

  9. PHP性能优化学习笔记--PHP周边性能优化--来自慕课网Pangee http://www.imooc.com/learn/205

    PHP一般运行于Linux服务器中,周边主要包括:Linux运行环境.文件存储.数据库.缓存.网络 常见PHP场景的开销次序: 读写内存<<读写数据库(使用内存作为缓存.异步处理)< ...

随机推荐

  1. 由一段JS代码引发的思考

    不知道大家在编程的时候有没有遇到过这种情况,就是在循环遍历删除一部分内容的时候,发现只能删除其中一部分,而另一部分却总也删不掉,然后觉得自己的逻辑没有问题啊,于是陷入了深深的抑郁之中…… 昨天在处理一 ...

  2. 【BZOJ-4590】自动刷题机 二分 + 判定

    4590: [Shoi2015]自动刷题机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 156  Solved: 63[Submit][Status ...

  3. 【BZOJ-4521】手机号码 数位DP

    4521: [Cqoi2016]手机号码 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 303  Solved: 194[Submit][Status ...

  4. css选择器([class*=" icon-"], [class^=icon-] 的区别)

    官方解释: [attribute^=value],a[src^="https"],选择其 src 属性值以 "https" 开头的每个 <a> 元素 ...

  5. [NOIP2015] 提高组 洛谷P2668 斗地主

    题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3<4< ...

  6. MyBatis详解 与配置MyBatis+Spring+MySql

    MyBatis 是一个可以自定义SQL.存储过程和高级映射的持久层框架.MyBatis 摒除了大部分的JDBC代码.手工设置参数和结果集重获.MyBatis 只使用简单的XML 和注解来配置和映射基本 ...

  7. 软件工程个人作业-Week2

    第一部分  调研, 评测 必应词典客户端版本:安卓版5.2.2 bug描述一:在学习页面点击“单词挑战”或“我爱说英语”会弹出“加载失败,请稍后重试”,无论点击多少次都加载不出来. bug描述二:在未 ...

  8. Mysql学习笔记(四)聊聊数据库索引

    小心情(可直接跳到分割线后) 今天心情好些了.一些浓的化不开的坏情绪,也渐渐的在晚上解决掉一个复杂的逻辑问题后,渐渐消散了. 今天中午去吃饭的时候,坤哥漫不经心的说:'我这么多年终于悟出了一个道理,人 ...

  9. JS生成随机数的各种函数

    第一种方法 /* *@desc:生成随机字符串 *@remark:toString方法可以接收一个基数作为参数的原理,这个基数从2到36封顶.如果不指定,默认基数是10进制 */ function g ...

  10. javascript变量、作用域和内存问题......

    1基本类型是指那些保存在栈内存的简单数据段,引用类型是指那些保存在堆内存中的对象,变量中保存的实际上只是一个指针. 2javascript中5种基本数据类型Undefined,Null,Boolean ...