iOS 离屏渲染

2020-07-09  本文已影响0人  windy_3c22

1、离屏渲染

1.1什么是离屏渲染

在正常情况下,经过CPU的计算以及GPU的渲染之后,会将结果存放到帧缓存区,随后视频控制器会读取帧缓存区的数据,经过数模转换,再逐行显示到屏幕上。在GPU渲染的过程中,一般情况下,会遵循‘画家算法’按次序由远及近的一层一层将结果放置到帧缓存区中,当当前帧缓存区的数据显示到屏幕上之后,就会将该帧丢弃,周而复始。(如下图)

画家算法--由远及近

但是某些特殊情况下(例:一个多图层的view设置圆角),当我们一层一层的渲染完图层后,要应用一些操作(例:裁剪),但是之前放置在帧缓存区的已渲染完的数据早已经被丢弃,这时是不可能对已经丢弃的图层进行操作了。
因此,我们需要开辟些离屏缓存区来存放一些中间状态的数据,等待全部的图层都渲染到离屏缓存区之后,再分别从各离屏缓存区取出数据,分别做相应的操作(裁剪)后,组合存入帧缓存区,再等待屏幕控制器的读取和屏幕刷新。就是离屏渲染。

1.2离屏渲染触发原理

app进行额外的渲染和合并-->需额外开辟offsecrren buffer空间 --> frame buffer --> 屏幕(如下图)

离屏渲染流程.png

例1、2离屏渲染图/正常渲染图

正常渲染

当sublayer绘制到屏幕上之后,就会将sublayer从帧缓存区移除,从而节省空间

离屏渲染图
1.3离屏渲染的优劣
1.4 常见的几种触发离屏渲染的情况

光栅化--离屏渲染
光栅化是将一个layer预先渲染成位图(bitmap),然后加入缓存中。对于阴影效果比消耗资源对静态内容进行缓存,可提升一定幅度的性能。
使用时注意事项:

1.4 iOS常见的圆角导致的离屏渲染的处理方法

方案1

_imageView.clipsToBounds=YES;
_imageView.layer.cornerRadius=4.0;

方案2

方案2
方案3
方案3
方案4
方案4

2、圆角离屏渲染实例

圆角cornerRadius解释

Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to true causes the content to be clipped to the rounded corners

设置cornerRadius>0,只为layer的backgroundColor和border设置圆角;而不会对layer的contents设置圆角,除非同时设置了layer.masksToBounds为true(对应UIView的clipsToBounds属性)。
验证官方解释
2.1 只设置cornerRadius>0,不设置ClipsBounds和layer.masksToBounds 结果:如图1

 UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
 view1.backgroundColor = [UIColor cyanColor];
 view1.layer.cornerRadius = 20;
 view1.layer.borderColor = [UIColor blackColor].CGColor;
 view1.layer.borderWidth = 1.0;
 [self.view addSubview:view1];
图1
结果图显示backgroundcolor和border都显示圆角,并且无触发离屏渲染。
设置clipsToBounds或layer.masksToBounds 为true。同样无触发离屏渲染。 结果图:图1。
2.2 为view1添加子view2结果(如图2)或者view1的contents添加内容结果(如图3)。只设置cornerRadius。根据结果图显示子view2和contents内容都未显示成圆角。未触发离屏渲染
//**********view1的contents添加内容
UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
view1.backgroundColor = [UIColor cyanColor];
 view1.layer.cornerRadius = 20;
 view1.layer.borderColor = [UIColor blackColor].CGColor;
 view1.layer.borderWidth = 1.0;
view1.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"123"].CGImage);
//view1.clipsToBounds = YES;
[self.view addSubview:view1];
//**********view1添加子view2
 UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
 view1.backgroundColor = [UIColor cyanColor];
 view1.layer.cornerRadius = 20;
 view1.layer.borderColor = [UIColor blackColor].CGColor;
 view1.layer.borderWidth = 1.0;
 [self.view addSubview:view1];
 UIView *view2 = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
 view2.backgroundColor = [UIColor purpleColor];
 [view1 addSubview:view2];

设置clipsToBounds或layer.masksToBounds为true。添加子view结果(结果图4),contents结果(图5)。结果图显示设置裁剪后无论添加的子view2或者contents内容都显示成圆角了。并且触发离屏渲染。



从上面例子可以看出设置圆角、裁剪时触发离屏渲染的例子。单独设置背景或一个图层的圆角时。子view或着contents是没有圆角的,也没有触发离屏渲染。所以获取每一图层单独渲染即可。
同时设置裁剪时,只有单一图层或着背景时,同样也没有触发离屏渲染。获取相应的图层直接渲染即可。当存在其他子view或着contents时。需要其中的子view或contents都显示成圆角(子view/contents 因为背景view的裁剪属性需组合)。触发了离屏渲染。
因此圆角触发离屏渲染只需多个图层都需要显示成圆角并且组合之后显示才行。
第二个例子中在view1中添加子view2。但是不设置裁剪。分别设置view1和view2的cornerRadius值相同。也可达到我们想要的圆角效果。但是结果却是未触发离屏渲染。这是因为view1和view2的两个图层不需要进行组合。

2.3圆角离屏渲染触发
图层的叠加绘制大概遵循“画家算法/油画算法”(由远及近,即先绘制场景中距离观察者较远的物体,再绘制较近的物体)。
例:图7, 先绘制红色图形,在绘制黄色图形,在绘制灰色图形。

图7
当我们设置了cornerRadius以及masksToBounds进行圆角+裁剪时,masksToBounds裁剪属性会应用到所有的图层上。
裁剪图层
需各图层在offsecreen(离屏缓存区)保存,等待圆角、裁剪处理完成后在片元着色器组合 ->进入帧缓存区->渲染显示(1.2中离屏渲染流程图)。
上一篇下一篇

猜你喜欢

热点阅读