2、详解离屏渲染
一、什么是离屏渲染?
顾名思义为屏幕外的渲染,即渲染结果不会直接呈现到当前屏幕上,而是等待合适的时机才会被显示。是一种以空间换取时间的策略。
二、屏幕上显示的数据加载流程
- 正常渲染加载流程
- 离屏渲染加载流程

三、正常渲染流程:
app中数据经过计算和渲染之后,将结果放到帧缓冲区,视频控制器从帧缓冲区读取,并显示到屏幕上。
-
在GPU的渲染流程中,显示到屏幕上的图像遵循油画算法按照由远及近的顺序,依次将结果存储到帧缓冲区。
-
视频控制器从帧缓冲区读取一帧数据,将其显示到屏幕上后,会立即丢弃这帧数据,不会做任何保留,这样做的目的是节省空间,且在屏幕上各自显示各自的,互相不影响。
四、 离屏渲染流程:
当app需要进行额外的渲染和合并时,例如对复杂视图设置圆角和裁剪,我们需要把所有图层都进行圆角+裁剪,然后将合并后的结果存入帧缓冲区,再从帧缓冲区中取出交给屏幕去显示。这时在正常的渲染流程中,我们是无法做到对所有图层进行圆角裁剪的,因为它是用一个丢一个。
我们需要提前将处理好的结果放入离屏缓冲区,然后将几个图层进行合并叠加存入到帧缓冲区,最后再由视频控制器读取帧缓冲区数据显示到屏幕上。
五、 离屏渲染的代价
-
离屏渲染需要在屏幕外开辟额外的内存空间,提前使用GPU渲染复杂的视图,保证视频控制器能够及时的从缓冲区读到新的渲染结果。
-
视频控制器要读取离屏缓冲区的结果,需要把渲染上下文从当前帧缓冲工区切换到离屏缓冲区,当显示非离屏渲染视图的时候又要切换回来,然而不可能在一屏上所有的元素都是离屏渲染的,所以视屏控制器上下文需要不停第来回切换,而这种上下文切换的代价是非常昂贵的。所以离屏渲染会带来各方面的开销,要尽可能的避免。
六、常见的几种触发离屏渲染的情况
-
使用了mask的layer (layer.mask),mask作为遮罩,显示在其所在的大layer以及大layer的所有子sublayer之上,masklayer可能也会带透明度、形状等。
15944467659123.jpg
我们需要在离屏缓冲区内完成Image和Mask的裁剪合并处理,才能将最终的Masked Image转存到帧缓冲区,再到显示。
-
需要进行裁剪的layer (layer.maskToBounds /view.clipsToBounds)
-
设置了组透明度为yes,并且透明度不为1的layer (layer.allowGroupsOpacity/layer.opacity)
15944469118351.jpg
有很多子sublayer,当我们对大的layer设置alpha时,会首先在离屏缓冲区等待整个layer里面的sublayer全部完成之后,再根据组透明度opacity计算新的颜色,再和下面的layer颜色整合,才会给帧缓冲区等待显示。所以并不是每渲染一层sublayer就立马给显示。如果opacity为1,则不需要调整透明度,正常画家算法显示。
-
添加了投影的layer (layer.shadow*)
开启阴影效果,shadow是一个背景色,是layer的背景,在layer的下面,shadow是根据layer而来,所以要先知道layer才能知道shadow的大小位置。如果没有离屏渲染,按照油画算法,必须先将shadow放入帧缓冲区,先显示。但是layer没有,不可能先渲染出shadow。只能利用离屏渲染缓冲区,等待shadow,layer等渲染合并完成之后,再送入帧缓冲区去显示。 -
采用了光栅化的layer (layer.shouldRasterize)
-
绘制了文字的layer (UIlabel,CATextLayer,Core Text等)