iOS 离屏渲染

2020-07-10  本文已影响0人  阿木小丸子

1、什么是离屏渲染?

在正常情况下,经过CPU的计算以及GPU的渲染之后,会将结果存放到帧缓存区,随后视图控制器会读取帧缓存区的数据,经过数模转换,再逐行显示到屏幕上。
1.jpeg
并且在GPU渲染的过程中,一般情况下,会遵循‘画家算法’按次序由远及近的一层一层将结果放置到帧缓存区中,当前帧缓存区的数据显示到屏幕上之后,就会将该帧丢弃,周而复始。
@图片来自网络.jpeg
如果我们要对一个多图层的view设置圆角,当我们渲染完图层,要应用一些操作的时候,比如裁剪,之前放置在帧缓存区的数据早已经被丢弃,所以不能同时对多图层进行裁剪操作。
因此,我们需要开辟一些离屏缓存区来存放一些中间状态的数据,等待全部的图层都渲染到离屏缓存区之后,分别从各个离屏缓存区取出数据,分别做相应的操作(裁剪等)之后,组合存入帧缓存区,再等待屏幕控制器的读取和屏幕刷新。(参考HLinzl)
3.jpeg

2、如何检测项目中哪些图层发生了离屏渲染?

我们先看如下的代码:
// 有图片
    UIButton *btn1 = [[UIButton alloc]initWithFrame:CGRectMake(100, 40, 60, 60)];
    [btn1 setImage:[UIImage imageNamed:@"test_icon"] forState:UIControlStateNormal];
    btn1.layer.cornerRadius = 30;
    btn1.clipsToBounds = YES;
    [self.view addSubview:btn1];
    
    // 无图片
    UIButton *btn2 = [[UIButton alloc]initWithFrame:CGRectMake(100, 120, 60, 60)];
    btn2.backgroundColor = [UIColor orangeColor];
    btn2.layer.cornerRadius = 30;
    btn2.clipsToBounds = YES;
    [self.view addSubview:btn2];
    
    // 有背景色 + 图片
    UIImageView *imageV1 = [[UIImageView alloc]initWithFrame:CGRectMake(100, 200, 60, 60)];
    imageV1.backgroundColor = [UIColor blueColor];
    imageV1.image = [UIImage imageNamed:@"test_icon"];
    imageV1.layer.cornerRadius = 30;
    imageV1.layer.masksToBounds = YES;
    [self.view addSubview:imageV1];
    
    // 仅有图片
    UIImageView *imageV2 = [[UIImageView alloc]initWithFrame:CGRectMake(100, 200, 60, 60)];
    imageV2.image = [UIImage imageNamed:@"test_icon"];
    imageV2.layer.cornerRadius = 30;
    imageV2.layer.masksToBounds = YES;
    [self.view addSubview:imageV2];
这写代码是我们平时做项目时会经常用到的,但是这些圆角的设置就一定会发生离屏渲染吗?
接下来,我们在模拟器运行这些代码,打开模拟器的如下设置:
设置.png
看,神奇的现象就出现了!
运行结果.png
上图的第一个和第三个就发生了离屏渲染

3、离屏渲染的利弊:

弊:

(1)离屏渲染需要额外开辟离屏缓冲区的存储空间,加大了系统的负担,会造成性能上的损耗。而存储空间的大小的上限是2.5倍的屏幕像素大小,一旦超过,则无法使用离屏渲染。
(2)一旦因为离屏渲染导致最终存入帧缓存区的时候已经超过了16.67ms,则会出现掉帧的情况。

利:

(1)在我们项目中有一些特殊的效果(比如一些特殊动画效果),需要额外的缓冲区来保存中间状态,不得不使用离屏渲染。
(2)如果某一个效果会多次出现在屏幕上,那么可以提前渲染offscreen Buffer ,来达到复用的目的,这样CPU/GPU就不用做一些重复的计算。

3、离屏渲染的常见触发情况:

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

上一篇下一篇

猜你喜欢

热点阅读