博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS 离屏渲染
阅读量:5962 次
发布时间:2019-06-19

本文共 2085 字,大约阅读时间需要 6 分钟。

  hot3.png

先放结论

  • 如果能够只用 cornerRadius 解决问题,就不用优化。

  • 如果必须设置 masksToBounds,可以参考圆角视图的数量,如果数量较少(一页只有几个)也可以考虑不用优化。

  • UIImageView 的圆角通过直接截取图片实现,其它视图的圆角可以通过 Core Graphics 画出圆角矩形实现。

GPU渲染机制:

CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。

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

  • On-Screen Rendering

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

  • Off-Screen Rendering

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

离屏渲染的触发方式

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

  • shouldRasterize(光栅化)
  • masks(遮罩)
  • shadows(阴影)
  • edge antialiasing(抗锯齿)
  • group opacity(不透明)
  • 复杂形状设置圆角等
  • 渐变

当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染被唤起。

屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。

所以当使用离屏渲染的时候会很容易造成性能消耗,因为在OPENGL里离屏渲染会单独在内存中创建一个屏幕外缓冲区并进行渲染,而屏幕外缓冲区跟当前屏幕缓冲区上下文切换是很耗性能的。

Instruments监测离屏渲染

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

  • Color Offscreen-Rendered Yellow

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

  • Color Hits Green and Misses Red

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

iOS版本上的优化

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

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

这可能是苹果也意识到离屏渲染会产生性能问题,所以能不产生离屏渲染的地方苹果也就不用离屏渲染了。

正常添加:label.layer.cornerRadius = 8。但通常我们还会加上:label.layer.masksToBounds = true。此时并不会有性能损耗的,但是如是设置Color Offscreen-Rendered Yellow会发现 label 的四周出现了黄色的标记,说明这里出现了离屏渲染。

但是根据大神的测试,离屏渲染并非由设置圆角导致的,因为 UIView 只是设置了 cornerRadius,但它没有出现离屏渲染,设置 masksToBounds 会导致离屏渲染,从而影响性能,但是影响并没有想象中的那么大,例如有大神测试 17 个带有圆角的视图,滑动时的帧数依然在 58 - 59 fps 左右波动,甚至当屏幕上有奖金40个的时候,才开始有点卡顿,fps 下降到 33 左右,此时才会影响用户体验。

高效地设置圆角

普通的 UIView 设置圆角,和为 UIImageView 设置圆角的原理不同

override func drawRect(rect: CGRect) {

    let maskPath = UIBezierPath(roundedRect: rect,
                                byRoundingCorners: .AllCorners,
                                cornerRadii: CGSize(width: 3, height: 3))
    let maskLayer = CAShapeLayer()
    maskLayer.frame = self.bounds
    maskLayer.path = maskPath.CGPath
    self.layer.mask = maskLayer
}

此段代码可以实现 cornerRadius = 3 的效果,但是并不是完美的代码,但是实际测试中发现,对drawRect的重写会严重影响内存(内存暴增)和用户体验(更加卡顿),这种方法本质上是用遮罩层 mask 来实现,因此同样无可避免的会导致离屏渲染,但是带来的副作用是相比之前的fps在屏幕上圆角控件多的情况下,下降是原来的一倍!

转载于:https://my.oschina.net/rainwz/blog/760818

你可能感兴趣的文章
软件开发模式对比(瀑布、迭代、螺旋、敏捷)
查看>>
unity3d uGUI中世界坐标与屏幕坐标实际运用
查看>>
基于keepalived主从模型实现Nginx的高可用
查看>>
一个完整的微服务系统,应该包含哪些功能?
查看>>
oracle 单实例 RAC架构 及RAC启动流程
查看>>
Iptables防火墙在企业网中的应用
查看>>
ClassLoader的双亲委派机制
查看>>
MongoDB复制错误及处理方法
查看>>
Linux 系统安全常规优化
查看>>
电脑死机的各种硬件和软件原因解析
查看>>
WinForm窗体背景渐变色的设置
查看>>
我的友情链接
查看>>
超棒的PowerShell内建的帮助系统
查看>>
Nginx基础之http、https、socket 、SSL及TCP简单关系
查看>>
SQL Server游标的使用
查看>>
转:java反射机制
查看>>
第三部分: zabbix安装配置
查看>>
Qt for Windows:Qt 5.5 MinGW 静态编译版本制作带Mysql
查看>>
test
查看>>
Linux运维工程师笔试题第十二套
查看>>