iOS 离屏渲染原理

什么是离屏渲染

正常情况下,在当前屏幕显示的内容,由 GPU 渲染完成后放到当前屏幕的帧缓存区,不需要额外的渲染空间。iOS的屏幕刷新率是 60Hz,也就是刷新一帧的时间是 16.67 ms,每隔这段时间视频控制器就会去读一次缓存区的内容来显示。

正常渲染.png

而离屏渲染(offscreen-rendering)顾名思义为屏幕外的渲染,即渲染的结果不会直接呈现到当前屏幕上,而是等待合适的时机才会被显示。

离屏渲染.png

如何查看是否触发了离屏渲染

步骤:打开模拟器 => debug => Color Off-screen Rendered

离屏渲染示例

图中第一个和第三个按钮出现黄色背景,说明是触发了离屏渲染。

产生离屏渲染有两个原因:

1、当多个图层需要额外渲染和合并的时候,不能直接渲染到FrameBuffer,需要借助offscreen buffer 暂存各个图层渲染结果,等各个图层渲染完成之后再进行合并到FrameBuffer供屏幕显示;

2、当我们使用CALayer的属性shouldRestrize(光栅化)并设置为YES时,就会触发离屏渲染。以下是官方解释:

Declaration

@property BOOL shouldRasterize;
Discussion

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. 

shouldRasterize (光栅化)使用建议
1、如果layer没有被复用,则没必要打开光栅化;
2、如果layer是动态的,需要频繁修改,比如处在动画中,那么开启离屏渲染会影响效率;
3、离屏渲染缓存时间有限,超过100ms不被使用则会被丢弃,无法复用;
4、离屏渲染缓存空间有限,最大不能超过屏幕像素的2.5倍,否则无效;

离屏渲染有哪些问题

1、额外存储空间,增加内存开销;
2、OffscreenBuffer 拷贝到FrameBuffer过程需要时间,影响性能;
3、OffscreenBuffer空间有限制限制,最多能缓存屏幕像素的2.5倍;

离屏渲染存在的意义

离屏渲染会给我们带来性能问题,为什么还要用呢?离屏渲染是系统触发,使用离屏渲染主要是以下原因:
1、无法一次性绘制,需要额外缓存中间状态,合成完成之后再进行渲染;
2、OffscreenBuffer可以复用, 在需要重复展示的地方可以达到复用的目的,提高效率。

圆角触发的离屏渲染

设置圆角都会触发离屏渲染吗?

通常如果设置 layer 的 cornerRadius + masksToBounds(对应的UIView的clipsToBounds属性) 会触发离屏渲染, layer.cornerRadius只会设置backgroundColor和border的圆角,不会设置layer.contents的圆角,因此不会触发离屏渲染,除非同时设置了layer的masksToBounds为YES。但是,只要设置这几个属性就会触发离屏渲染吗?通过以下代码我们来试一下:

    UIView *imageView = [[UIView alloc]init];
    imageView.frame = CGRectMake(100, 480, 100, 100);
    [self.view addSubview:imageView];
    imageView.layer.cornerRadius = 50;
    imageView.layer.masksToBounds = YES;
    imageView.layer.borderColor = [UIColor redColor].CGColor;
    imageView.backgroundColor = [UIColor greenColor];
    imageView.layer.borderWidth = 10;

接下来检查一下发现没有触发离屏渲染:

无离屏渲染

前面提到过masksToBounds是设置layer.contents的圆角的。但从上面的代码来看我们并没有设置contents,因此contents为空,masksToBounds设置是无效的。接下来我们设置一下contents的值试一下:

    UIView *imageView = [[UIView alloc]init];
    imageView.frame = CGRectMake(100, 480, 200, 200);
    [self.view addSubview:imageView];
    imageView.layer.cornerRadius = 100;
    imageView.layer.masksToBounds = YES;
    imageView.layer.borderColor = [UIColor redColor].CGColor;
    imageView.backgroundColor = [UIColor greenColor];
    imageView.layer.borderWidth = 10;
    imageView.layer.contents = (__bridge id)[UIImage imageNamed:@"btn.png"].CGImage;

这时候在查看,确实是触发了离屏渲染:


离屏渲染
为什么设置圆角会触发离屏渲染呢?

由下面这张图分析一下圆角触发的离屏渲染的原因:

屏幕快照 2020-07-10 下午4.32.22.png

cornerRadius + masksToBounds 会涉及到三个图层设置圆角并合并的情况。如果不设置圆角的话,那么在渲染的时候他只需要从远到近一个个渲染直接绘制到屏幕上就可以了。但这里因为设置了圆角,那么就涉及到裁剪的问题,子layer要根据父layer的大小进行裁剪,但是由于子layer和父layer不是同时进行渲染的,所以这时候系统就需要开辟额外的缓存区OffscreenBuffer 先暂时吧父layer渲染的结果缓存起来,等子layer渲染完成之后再从OffscreenBuffer 把父layer从OffscreenBuffer取出进行合并然后丢到帧缓存区等待屏幕显示。这就是设置圆角会触发离屏渲染的原因。这根前面提到的触发离屏渲染的原因1是一样的。
根据这个原理,其实不设置contents的值,而是在当前这个视图上加一个带颜色的子视图,也会触发离屏渲染:

    UIView *imageView = [[UIView alloc]init];
    imageView.frame = CGRectMake(100, 480, 200, 200);
    [self.view addSubview:imageView];
    imageView.layer.cornerRadius = 100;
    imageView.layer.masksToBounds = YES;
    imageView.layer.borderColor = [UIColor redColor].CGColor;
    imageView.backgroundColor = [UIColor greenColor];
    imageView.layer.borderWidth = 10;
    UIView *view1 = [[UIView alloc]init];
    view1.frame = CGRectMake(50, 50, 40, 40);
    view1.backgroundColor = [UIColor blueColor];
    [imageView addSubview:view1];

:这里面要说的是圆角设置的几个图层中父layer和自子layer必须有色值,否则也不会触发离屏渲染,因为不需要合成。比如以下情况就不会触发离屏渲染:

  //不会触发离屏渲染
    UIView *imageView = [[UIView alloc]init];
    imageView.frame = CGRectMake(100, 480, 200, 200);
    [self.view addSubview:imageView];
    imageView.layer.cornerRadius = 100;
    imageView.layer.masksToBounds = YES;
    imageView.layer.borderColor = [UIColor redColor].CGColor;
    imageView.backgroundColor = [UIColor greenColor];
    imageView.layer.borderWidth = 10;
    UIView *view1 = [[UIView alloc]init];
    view1.frame = CGRectMake(50, 50, 40, 40);
    view1.backgroundColor = [UIColor clearColor];
    [imageView addSubview:view1];

或者:

//不会触发离屏渲染
    UIView *imageView = [[UIView alloc]init];
    imageView.frame = CGRectMake(100, 480, 200, 200);
    [self.view addSubview:imageView];
    imageView.layer.cornerRadius = 100;
    imageView.layer.masksToBounds = YES;
    imageView.layer.borderColor = [UIColor clearColor].CGColor;
    imageView.backgroundColor = [UIColor clearColor];
    imageView.layer.borderWidth = 10;
    UIView *view1 = [[UIView alloc]init];
    view1.frame = CGRectMake(50, 50, 40, 40);
    view1.backgroundColor = [UIColor greenColor];
    [imageView addSubview:view1];

圆角处理避免离屏渲染的替代方案

离屏渲染会带来一定的性能问题,所以有时候我们要尽量避免,以下是几个避免离屏渲染的方案:
1、贝塞尔曲线UIBezierPath;
2、直接切一个带有圆角的图片;
3、另外还可以参考YYImage的方案等等。

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

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 等)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容