iOS 高级核心动画 day09 - 性能调优:CPU、GPU、离屏渲染

一、CPU VS GPU

1. 对于计算机硬件,绘图和动画有哪两种处理方式?为什么处理图像尽量使用 GPU?

  • CPU:中央处理器
  • GPU:图形处理器
  • 总体来说,我们可以用软件(CPU)做任何事情,但是对于图像处理,通常用硬件(GPU)会更快,因为 GPU 使用图像对高度和浮点运算做了优化

2. 就 CPU 和 GPU 而言,我们所说的动画性能优化,其实是让这两者怎么配合?

  • 大多数动画性能优化都是关于合理利用 GPU 和 CPU,使得它们都不会超出负荷。
  • 于是我们首先需要知道 Core Animation 是如何在这两个处理器之间分配工作的。

3. Core Animation 处在 iOS 的核心地位:应用内和应用间都会用到它,那对于 iOS 它处于什么层级呢?

  • 一个简单的动画可能同步显示多个 app 的内容,例如当在 iPad 上多个程序之间使用手势切换,会使得多个程序同时显示在屏幕上。
  • 在一个特定的应用程序中用代码实现它是没有意义的,因为在 iOS 中不可能实现这种效果(APP 都是被沙箱管理,不能访问别的视图)。
  • 动画和屏幕上组合的图层实际上被一个单独的进程管理,而不是你的应用程序。这个进制就是所谓的渲染服务
  • 在 iOS5 和之前的版本这个渲染服务叫做SpringBoard 进程(同时管理着 iOS 的主屏)。在 iOS6 之后的版本叫做 BackBoard

4. 一个动画的展示过程?

在应用程序内,当运行一段动画时候,这个过程会被分离成如下四个阶段

  • 布局 - 这是准备你的视图/图层的层级关系,以及设置图层属性(位置,背景色,边框等等)的阶段。
  • 显示 - 这个图层的寄宿图片被绘制的阶段。绘制可能涉及你的 -drawRect:-drawLayer:inContext: 方法的调用路径。
  • 准备 - 这是 Core Animation 准备发送动画数据到渲染服务的阶段。这同时也是 Core Animation将要执行一些别的事务例如解码动画过程。
  • 提交 - 这是最后的阶段,Core Animation 打包所有图层和动画属性,然后通过 IPC(进程间通信)发送到渲染服务进行显示。

但是上面四个阶段仅仅发生在你的应用程序之内,在动画在屏幕上显示之前仍然有更多的工作。一旦打包的图层和动画到达渲染服务进程,它们会被反序列化来形成另一个叫做渲染树的图层树。使用这个树状结构,渲染服务对动画的每一帧做出如下工作:

  • ⑤ 对所有的图层属性计算中间值,设置 OpenGL 几何形状(纹理化的三角形)来执行渲染
  • ⑥ 在屏幕上渲染可见的三角形

所以一共有六个阶段;最后两个阶段在动画过程中不停地重复。

5. 上面六个阶段,哪些在 CPU?哪些在 GPU?哪些阶段开发者能介入?

  • 前五个阶段都在软件层面处理(通过 CPU),只有最后一个被 GPU 执行。
  • 而且,你真正只能控制前面两个阶段:布局和显示。Core Animation 框架在内部处理剩下的事务,你也控制不了它。
  • 这个并不是个问题,因为在布局和显示阶段,你可以决定哪些由 CPU 执行,哪些交给 GPU 去做。那么该如何判断呢?

6. GPU 主要干些什么事情?

  • GPU 为一个具体的任务做了优化:它用来采集图片和形状(三角形),运行变换,应用纹理和混合然后把它们输送到屏幕上。

7. 有一些事情会降低(基于 GPU)图层绘制(至少说两点)?

  • 重绘 - 主要由重叠的半透明图层引起。GPU 的填充比例(用颜色填充像素的比率)是有限的,所以需要避免重绘(每一帧用相同的像素填充多次)的发生。
  • 离屏绘制 - 这发生在当不能直接在屏幕上绘制,并且必须回到离屏图片的上下文的时候。
  • 过大的图片 - 如果视图超过 GPU 支持的2048x2048或者4096x4096尺寸的纹理,就必须要用CPU在图层每次显示之前对图片预处理,

8. 哪些 CPU 的操作会延迟动画的开始时间,从而造成卡顿(至少说两点)?

  • 布局计算 - 如果你的视图层级过于复杂,当视图呈现或者修改的时候,计算图层帧率就会消耗一部分时间。
  • 大量视图懒加载 - iOS只会当视图控制器的视图显示到屏幕上时才会加载它,但是同一瞬间太多视图需要加载了。
  • 解压图片 - PNG或者JPEG压缩之后的图片文件会比同质量的位图小得多。但是在图片绘制到屏幕上之前,必须把它扩展成完整的未解压的尺寸(通常等同于图片宽 x 长 x 4个字节)。

二、离屏渲染

1. 如何通过iOS模拟器判断一个视图,是否发生了离屏渲染?

  • 开启 iOS 模拟器的下面这个选项,如果视图呈换色,就表明发生了离屏渲染
image.png

2. 为什么有些操作会发生离屏渲染?

  • 图层的叠加绘制大概遵循“画家算法”
  • 画家算法:先绘制场景中离观察者较远的物体,再绘制较近的物体。

先绘制红色部分,再绘制⻩色部分,最后再绘制灰⾊部分,即可解决隐藏面消除的问题。即将场景按照物理距离和观察者的距离远近排序,由远及近的绘制即可。

image.png

当我们设置了cornerRadius以及masksToBounds进行圆角+裁剪时,masksToBounds裁剪属性会应用到所有的图层上。

image.png

本来我们从后往前绘制,绘制完一个图层就可以丢弃了。但现在需要依次在 Offscreen Buffer中保存,等待圆角+裁剪处理,即引发了 离屏渲染 。

3. 为什么离屏渲染会降低效率?

  • 要创建离屏缓冲区
  • 要进行缓冲区上下文的切换
  • 需要离屏渲染的计算通常比较复杂(不符合画家算法)
渲染结果先经过了离屏buffer,再到frame buffer

4. 同时设置 cornerRadiusmasksToBounds,也就是圆角 + 裁剪,一定会触发离屏渲染吗?

  • 不一定
  • 如果仅仅裁剪了一个背景颜色或者一个图片,不会触发离屏渲染(其实就是符合画家算法,不需要另起离屏缓冲区)

5. 小技巧:对于只有一张图片的 UIButton,如何设置圆角而不触发离屏渲染?

  • 下面这种做法不会触发离屏渲染
// 下面这种做法不会触发离屏渲染
btn.imageView.clipsToBounds = YES;
btn.imageView.layer.cornerRadius = 30;
  • 下面这种做法触发离屏渲染
// 下面这种做法会触发离屏渲染
btn.clipsToBounds = YES;
btn.layer.cornerRadius = 30;

参考好文章:
https://juejin.cn/post/6846687603316490254

https://medium.com/@jasonyuh/%E5%85%B3%E4%BA%8E%E7%A6%BB%E5%B1%8F%E6%B8%B2%E6%9F%93%E7%9A%84%E6%B7%B1%E5%85%A5%E7%A0%94%E7%A9%B6-e776f56b3e60

你可能感兴趣的:(iOS 高级核心动画 day09 - 性能调优:CPU、GPU、离屏渲染)