离屏渲染的探究

2020-07-07  本文已影响0人  远方竹叶

什么是离屏渲染

作为一个 iOS 开发者,你肯定听说过离屏渲染,什么是离屏渲染呢?

案例

在模拟器上显示一张裁剪的圆角图片

UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:CGRectMake(107, 100, 200, 200)];
bgImageView.backgroundColor = [UIColor whiteColor];
bgImageView.image = [UIImage imageNamed:@"qh"];

bgImageView.layer.cornerRadius = 100.0f;
bgImageView.layer.masksToBounds = YES;
[self.view addSubview:bgImageView];

开启模拟器 debug 模式下的离屏渲染

运行模拟器,会发现此时的 imageView 变成了黄色,说明出现了离屏渲染。

修改一下代码,去掉 backgroundColor,

// bgImageView.backgroundColor = [UIColor whiteColor];
bgImageView.image = [UIImage imageNamed:@"qh"];

bgImageView.layer.cornerRadius = 100.0f;
bgImageView.layer.masksToBounds = YES;

运行效果图

黄色部分消失,说明设置了 cornerRadius + masksToBounds 不一定会导致离屏渲染。

离屏渲染过程中,渲染显示发生了什么?

正常情况下,App 通过 CPU 和 GPU 的合作,不停的将内容渲染完成放入 Frame Buffer 帧缓冲器中,而显示屏幕不断的从 Frame Buffer 中获取内容,显示实时的内容。

离屏渲染的流程:先额外创建离屏渲染缓冲区 Offscreen Buffer,将提前渲染好的内容放入 Offscreen Buffer,等到合适的时机再将 Offscreen Buffer 中的内容进一步的叠加、渲染、完成后将结果切换到 Frame Buffer 中,按照正常的模式继续下去。

为什么在帧缓存区之前多了离屏渲染?

如果渲染的画面比较复杂,如 UIImageView 的背景色 backgroundColor 需要裁剪渲染,image 也需要裁剪渲染,正常情况下,根据画家算法(由远及近的显示),先显示远处的背景,再显示近处的背景:

backgroundColor 渲染之后的位图先进入 帧缓冲区->屏幕,帧缓冲区的backgroundColor 清空;
image 的位图再进入 帧缓冲区->屏幕 ,帧缓冲区的 image 被清空;
再对 image 进行裁剪时,因为帧缓冲区已经被清空了,已经没有东西裁剪了。

因此,需要额外开辟一块缓冲区,等待合成、裁剪完成后-->帧缓冲区-->屏幕显示。 那么,这个额外的处理复杂渲染数据的地方就是 离屏渲染缓冲区(Offscreen Buffer)。 所以,在帧缓冲区之前要多了一个离屏渲染缓冲区。

离屏渲染对性能的影响

离屏渲染会加大系统的负担,会造成性能上的损耗,主要表现在:

离屏渲染的开销很大,为什么还要使用离屏渲染呢?

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

导致离屏渲染的解决办法

iOS 9.0 之前 UIImageView 跟 UIButton 设置圆角都会触发离屏渲染。

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

圆角优化

我们设置圆角一般通过如下方式:

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(107, 100, 200, 200)];
imageView.image = [UIImage imageNamed:@"qh"];
imageView.layer.cornerRadius = CGFloat(10);
imageView.layer.masksToBounds = YES;
[self.view addSubview:imageView];

这样处理的渲染机制是GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会给我们带来额外的性能损耗,如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,性能的代价会宏观地表现在用户体验上——掉帧。

优化方案1:使用贝塞尔曲线 UIBezierPath 和 Core Graphics 框架画出一个圆角

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(107, 100, 200, 200)];
imageView.image = [UIImage imageNamed:@"qh"];

//开始对 imageView 进行画图
UIGraphicsBeginImageContextWithOptions(imageView.frame.size, NO, [UIScreen mainScreen].scale);
    
//使用贝塞尔曲线画出一个圆形图
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width / 2] addClip];

[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    
//结束画图
UIGraphicsEndImageContext();
[self.view addSubview:imageView];

优化方案2:使用 CAShapeLayer 和 UIBezierPath 设置圆角

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(107, 100, 200, 200)];
imageView.image = [UIImage imageNamed:@"qh"];

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];

⚠️CAShapeLayer 需要贝塞尔曲线配合使用才有意义(才有效果),CAShapeLayer 继承于 CALayer,可以使用 CALayer 的所有属性值。CAShapeLayer 动画渲染直接提交到手机的 GPU 当中,相较于 view 的drawRect 方法使用 CPU 渲染而言,其效率极高,能大大优化内存使用情况。

优化方案3:使用带圆角的图片

优化方案4:添加遮罩

离屏渲染的另外一个原因 - 光栅化

官方说明:

/* When true, the layer is rendered as a bitmap in its local coordinate
 * space ("rasterized"), then the bitmap is composited into the
 * destination (with the minificationFilter and magnificationFilter
 * properties of the layer applied if the bitmap needs scaling).
 * Rasterization occurs after the layer's filters and shadow effects
 * are applied, but before the opacity modulation. As an implementation
 * detail the rendering engine may attempt to cache and reuse the
 * bitmap from one frame to the next. (Whether it does or not will have
 * no affect on the rendered output.)
 *
 * When false the layer is composited directly into the destination
 * whenever possible (however, certain features of the compositing
 * model may force rasterization, e.g. adding filters).
 *
 * Defaults to NO. Animatable. */

使用光栅化建议:

上一篇 下一篇

猜你喜欢

热点阅读