离屏渲染解决方案

这个版本我们设计师采用了大量的圆角, 阴影,缩放等效果, 导致刚打开app就加载到了首页,iPhone6以下的机型普遍可以感觉到卡顿的现象.为了解决这一问题我就想到了应该是离屏渲染导致的结果.

GPU屏幕渲染有两种方式:

(1)On-Screen Rendering (当前屏幕渲染) 

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

(2)Off-Screen Rendering (离屏渲染)

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

当前屏幕渲染不需要额外创建新的缓存,也不需要开启新的上下文,相对于离屏渲染性能更好。但是受当前屏幕渲染的局限因素限制(只有自身上下文、屏幕缓存有限等),当前屏幕渲染有些情况下的渲染解决不了的,就使用到离屏渲染。

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

(1)创建新缓冲区

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

(2)上下文切换

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

由于垂直同步的机制,如果在一个 HSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

既然离屏渲染这么耗性能,为什么有这套机制呢?

有些效果被认为不能直接呈现于屏幕,而需要在别的地方做额外的处理预合成。图层属性的混合体没有预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染。屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。

下面的情况或操作会引发离屏渲染:

- 为图层设置遮罩(layer.mask)

- 将图层的layer.masksToBounds / view.clipsToBounds属性设置为true

- 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0

- 为图层设置阴影(layer.shadow *)。

- 为图层设置layer.shouldRasterize=true

- 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层

- 文本(任何种类,包括UILabel,CATextLayer,Core Text等)。

- 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。

- 最根本的原因是一个视图含有多个图层,GPU无法一次性裁剪多个图层,需要单独裁剪,然后再合并(比如button上设置图片,此时button含有两个图层,一个view,一个imageview)。

优化方案

官方对离屏渲染产生性能问题也进行了优化:

iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染。

iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。

1、圆角优化

我们设置圆角一般通过如下方式:

imageView.layer.cornerRadius = CGFloat(10);

imageView.layer.masksToBounds = YES;

这样处理的渲染机制是GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会给我们带来额外的性能损耗,如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,性能的代价会宏观地表现在用户体验上——掉帧。

对于只需要圆角的某些场合,也可以用一张已经绘制好的圆角图片覆盖到原本视图上面来模拟相同的视觉效果。最彻底的解决办法,就是把需要显示的图形在后台线程绘制为图片,避免使用圆角、阴影、遮罩等属性。

2、shadow优化

       let shadowView =UIView.init(frame:self.frame)

        shadowView.backgroundColor=UIColor.white

        shadowView.layer.shadowColor=UIColor.red.cgColor

        shadowView.layer.shadowOpacity=0.6

        shadowView.layer.shadowOffset=CGSize.init(width:0, height:4)

        shadowView.layer.shadowRadius= 4


添加这一行可以解决离屏渲染卡顿问题

缓存离屏渲染的轨迹,以便重用

shadowView.layer.shadowPath=UIBezierPath.init(rect:self.bounds).cgPath


如果不保存渲染的轨迹的话, 每次执行到这些需要离屏渲染的视图时都需要重新计算渲染的内容, 保存了以后GPU下次直接拿来上次的使用.

经过改良后的渲染, 再使用instruments 检测, 发现黄色的离屏渲染已经没有了,当然由此引起的卡顿想现象也消失了 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 屏幕显示图像的原理: 高中物理应该学过显示器是如何显示图像的:需要显示的图像经过CRT电子枪以极快的速度一行一行的...
    青火阅读 27,876评论 18 104
  • 实际开发中,大多会遇到圆角或者圆形的控件的情况。通常,简便的解决方案主要是: 1.让美工做一个圆角的图片...
    LanWor阅读 1,775评论 1 5
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,271评论 5 13
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,696评论 6 30
  • 个人博客地址:Lixuzong's Blog 我们所在屏幕上看到的都是Core Animation框架提供的,所以...
    猿鹿说阅读 554评论 0 1

友情链接更多精彩内容