ios开发不懂计算机原理,iOS离屏渲染原理和优化

几乎做iOS开发的人都知道,设置圆角会触发离屏渲染,那么什么情况下设置圆角不会触发离屏渲染呢,为什么会触发离屏渲染。

油画算法

计算机图层的叠加绘制大概遵循油画算法,在这种算法下会按层绘制,首先绘制距离较远的场景,然后用绘制距离较近的场景覆盖较远的部分,如下图。

8cabc2836bcdcab852f70ea1e105696a.png

这样就不会导致远的物体挡住近的物体,但是有个局限,就是无法在后面一层渲染完成后,再回去修改前面图层,因为前面的图层已经被覆盖了

离屏渲染

对于有前后依赖的图层(如全局剪切,阴影等),油画算法无法满足我们的需求,对于有前后依赖的图层,我们可以再另开辟一个空间,用于临时渲染,渲染完成后再渲染到当前的缓冲区上,这个临时渲染,就是离屏渲染,由于需要开辟一个新的内存空间,并且共享同一个上下文,所以还需要做上下文切换(状态切换),并且渲染完成后还要进行拷贝操作

开辟临时缓存空间

上下文切换,上下文对象比较大,切换操作会带来一定的性能消耗

内存拷贝

额外的渲染(没有进一步考证)

上面4项带来的开销会很大,并且每一帧渲染都需要执行,如果屏幕上触发离屏渲染的操作过多,会导致GPU渲染时间过长造成卡顿,应该避免触发离屏渲染

8d043853836dff4bfad6f5cc8835d590.png

iOS圆角问题

官方文档关于layer.cornerRadius的描述

layer-cornerradius.png

layer.cornerRadius只作用backgroundColor和border,不会作用于content,支持动画

离屏渲染是GPU无法按油画算法一次性渲染完我们的视图才会触发,我们先来看几个iOS的例子,模拟器打开Color Off-screen Rendered

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39// 1. UIImageView

let imageView = UIImageView(frame: CGRect(x: 50, y: 100, width: 300, height: 200))

self.view.addSubview(imageView)

imageView.image = UIImage.init(named: "test.jpg")

// image + cornerRadius + masksToBounds 不会触发离屏渲染

imageView.layer.cornerRadius = 10

imageView.layer.masksToBounds = true

// 触发离屏渲染

imageView.backgroundColor = UIColor.green

// 添加一个空的UIView不会触发离屏渲染

// imageView.addSubview(UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10)))

// 2. UIButton

let button = UIButton(type: .custom)

button.frame = CGRect(x: 50, y: 300 + 50, width: 300, height: 50)

self.view.addSubview(button)

button.setTitle("Test", for: .normal)

button.setTitleColor(UIColor.blue, for: .normal)

button.layer.cornerRadius = 10

button.layer.masksToBounds = true

// 触发离屏渲染

button.backgroundColor = UIColor.green

// 触发离屏渲染

button.setBackgroundImage(UIImage(named: "test.jpg"), for: .normal)

// 3. UIView

let view = UIView(frame: CGRect(x: 50, y: 400 + 50, width: 300, height: 50))

self.view.addSubview(view)

view.backgroundColor = UIColor.red

view.layer.cornerRadius = 10

view.layer.masksToBounds = true

// label如果被渲染,则会触发渲染,如果text为空不会被渲染

let label = UILabel(frame: CGRect(x: 10, y: 10, width: 1, height: 1))

label.text = "1"

view.addSubview(label)

2dc98b4a9801a70c7d78c8cff4360109.png

如果设置了cornerRadius+masksToBounds(裁切),并且用于渲染的图层大于1,就会触发离屏渲染,其中如果设置backgroundColor,背景颜色相当于一个单独一个图层,subviews的图层也算,UILabel如果text为空(subviews为空,backgroundColor为空),则不会生成渲染图层

所以设置了cornerRadius+masksToBounds的

UIImageView设置图片不会触发离屏渲染

UIView设置了背景颜色,但不添加subview,不会触发离屏渲染

UILabel设置了文字,并且设置了backgroundColor,会触发离屏渲染

UIButton只设置文字和背景,会触发离屏渲染

优化圆角问题

基于上面的问题,我们可以有几个优化方向

避免使用裁切(masksToBounds)操作,如果我们能确保View里面的内容不会溢出,就可以不用masksToBounds

即使要用到裁切的操作,尽量放到子view里面,不要在上层view使用masksToBounds,因为裁切需要对所有的layer和subview所有图层都进行裁切,这样离屏渲染会需要更大的空间,裁切更多的图层,应该只对必要的view/layer进行裁切

提前切好需要的圆角,避免渲染的时候再切

其他触发离屏渲染的情况使用了遮罩的 layer (layer.mask)

需要进行裁剪的 layer (layer.masksToBounds / view.clipsToBounds)

设置了组透明度为 YES,并且透明度不为 1 的layer (layer.allowsGroupOpacity / layer.opacity)

添加了投影的 layer (layer.shadow),但如果设置了shadowPath,则系统已经知道如何绘制阴影了,不会触发离屏渲染

采用了光栅化的 layer (layer.shouldRasterize),光栅化也可以优化离屏渲染问题

绘制了文字的 layer (UILabel, CATextLayer, CoreText等)

毛玻璃

在iOS系统中,毛玻璃效果应用的非常广泛,从上面分析也可以知道,这个肯定会触发离屏渲染的,图层之间存在依赖,下面是UIBlurEffect的处理过程

12e8b440e41f94ef669b504d589fd073.png

在GPU的渲染过程如下图

76699be30622598fa4ef876dd518b0e3.png

GPU在渲染完Content之后,会另外开辟一个Off-screen buffer,执行下面步骤,最后再做合并处理,最后再拷贝回On-screen buffer上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值