在iOS开发中到底什么是离屏渲染?
离屏渲染有什么性能问题?
离屏渲染是否应该完全禁止呢?
一、iOS渲染流程梳理
iOS开发中,将图像显示到屏幕上有两种方式:
1、正常渲染流程
2、离屏渲染流程
二、离屏渲染的性能问题
2.1 离屏渲染存在的性能问题
1、相比于正常的渲染流程,离屏渲染需要额外创建一个缓冲区,需要多耗费一些空间;
2、触发离屏渲染后,需要先从 Frame Buffer 切换到 Off-Screen Buffer ,渲染完毕后再切换回 Frame Buffer ,这一过程需是比较耗费性能的,因为要来回切换上下文;
3、数据由 Off-Screen Buffer 取出,再存入 Frame Buffer 也需要耗费时间,这样增加了掉帧的可能性;
4、 离屏缓冲区 存在空间限制,即屏幕像素的2.5倍,当大于这一值时便不会触发离屏渲染。
2.2 为何要使用离屏渲染
既然离屏渲染存在这么多的性能问题,为什么依然存在呢?
主要有以下两点原因:
1、有些后续经常用到的图层数据,可以先缓存在离屏缓存,用到时直接复用。
2、存在一些特殊效果,正常流程无法完成,必须使用离屏渲染,比如圆角、阴影和遮罩、高斯模糊、半透明图层混合等正常的渲染流程采用油画算法由远及近的渲染图层,当一个图层显示到屏幕上后,帧缓冲区会立即删除这一图层的数据。
例如将这张图显示到屏幕上可以分为两步:
1、先绘制黄色背景图层,显示到屏幕上后,删除帧缓冲区中黄色图层的数据。
2、再渲染蓝色图层,显示蓝色图层到屏幕后,删除帧缓冲区中蓝色图层数据,如果给图层设置了特殊效果则有可能需要触发离屏渲染,以圆角为例。
我们想要是如右图所示的效果,设置圆角后包括子视图也进行圆角裁剪。
但是按照正常流程显示完黄色图层后,在渲染蓝色图层进行圆角设置时(超出时按圆角裁剪,未超出则不需要裁剪),已经找不到黄色图层的数据。
因此,需要增加离屏缓冲区,将后续要用到的图层数据先缓存起来,在后续用到时进行渲染显示。
三、离屏渲染的触发及检测
3.1 离屏渲染检测
1、模拟器下检测:Simulator –> Debug –> Color Off-screen rendered,模拟器下只需要设置模拟器一次就可以
2、真机下检测:XCode –> Debug –> View Debugging –>Rendering –> Color Offscreen-Rendered Yellow,真机下每次运行后都要在XCode中设置一下生效。
检测结果如果覆盖有黄色图层,则表示产生了离屏渲染,否则没有产生离屏渲染
3.2 离屏渲染触发及建议
1、如上文所述,实现一些特殊效果例如圆角、阴影和遮罩、高斯模糊、半透明图层混合等。
2、设置view.layer.shouldRasterize 为 true时,会触发离屏渲染shouldRasterize 光栅化使用目的:通过开辟离屏缓冲区缓存图像,以便将来使用,提升性能。但是如果缓存的图像会经常被更改,则开启离屏缓存区反而会降低性能。
因此对于是否开启 shouldRasterize 有以下建议:
- 如果缓存的图像在之后用不到或很少用到( 100ms内用不到 ),则不需要开启shouldRasterize
- 如果缓存的图像会经常发生变动,比如本身处于动画中,或者像tabeleView的cell的上图片可能经常改变,则不要开启shouldRasterize
- 缓存的图像过大,超过屏幕像素的 2.5 倍,不会触发离屏渲染,所以开启shouldRasterize也没有效果
四、iOS设置圆角触发离屏渲染原因
我们以UIButton和 UIImageView为例:
结果显示1和3触发了离屏渲染,2和4未触发离屏渲染。为什么会这样呢?
我们先看一下 cornerRadius 和 masksToBounds 这几个属性。
cornerRadius用于设置圆角半径
masksToBounds设置超出部分裁剪,仅设置cornerRadius不会对内容进行圆角处理,只有设置 masksToBounds=YES才会对内容进行圆角处理
从运行结果可以看出,虽然设置了 cornerRadius 和 masksToBounds = YES , bt2并没有会触发离屏渲染,可见不是设置了 masksToBounds = YES 就一定会离屏渲染。
因为 bt2 只设置了一个背景颜色,只有一个背景图层,直接将这一层渲染到屏幕上就可以了,不需要开辟离屏缓冲区。而 bt1 设置了一个背景图片,会有一个背景图层和内容图层,所以需要离屏渲染(如果去掉图片,设置title,则title长度超出时,会离屏渲染,title未超出则不会触发)。
在 3 和 4中, img1 设置了 图片 + 背景颜色,会有两个图层则产生离屏渲染, img2 只设置背景,没有图片,则不会离屏渲染。
由此可见,设置圆角触发离屏渲染的条件是 contents 有子视图,并设置了 masksToBounds = YES 。这是一个与的关系,两者必须都满足。
总结
1、iOS图形渲染流程分为 正常渲染流程 和 离屏渲染流程 ;
2、离屏渲染是在帧缓冲区之外开辟了一个临时的缓冲区,用于保存一些暂时没有用到的数据,之后会从离屏缓冲区取出,渲染后再放入帧缓冲区;
3、离屏渲染会有一定的性能问题,但是我们依然会有使用到的地方;
4、离屏缓冲区最大为屏幕像素的2.5倍,超出不会触发离屏渲染;
5、设置圆角不一定会触发离屏渲染,但是如果有多个图层,则会触发离屏渲染。