深入解析iOS离屏渲染原理

619 阅读4分钟
  • 如何检测项目中那些图层触发了离屏渲染

    Debug模式下虚拟机为例:
    选中模拟器->Debug->Color Off-screen Rendered

    效果:
    相关代码:

        UIImageView * view1 = [[UIImageView alloc]initWithFrame:CGRectMake(100, 130, 100, 100)];
        view1.layer.cornerRadius = 50;
        view1.backgroundColor = [UIColor redColor];
        [self.view addSubview:view1];
        
        UIImageView * view2 = [[UIImageView alloc]initWithFrame:CGRectMake(100, 240, 100, 100)];
        view2.image = [UIImage imageNamed:@"timg.jpeg"];
        view2.layer.cornerRadius = 50;
        view2.clipsToBounds = YES;
        [self.view addSubview:view2];
        
        UIImageView * view3 = [[UIImageView alloc]initWithFrame:CGRectMake(100, 350, 100, 100)];
        view3.image = [UIImage imageNamed:@"timg.jpeg"];
        view3.layer.cornerRadius = 50;
        view3.clipsToBounds = YES;
        view3.backgroundColor = [UIColor redColor];
        [self.view addSubview:view3];
        
        UIImageView * view4 = [[UIImageView alloc]initWithFrame:CGRectMake(100, 460, 100, 100)];
        view4.image = [UIImage imageNamed:@"timg.jpeg"];
        view4.layer.cornerRadius = 50;
        view4.backgroundColor = [UIColor redColor];
        [self.view addSubview:view4];
    

    很多人对于离屏渲染都有个误区就是圆角加clipsToBounds/masksToBounds设置为true就会造成离屏渲染。冲上面的代码就可以推到这种说法,从上图可知只有view3造成了离屏渲染,而view2设置了圆角并且clipsToBounds设置为true,但是view2并没有造成离屏渲染,究竟触发离屏渲染的真正原因是什么呢?

  • 普通渲染流程和离屏渲染流程

    • 普通渲染流程
    • 离屏渲染流程

      首先不管是哪一种渲染最后视屏控制器都是从帧缓冲区中读取数据然后显示,但是离屏渲染比正常的渲染多了一个离屏缓冲区
  • 造成离屏渲染的两种原因

    • shouldRasterize 光栅化

      When the value of this property is YES, the layer is rendered as a bitmap in its local coordinate space and then composited to the destination with any other content 大概意思就是当layer开始了shouldRasterize之后就会保存一段时间,以供其他图层复用

      光栅化使用建议:

      1. 如果layer不能被复用,则不要打开光栅化
      2. 如果layer不是静态的,需要频繁被修改。比如处于动画中,那么开启光栅化反而影响效率
      3. 离屏渲染缓存内容是有时间限制的,缓存内容在100ms内内容如果没有被使用则会被丢弃,无法复用
      4. 离屏渲染缓存空间有限,超过2.5倍屏幕像素大小的话会失效,且无法复用
    • 圆角触发的离屏渲染
      首先理解一下图层叠加的算法油画算法就是先画远的物体然后再画近的物体由远及近

      再来看一下CALayer的层次结构,CALayer由背景色backgroundColor、内容contents、边缘borderWidth&borderColor构成,如下图所示
      然后再看一下官方API关于圆角的解释
      就是说圆角只会对背景色和边框起作用,并不会对contents起作用,所以要想对contents也做圆角处理那么需要将圆角意外的东西裁减掉就是clipsToBounds/masksToBounds设置为true。
      接下来看一下正常绘制的一个过程:
      可以发现每次绘制并显示完成之后会将对应的subLayer移除,就没办法进行圆角处理,所以引入离屏缓冲区先图层绘制存放到离屏缓冲区,都绘制完成之后再一个一个拿出来裁剪混合存储到帧缓冲区最后显示:
      说白了就是牵扯到给contents做圆角并且还有混合过程的情况就会出现离屏渲染,例如只设置UIImageView的image不加背景色设置圆角并且裁剪并不会出发离屏渲染,但是要设置背景色就牵扯到两个以上图层的混合(应为裁剪是在所有图层绘制完成之后才对每个图形进行裁剪,所以就要将所有图层显存放到离屏缓冲区中然后一个一个提取裁剪最后混合显示)所以就要利用到离屏缓冲区,所以就会触发离屏渲染

  • 常见触发离屏渲染的几种情况

    1. 使用了 mask 的 layer (layer.mask)
    2. 需要进行裁剪的 layer (layer.masksToBounds /view.clipsToBounds)
    3. 设置了组透明度为 YES,并且透明度不为 1 的layer (layer.allowsGroupOpacity/ layer.opacity)
    4. 添加了投影的 layer (layer.shadow*)
    5. 采用了光栅化的 layer (layer.shouldRasterize)
    6. 绘制了文字的 layer (UILabel, CATextLayer, Core Text 等)
  • 离屏渲染耗性能的原因

    1. 离屏渲染要创建离屏缓冲区会占用内存
    2. 离屏渲染过程中需要多次切换上下文环境,首先要冲当前屏幕切换到离屏等渲染完成之后需要显示的时候再冲离屏切换到当前屏幕,切换上下文环境的代价是很大的