iOS的离屏渲染

图像显示原理(Off-Screen Rendering)

  1. CPU(Central Processing Unit,中央处理器)和GPU(Graphics Processing Unit,图形处理器)通过总线连接起来
  2. CPU往往输出结果的是位图。它负责对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics等。
  3. CPU输出的位图信息在合适的时机上传到GPU,GPU拿到位图后进行图层的渲染,包括纹理的合成。之后会将处理结果放在帧缓冲区。
  4. 由视屏控制器根据垂直同步信号(VSync)在指定的时间之前,在帧缓冲区提取屏幕显示内容,然后将其显示到手机屏幕上。

卡顿产生等原因

  • CPU和GPU工作时所花时间过长,垂直同步信号过来的时候,计算和渲染还没有完成,导致掉帧
  • 卡顿解决的主要思路:尽可能减少CPU、GPU资源消耗

什么是离屏渲染

当我们在UIView上设置某些图层属性,比如设置视图的圆角属性、蒙层遮罩,标记未预合成之前,不能用于直接显示的时候,就触发了离屏渲染。离屏渲染的概念起源于GPU,指的是在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
离屏渲染导致卡顿的原因:

  1. 需要创建新的缓冲区
  2. 离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕
  3. 增加了GPU的工作量,可能导致GPU + CPU的耗时过多,在16.7毫秒(1秒60帧)的时候没有完成任务,出现卡顿掉帧。

如何检测是否触发离屏渲染?

WechatIMG422.png

打开模拟器运行,找到设置选项debug,勾选Color Off-screen Rendered,如果触发离屏渲染就会将控件标记为黄色。

什么操作会触发离屏渲染

圆角

  • 在iOS9之前,UIImageView同时设置layer.masksToBounds = YESlayer.cornerRadius大于0时就会触发离屏渲染,iOS 9之后系统做了优化,不会触发了。虽然系统做了优化,但是用这种方式给UIImageView圆角切割又设置了背景色,那么还是会触发离屏渲染的(普通UIView设置纯背景色不会)。
  • UIButtonmasksToBounds = YES下发生离屏渲染与背景图存不存在有关系, 如果没有给按钮设置 btn.image = [UIImage imageName:@"xxxxx"]; 是不会产生离屏渲染的。

如何解决呢?

  1. 让美工切图
  2. 通过CoreGraphics绘制裁剪圆角

不透明(group opacity)

GroupOpacity 是指 CALayerallowsGroupOpacity属性,UIView 的alpha属性等同于 CALayer opacity属性。开启 GroupOpacity 后,子 layer在视觉上的透明度的上限是其父layer 的opacity
GroupOpacity 开启离屏渲染的条件是:layer.opacity != 1.0并且有子 layer 或者背景图。

遮罩(mask)

  • mask生来会触发离屏渲染的。
  • mask是CALayer的一个属性,它也是一个CALayer对象,默认为nil。当给一个layer设置mask时,mask的alpha值决定了多少层背景跟内容通过并显示。当mask的alpha为0时,整个mask与底层CALayer都不会显示。当alpha为1时,只有mask区域以内非透明区域,会显示该区域对应底层CALayer的像素值。

阴影(shadow)

阴影直接合成在视图的下面,视图结构里并没有多出一个视图。在没有指定阴影路径时,阴影是沿着视图的非透明部分扩展的,而且 CALayer 的三个视觉元素至少有一个存在时才会有阴影。

使用阴影必须保证 layer 的masksToBounds = false,因此阴影与系统圆角不兼容。但是注意,只是在视觉上看不到,对性能的影响依然。通常这样实现一个阴影:

let imageViewLayer = avatorView.layer
imageViewLayer.shadowColor = UIColor.blackColor().CGColor
imageViewLayer.shadowOpacity = 1.0 //此参数默认为0,即阴影不显示
imageViewLayer.shadowRadius = 2.0 //给阴影加上圆角,对性能无明显影响
imageViewLayer.shadowOffset = CGSize(width: 5, height: 5)
//设定路径:与视图的边界相同
let path = UIBezierPath(rect: cell.imageView.bounds)
imageViewLayer.shadowPath = path.CGPath//路径默认为 nil

也就是说在设置阴影同时给layer设置相同的shadowPath,可避免离屏渲染。

光栅化(EdgeAntialiasing)

shouldRasterize(光栅化): 将图转化为一个个栅格组成的图象。 光栅化特点:每个元素对应帧缓冲区中的一像素。

shouldRasterize = YES在其它属性触发离屏渲染的同时,会将光栅化后的内容缓存起来,如果对应的layer或者 sublayers没有发生改变,在下一帧的时候可以直接复用,从而减少渲染的频率。
当使用光栅化是, 可以开启 "Color Hits Green and Misses Red"来检查该场景下是否适合选择光栅化,绿色表示缓存被复用,红色表示缓存在被重复创建.对于经常变动的内容,不要开启,否则会造成性能的浪费。

如果cell里面的内容不断变化(cell的复用),如果设置了cell.layer.shouldRaseterize = YES则会降低图形性能,造成离屏渲染.

参考链接

离屏渲染优化详解:实例示范+性能测试
iOS-离屏渲染详解

你可能感兴趣的:(iOS的离屏渲染)