美文网首页
离屏渲染的探究

离屏渲染的探究

作者: 远方竹叶 | 来源:发表于2020-07-07 18:12 被阅读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)。 所以,在帧缓冲区之前要多了一个离屏渲染缓冲区。

离屏渲染对性能的影响

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

  • Offscreen Buffer 本身就需要额外的空间,大量的离屏渲染可能早能内存的过大压力。Offscreen Buffer 的总大小也有限,不能超过屏幕总像素的 2.5 倍。

  • 离屏渲染时由于 App 需要提前对部分内容进行额外的渲染并保存到 Offscreen Buffer,以及需要在必要时刻对 Offscreen Buffer 和 Framebuffer 进行内容切换,所以会需要更长的处理时间,则会出现掉帧的情况。

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

  • 对于一些特殊效果(一般都是系统自动触发的,比如阴影、圆角、遮罩、光栅化等)需要使用额外的 Offscreen Buffer 来保存渲染的中间状态,所以不得不使用离屏渲染。

  • 处于效率目的,可以将内容提前渲染保存在 Offscreen Buffer 中,达到复用的目的。

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

  • 使用了 mask 的 layer (layer.mask)
  • 需要进行裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
  • 设置了组透明度为 YES,并且透明度不为 1 的 layer (layer.allowsGroupOpacity/layer.opacity)
  • 添加了投影的 layer (layer.shadow*)
  • 采用了光栅化的 layer (layer.shouldRasterize)
  • 绘制了文字的 layer (UILabel, CATextLayer, Core Text 等)

导致离屏渲染的解决办法

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:添加遮罩

  • 再增加一个和背景色相同的遮罩 mask 覆盖在最上层,盖住四个角,营造出圆角的形状。但这种方式难以解决背景色为图片或渐变色的情况。

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

官方说明:

/* 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. */

使用光栅化建议:

  • 如果layer不能被重用,则没必要使用光栅化;

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

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

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

相关文章

  • 离屏渲染的探究

    什么是离屏渲染 作为一个 iOS 开发者,你肯定听说过离屏渲染,什么是离屏渲染呢? 案例 在模拟器上显示一张裁剪的...

  • iOS--离屏渲染

    离屏渲染(Offscreen rendering) 离屏渲染的定义 离屏渲染(offscreen-renderin...

  • 关于离屏渲染

    1.什么是离屏渲染 2.离屏渲染的触发方式 3.离屏渲染的意义 4.离屏渲染的不足 1.什么是离屏渲染 要了解离屏...

  • 图层性能-离屏渲染、光栅化等

    一.离屏渲染 1.在屏渲染、离屏渲染: On-Screen Rendering/在屏渲染:在屏渲染:指的是GPU的...

  • 四、iOS离屏渲染

    一、开启图层是否触发离屏渲染问题 注:离屏渲染的图层会标记为黄色 二、离屏渲染的渲染流程 三、离屏渲染触发的原因 ...

  • iOS下对离屏渲染的理解

    离屏渲染定义 离屏渲染的流程如图: GPU把渲染好的的内容存放到离屏渲染缓冲区中,在离屏渲染缓冲区(Offscre...

  • UI视图及UITableView流畅性优化

    一.离屏渲染 1. 在屏渲染和离屏渲染 在屏渲染是指在当前用于显示的屏幕缓冲区进行GPU渲染操作 离屏渲染是指在当...

  • iOS离屏渲染的原因

    cornerRadius和masksToBounds组合探究离屏渲染 我们可以在模拟器下,开启color Off-...

  • iOS 深入探究离屏渲染

    一、离屏渲染检测 用Xcode创建一个新的工程,然后在AppDelegate.m添加如下代码: 创建一个UIVie...

  • 探究iOS离屏渲染原理

    一、什么是离屏渲染? 在我前面的博客中对图像是如何显示到屏幕上有了详细的解读 传送门,这里在简单回顾下: 主要有以...

网友评论

      本文标题:离屏渲染的探究

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