当我们要在屏幕上显示内容, 至少需要一块与屏幕像素数据量一样大的 frame buffer 来作为数据存储区域 (GPU 渲染结果存储的地方)。但是此时出现了特殊情况导致渲染结果无法直接写入 frame buffer, 而是需要先暂存到另外的区域进行处理, 之后再写入到 frame buffer, 这种情况就称之为 离屏渲染。
接下来我们先一起看 2张 图来看一下整个的 渲染流程:
但是对于一些较复杂的动画的渲染并不能直接渲染叠加显示, 而是需要根据 Command Buffer
分通道进行渲染之后再组合。(图 2)
对比上面的 2 个流程图, 可以看到后面的渲染需要更多的渲染通道和合并的步骤。
相比之下 Offscreen Render 需要更多的渲染通道, 且多个渲染通道间切换肯定会消耗一定的时间, 当通道达到一定的数量, 对性能必然造成影响
正常的情况下, OpenGL 提交一个命令到 Command Buffer
, 随后 GPU 就开始渲染, 最后将渲染结果放到 Render Buffer
中。
这里如果想要绘制一个带有圆角并剪切圆角的容器 (maskToBounds 为 YES, 背景色不是透明, 具体可以看下面的例子 1), 就可能触发离屏渲染。
- 首先将 layer 的内容裁剪成圆角
- 容器的子控件在渲染的过程中, 因为父 layer 是被裁剪过的, 那么也需要被裁剪; 但是这时的 父 layer 已经被渲染完成而子 layer 还在队列中, 没有办法进行统一裁剪, 所以这个过程就没办法实现了
所以系统就不得不去 开辟独立于 frame buffer
的内存, 先把父 layer 以及他的子 layer依次画好, 然后合并到一起进行裁剪, 再把结果放到 frame buffer
中, 这就是为什么需要离屏渲染。
可能不是很好理解, 那么接下来再结合几个案例看一下吧~
注: 以上内容如果有不同观点欢迎指出~
//UIImageView 设置了图片+背景色; UIImageView *img1 = [[UIImageView alloc]init]; img1.frame = CGRectMake(100, 320, 100, 100); img1.backgroundColor = [UIColor blueColor]; [self.view addSubview:img1]; img1.layer.cornerRadius = 50; img1.layer.masksToBounds = YES; img1.image = [UIImage imageNamed:@"btn.jpeg"]; //UIImageView 只设置了图片,无背景色; UIImageView *img2 = [[UIImageView alloc]init]; img2.frame = CGRectMake(100, 480, 100, 100); [self.view addSubview:img2]; img2.layer.cornerRadius = 50; img2.layer.masksToBounds = YES; img2.image = [UIImage imageNamed:@"btn.jpeg"]; 复制代码
执行结果:
UIImageView
的image
, 并加上圆角+裁剪,是不会产生离屏渲染的。但如果加上了背景色、边框或其他有图像内容的图层,还是会产生离屏渲染。关于 iOS 9 的优化后
可以理解为,因为只有 单层 内容需要添加圆角和裁切,所以可以不需要用到离屏渲染技术。但如果加上了背景色、边框或其他有图像内容的图层,就会产生为 多层 添加圆角和裁切,所以还是会触发离屏渲染。
- 系统先计算好mask部分,然后保存到离屏缓冲区
- 计算layer部分,计算好之后保存到离屏缓冲区
- 对mask和layer进行合并剪裁计算,最后结果提交到FrameBuffer,展示到屏幕上
关于离屏渲染还有很多种情况, 这里不一一赘述了, 感兴趣的可以去网上查找一下, 下面在列举几种会出现的情况:
layer.shouldRasterize 光栅化
edge antialiasing(抗锯齿)
半透明视图混合时
1. 针对圆角、阴影效果等的方案
- 使用带圆角的图片
- 使用贝塞尔曲线进行圆角绘制
- 跟服务端讨论进行圆角处理
- 当不存在短时间内需要反复多次大量复用的layer时,shouldRasterize设置为NO
2. 可复用时的优化方案
CALayer为这个方案提供了对应的解法:shouldRasterize。一旦被设置为true,Render Server就会强制把layer的渲染结果(包括其子layer,以及圆角、阴影、group opacity等等)保存在一块内存中,这样一来在下一帧仍然可以被复用,而不会再次触发离屏渲染。有几个需要注意的点:
以上就是本次的全部内容, 关于离屏渲染的东西可能不是很全面, 欢迎继续补充。另外, 后续可能还会继续补充一下关于 Core Animation
相关的知识, 感谢~
和谐学习, 不急不躁~
参考资料:
1. iOS离屏渲染
2. 关于iOS离屏渲染的深入研究
3.iOS 关于离屏渲染的理解 以及解决方案