iOS 探索--离屏渲染
2020/7/15 23:09:48
本文主要是介绍iOS 探索--离屏渲染,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
定义
当我们要在屏幕上显示内容, 至少需要一块与屏幕像素数据量一样大的 frame buffer 来作为数据存储区域 (GPU 渲染结果存储的地方)。但是此时出现了特殊情况导致渲染结果无法直接写入 frame buffer, 而是需要先暂存到另外的区域进行处理, 之后再写入到 frame buffer, 这种情况就称之为 离屏渲染。
检测离屏渲染
- 模拟器 可以通过设置 Debug -> Color Off-screen Rendered 来打开离屏渲染检测
- 真机 则通过设置 Debug -> View Debugging -> Rendering -> Color Off-screen Rendered 来打开离屏渲染检测
- 具体情况如上图那样, 图中的颜色呈现 黄色 的区域就是触发了 离屏渲染 的区域
离屏渲染分析
渲染流程分析
接下来我们先一起看 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
中, 这就是为什么需要离屏渲染。
可能不是很好理解, 那么接下来再结合几个案例看一下吧~
注: 以上内容如果有不同观点欢迎指出~
离屏渲染案例
1. cornerRadius + maskToBounds
//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"]; 复制代码
执行结果:
- (图 1) 设置了 圆角+maskToBounds, 然后设置了背景颜色, 发现产生了离屏渲染, 这个就是我们上面所举的例子
- (图 2) 设置了 圆角+maskToBounds, 背景色为透明, 没有发生离屏渲染。这是因为苹果在 iOS 9 之后进行的优化, 假如我们只设置
UIImageView
的image
, 并加上圆角+裁剪,是不会产生离屏渲染的。但如果加上了背景色、边框或其他有图像内容的图层,还是会产生离屏渲染。 - 所以并不是 圆角+maskToBounds 必定会产生离屏渲染
关于 iOS 9 的优化后
可以理解为,因为只有 单层 内容需要添加圆角和裁切,所以可以不需要用到离屏渲染技术。但如果加上了背景色、边框或其他有图像内容的图层,就会产生为 多层 添加圆角和裁切,所以还是会触发离屏渲染。
2. mask.layer
- 系统先计算好mask部分,然后保存到离屏缓冲区
- 计算layer部分,计算好之后保存到离屏缓冲区
- 对mask和layer进行合并剪裁计算,最后结果提交到FrameBuffer,展示到屏幕上
3. 其它会产生离屏渲染的情况
关于离屏渲染还有很多种情况, 这里不一一赘述了, 感兴趣的可以去网上查找一下, 下面在列举几种会出现的情况:
-
layer.shouldRasterize 光栅化
-
edge antialiasing(抗锯齿)
-
半透明视图混合时
离屏渲染的优化
1. 针对圆角、阴影效果等的方案
- 使用带圆角的图片
- 使用贝塞尔曲线进行圆角绘制
- 跟服务端讨论进行圆角处理
- 当不存在短时间内需要反复多次大量复用的layer时,shouldRasterize设置为NO
2. 可复用时的优化方案
CALayer为这个方案提供了对应的解法:shouldRasterize。一旦被设置为true,Render Server就会强制把layer的渲染结果(包括其子layer,以及圆角、阴影、group opacity等等)保存在一块内存中,这样一来在下一帧仍然可以被复用,而不会再次触发离屏渲染。有几个需要注意的点:
- shouldRasterize的主旨在于降低性能损失,但总是至少会触发一次离屏渲染。如果你的layer本来并不复杂,也没有圆角阴影等等,打开这个开关反而会增加一次不必要的离屏渲染
- 离屏渲染缓存有空间上限,最多不超过屏幕总像素的2.5倍大小
- 一旦缓存超过100ms没有被使用,会自动被丢弃
- layer的内容(包括子layer)必须是静态的,因为一旦发生变化(如resize,动画),之前辛苦处理得到的缓存就失效了。如果这件事频繁发生,我们就又回到了“每一帧都需要离屏渲染”的情景,而这正是开发者需要极力避免的。针对这种情况,Xcode提供了“Color Hits Green and Misses Red”的选项,帮助我们查看缓存的使用是否符合预期
- 其实除了解决多次离屏渲染的开销,shouldRasterize在另一个场景中也可以使用:如果layer的子结构非常复杂,渲染一次所需时间较长,同样可以打开这个开关,把layer绘制到一块缓存,然后在接下来复用这个结果,这样就不需要每次都重新绘制整个layer树了
最后
以上就是本次的全部内容, 关于离屏渲染的东西可能不是很全面, 欢迎继续补充。另外, 后续可能还会继续补充一下关于 Core Animation
相关的知识, 感谢~
和谐学习, 不急不躁~
参考资料:
1. iOS离屏渲染
2. 关于iOS离屏渲染的深入研究
3.iOS 关于离屏渲染的理解 以及解决方案
这篇关于iOS 探索--离屏渲染的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值