美文网首页OpenGL深入
iOS开发之离屏渲染

iOS开发之离屏渲染

作者: 爱看书de图图 | 来源:发表于2020-07-07 16:48 被阅读0次

  离屏渲染可谓是iOS开发中老生常谈的问题了,不管是开发中尽量避免也好,还是面试中这是最高频的面试题目也好。大多数人都知道,设置View的阴影圆角等容易触发离屏渲染,大多数人都知道这个概念,也能说出个大概,但是了解表面现象并不是我们本意,我们需要探索触发离屏渲染的本质,真正的揭开离屏渲染的神秘面纱。
  在OpenGL中,渲染缓冲区有两种:

On-Screen Rendering (当前屏幕渲染)
指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。APP在显示的时候,是通过GPUCPU合作,不断的从FrameBuffer渲染到屏幕上。
Off-Screen Rendering (离屏渲染)
指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。因为有些图形是需要进行混合计算,保留一个中间形态,然后再显示在屏幕上的。

  离屏渲染出发原因如下图所示:①为遮罩层,②为背景图,③为合成后的图片。
  首先要显示图片③,那么在计算机底层通过顶点着色器和片源着色器开始绘制的时候,是不可能直接渲染完成的,计算机也要经过运算,首先要在内部渲染①和②,此时的①和②的遮罩纹理和图层纹理不能直接显示在屏幕上,所以需要一个中间态来保存,也就是离屏渲染缓冲区,然后再第三步的时候,由帧缓冲区提交显示。需要注意,离屏渲染空间大小有限制,为当前屏幕的2.5倍,超过这个限制,就不能存放在离屏缓冲区

离屏渲染.jpeg
  结合上图,我们知道离屏渲染的代价很高,想要进行离屏渲染,首选要创建一个新的缓冲区,屏幕渲染会有一个上下文环境,离屏渲染的整个过程需要切换上下文环境,先从当前屏幕切换到离屏,等结束后,又要将上下文环境切换回来。这也是为什么会消耗性能的原因了。那么我们就会有疑问,既然离屏渲染会有性能问题,为什么还会需要离屏渲染?
离屏渲染存在的作用:

1、有些特殊效果不能一次性的直接显示出来,需要使用额外的Off-Screen Buffer来保存中间状态,这些是由系统自动触发的。
2、离屏渲染可以带来效率的提升,因为某些效果会多次出现在屏幕的时候,需要提前渲染到Off-Screen Buffer,来达到复用的目的,这些是手动触发的。

离屏渲染触发的第二个原因:ShouldRasterize光栅化

  当一个图像混合了多个图层,每次移动时,每一帧都要重新合成这些图层,十分消耗性能。当我们开启光栅化后,会在首次产生一个位图缓存,当再次使用时候就会复用这个缓存。但是如果图层发生改变的时候就会重新产生位图缓存。

光栅化使用建议:
1.如果layer不能被复用,则没有必要打开光栅化;
2.如果layer不是静态的,需要被频繁修改,比如处于动画之中,那么开启离屏渲染反而影响效率;
3.离屏渲染缓存内容有时间限制,缓存内容100ms内容如果没有被使用,那么就会丢弃,无法进行复用;
4.离屏渲染缓存空间有限,超过2.5倍屏幕像素大小的话,也会失效,且无法进行复用;

  需要注意并不是所有的设置layer的圆角就会触发离屏渲染,要出发离屏渲染有很多的条件限制,下图可以很好地解释怎么样触发,接下来我也会用案例来说明。

图片.png
  一个图片包含边框,内容,背景,设置layer.cornerRadius指挥设置背景和border圆角,不会设置content的圆角,除非同时设置了layer.masksToBoundsYES(对应View中的ClipsToBounds)。
    //1.按钮存在背景图片
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 30, 100, 100);
    btn1.layer.cornerRadius = 50;
    [self.view addSubview:btn1];
    
    [btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
    btn1.clipsToBounds = YES;
    
    //2.按钮不存在背景图片
    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn2.frame = CGRectMake(100, 180, 100, 100);
    btn2.layer.cornerRadius = 50;
    btn2.backgroundColor = [UIColor blueColor];
    [self.view addSubview:btn2];
    btn2.clipsToBounds = YES;
    
    //3.UIImageView 设置了图片+背景色;
    UIImageView *img1 = [[UIImageView alloc]init];
    img1.frame = CGRectMake(100, 320, 100, 100);
    img1.backgroundColor = [UIColor blueColor];
    [self.view addSubview:img1];
    img1.layer.cornerRadius = 50;
    img1.layer.masksToBounds = YES;
    img1.image = [UIImage imageNamed:@"btn.png"];
    
    //4.UIImageView 只设置了图片,无背景色;
    UIImageView *img2 = [[UIImageView alloc]init];
    img2.frame = CGRectMake(100, 480, 100, 100);
    [self.view addSubview:img2];
    img2.layer.cornerRadius = 50;
    img2.layer.masksToBounds = YES;
    img2.image = [UIImage imageNamed:@"btn.png"];

  上面案例中,1,3会出发离屏渲染,2,4不会,因为2,4只对View的单一属性进行了修改,不需要额外的离屏缓冲区来保存纹理数据,所以不会出发离屏渲染。下面是我总结的离屏渲染常见的触发情况:

1.使⽤了masklayer(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 等)

  在实际的项目中,我们在处理圆角方案的时候,可以有很多种来避免离屏渲染,最常见的方案有:

方案一

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

imageView.layer.cornerRadius = CGFloat(10);
imageView.layer.masksToBounds = YES;
方案二

  使用贝塞尔曲线UIBezierPathCore Graphics框架画出一个圆角。

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; 
imageView.image = [UIImage imageNamed:@"myImg"]; 
//开始对imageView进行画图 
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0); 
//使用贝塞尔曲线画出一个圆形图 
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds 
cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext(); 
//结束画图 
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
方案三

  使用CAShapeLayerUIBezierPath设置圆角。

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; 
imageView.image = [UIImage imageNamed:@"myImg"]; 
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds 
byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init]; 
//设置大小 
maskLayer.frame = imageView.bounds; 
//设置图形样子 
maskLayer.path = maskPath.CGPath;
imageView.layer.mask = maskLayer; 
[self.view addSubview:imageView];

  方案三比方案二消耗内存更小,渲染速度更快,建议使用方案三。

Shadow优化

  对于shadow,如果图层是个简单的几何图形或者圆角图形,我们可以通过设置shadowPath来优化性能,能大幅提高性能。示例如下:

imageView.layer.shadowColor = [UIColor grayColor].CGColor;
imageView.layer.shadowOpacity = 1.0;
imageView.layer.shadowRadius = 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame];
imageView.layer.shadowPath = path.CGPath;

  最后是一些其他的优化建议:

1.当我们需要圆角效果时,可以让UI将图切好,这是最简单直接的方案
2.减少图片层级,因为图片绘制复杂度以层级的指数倍增加
3.尽量使用不包含透明alpha通道的图片资源
4.使用代码手动生成圆角Image设置到要显示的View上,利用UIBezierPath画出来圆角

相关文章

  • iOS离屏渲染的检测和优化

    在iOS离屏渲染介绍了离屏渲染的逻辑和原理,我们知道离屏渲染对于性能会有较大的消耗,那么开发中怎么避免产生离屏渲染...

  • iOS性能优化之图片圆角

    在Apple官方文档中多次提出开发时,避免触发离屏渲染效果.离屏渲染触发的情况有很多种,具体可参考iOS离屏渲染相...

  • 离屏渲染

    离屏渲染是在iOS开发过程中脱离不了的话题,那么什么是离屏渲染以及哪些情况会导致离屏渲染呢?以及离谱渲染有哪些优势...

  • 离屏渲染

    iOS离屏渲染优化(附DEMO) - CocoaChina_让移动开发更简单

  • iOS开发之离屏渲染

      离屏渲染可谓是iOS开发中老生常谈的问题了,不管是开发中尽量避免也好,还是面试中这是最高频的面试题目也好。大多...

  • 离屏渲染的探究

    什么是离屏渲染 作为一个 iOS 开发者,你肯定听说过离屏渲染,什么是离屏渲染呢? 案例 在模拟器上显示一张裁剪的...

  • iOS离屏渲染

    iOS-离屏渲染详解关于iOS离屏渲染的深入研究关于离屏渲染这两片文章分析的很详细。 渲染过程 根据渲染流水线示意...

  • iOS shouldRasterize光栅化--离屏渲染

    参考资料:ios开发中view.layer.shouldRasterize = YES 的使用说明iOS 离屏渲染...

  • iOS离屏渲染解析

    iOS离屏渲染解析

  • iOS圆角的离屏渲染,你真的弄明白了吗

    iOS圆角的离屏渲染,你真的弄明白了吗iOS圆角的离屏渲染,你真的弄明白了吗

网友评论

    本文标题:iOS开发之离屏渲染

    本文链接:https://www.haomeiwen.com/subject/utxaqktx.html