美文网首页
四、深入理解ios离屏渲染

四、深入理解ios离屏渲染

作者: 含笑州 | 来源:发表于2020-08-30 12:57 被阅读0次

    我相信大多数做过iOS开发的都应该知道离屏渲染,也知道常见的造成离屏渲染的方式,但是对于它的原理可能了解的并不是很多,接下来我带大家了解一下。

    一、了解离屏渲染

    正常渲染流程如下:

    图片来自网络

    在正常情况下,经过CPU的计算以及GPU的渲染之后,会将结果存放到帧缓存区,随后视频控制器会读取帧缓存区的数据,经过数模转换,再逐行显示到屏幕上。

    离屏渲染流程如下:图片来自网络

    但是在有些情况下,系统会在屏幕以外开辟缓存区来存放一些中间状态的数据,等待全部的图层都渲染到离屏缓存区之后,分别从各个离屏缓存区取出数据,分别做相应的操作(裁剪等)之后,组合存入帧缓存区,再等待屏幕控制器的读取和屏幕刷新。

    二、离屏渲染的利弊

    1.劣势:

    离屏渲染其实是加大了系统的负担,确实会造成性能上的损耗。主要表现在以下几个方面。

    离屏渲染需要额外的存储空间,存储空间大小的上限是2.5倍的屏幕像素大小,一旦超过,则无法进行复用

    离屏渲染缓存内容有时间限制,缓存内容100ms如果没有使用,那么它就会被丢弃,无法进行复用

    容易掉帧:一旦因为离屏渲染导致最终存入帧缓存区的时候,超过了16.67ms,则会出现掉帧的情况

    2.优势

    虽然离屏渲染会需要多开辟出新的临时缓存区来存储中间状态,但是对于多次出现在屏幕上的数据,可以提前渲染好,从而达到复用的目的,这样CPU/GPU就不用做一些重复的计算。

    其实在很多iOS开发的需求背景之下,比如 一些特殊动画效果的开发,此时需要多图层以及离屏缓存区保存中间状态,这种情况下就不得不使用离屏渲染

    三、常见触发离屏渲染的几种方式:

    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、如果layer不能被重用,则没必要使用光栅化;

    2、如果我们更新已光栅化的layer,会造成大量的离屏渲染。例如UITableViewCell因为复用的原因,重绘是很频繁的。如果此时设置了光栅化,反而会造成大量离屏渲染,降低性能;

    3、离屏渲染的缓存是有时间限制的,100ms内如果缓存的内容没有被复用,则会被丢弃,也就无法复用了;

    4、离屏渲染的空间有限,超过2.5倍屏幕像素的大小,离屏渲染也会失效,无法复用

    四、分析圆角触发离屏渲染案例

    1.代码如下:

    //1.按钮存在背景图片

        UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];

        btn1.frame=CGRectMake(100,30,100,100);

        btn1.layer.cornerRadius = 50;

    [self.view addSubview:btn1];

        [btn1setImage:[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会造成离屏渲染

    五、如何检测离屏渲染呢

    六、圆角导致的离屏渲染解决方案

    方案一:

    _imageView.clipsToBounds=YES;

    _imageView.layer.cornerRadius=4.0;

    方案二:

    方案三:

    方案四:

    最后大家也可以参考下YYImage中对于圆角的处理代码:

    - (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius

     corners:(UIRectCorner)corners

     borderWidth:(CGFloat)borderWidth

     borderColor:(UIColor*)borderColor

     borderLineJoin:(CGLineJoin)borderLineJoin {

    if (corners != UIRectCornerAllCorners) {

     UIRectCorner tmp = 0;

    if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft;

    if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight;

    if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft;

    if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight;

     corners = tmp;

     }

    UIGraphicsBeginImageContextWithOptions(self.size,NO,self.scale);

     CGContextRef context = UIGraphicsGetCurrentContext();

    CGRectrect = CGRectMake(0,0,self.size.width,self.size.height);

     CGContextScaleCTM(context, 1, -1);

     CGContextTranslateCTM(context, 0, -rect.size.height);

    CGFloatminSize =MIN(self.size.width,self.size.height);

    if(borderWidth < minSize /2) {

     UIBezierPath*path = [UIBezierPathbezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth)byRoundingCorners:corners

    cornerRadii:CGSizeMake(radius, borderWidth)];

     [pathclosePath];

     CGContextSaveGState(context);

     [pathaddClip];

    CGContextDrawImage(context, rect,self.CGImage);

     CGContextRestoreGState(context);

     }

    if(borderColor && borderWidth < minSize /2&& borderWidth >0) {

    CGFloatstrokeInset = (floor(borderWidth *self.scale) +0.5) /self.scale;

     CGRectstrokeRect =CGRectInset(rect, strokeInset, strokeInset);

    CGFloatstrokeRadius = radius >self.scale /2? radius -self.scale /2:0;

     UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius,

    borderWidth)];

     [pathclosePath];

     path.lineWidth= borderWidth;

     path.lineJoinStyle= borderLineJoin;

     [borderColorsetStroke];

    }

    相关文章

      网友评论

          本文标题:四、深入理解ios离屏渲染

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