Core Animation Pipeline 渲染流水线
-
Handle Events:
处理触摸事件
,这个过程中有可能会需要改变页面的布局和界面层次。 -
Commit Transaction:此时 app 会通过 CPU 处理显示内容的前置计算,比如布局计算、图片解码等任务,接下来会进行详细的讲解。之后将计算好的图层进行打包发给 Render Server。
-
Decode:打包好的图层被传输到 Render Server 之后,首先会进行解码。注意完成解码之后需要等待下一个 RunLoop 才会执行下一步 Draw Calls。
-
Draw Calls:解码完成后,Core Animation 会调用下层渲染框架(比如 OpenGL 或者 Metal)的方法进行绘制,进而调用到 GPU。
-
Render:这一阶段主要由 GPU 进行渲染。
-
Display:显示阶段,需要等 render 结束的下一个 RunLoop 触发显示。
Commit Transaction 发生了什么
一般开发当中能影响到的就是 Handle Events 和 Commit Transaction 这两个阶段,这也是开发者接触最多的部分。
- Layout:构建视图
这个阶段主要处理视图的构建和布局,遍历的操作[UIView layerSubview]
,[CALayer layoutSubLayers]
- 调用重载的
layoutSubviews
方法 - 创建视图,并通过
addSubview
方法添加子视图 - 计算视图布局,即所有的
Layout Constraint
由于这个阶段是在 CPU
中进行,通常是 CPU 限制或者 IO 限制,所以我们应该尽量高效轻量地操作
,减少这部分的时间,比如减少非必要的视图创建、简化布局计算、减少视图层级等。代码的主要调用结构如下:
- Display:绘制视图
这个阶段主要是交给 Core Graphics 进行视图的绘制,注意不是真正的显示,而是得到前文所说的图元 primitives 数据:
根据UIView和CALayer的关系,我们知道,主要是CALayer来负责一个view的展示,并最终将得到的bitmap
赋值给contents
属性,保存在backing store
中供后续使用。
我们知道view的绘制会在drawRect:
方法中,我们在其中打断点,可得到一下堆栈信息
通过调用堆栈,可以得到此过程主要如下:
- 根据Layout获得的数据,进行展示
- 通过CALayer和UIView之间的
代理
来进行展示,主要实现在的display
方法中
-
CALayer中实现
[self drawInContext:context]:
传入上下文进行绘制 -
UIView中实现
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
:对界面进行绘制并传入上下文 -
UIView中实现
- (void)displayLayer:(CALayer *)layer:
通过UIGraphicsGetImageFromCurrentImageContext
获取到当前上下文的图片,并赋值给layer.contents = (__bridge id)(image.CGImage); -
最后使用
UIGraphicsEndImageContext
关闭上下文
- 以上是默认的流程,如果自己重写实现了
drawRect:
,这个方法会直接调用 Core Graphics绘制方法得到bitmap
数据,同时系统会额外申请一块内存
,用于暂存绘制好的bitmap
。这样绘制过程从GPU
转移到了CPU
,这就导致了一定的效率损失。与此同时,这个过程会额外使用 CPU 和内存,因此需要高效绘制,否则容易造成CPU 卡顿或者内存爆炸
。
- Prepare:Core Animation 额外的工作
- 图片解码和转换
- Commit:打包并发送
- 图层打包并发送到 Render Server
注意: commit 操作是依赖图层树递归执行的,所以如果图层树过于复杂,commit 的开销就会很大。这也是我们希望减少视图层级,从而降低图层树复杂度的原因。
Rendering Pass: Render Server 的具体操作
Render Server
通常是OpenGL
或者是Metal
。以 OpenGL 为例,那么上图主要是 GPU 中执行的操作,具体主要包括:
-
GPU 收到 Command Buffer,包含图元 primitives 信息
-
Tiler 开始工作:先通过顶点着色器 Vertex Shader 对顶点进行处理,更新图元信息
-
平铺过程:平铺生成 tile bucket 的几何图形,这一步会将图元信息转化为像素,之后将结果写入 Parameter Buffer 中
-
Tiler 更新完所有的图元信息,或者 Parameter Buffer 已满,则会开始下一步
-
Renderer 工作:将像素信息进行处理得到 bitmap,之后存入 Render Buffer
-
Render Buffer 中存储有渲染好的 bitmap,供之后的 Display 操作使用
使用 Instrument 的 OpenGL ES,可以对过程进行监控。OpenGL ES tiler utilization 和 OpenGL ES renderer utilization 可以分别监控 Tiler 和 Renderer 的工作情况
动画渲染原理
OS 动画的渲染也是基于上述 Core Animation 流水线完成的。这里我们重点关注 app 与 Render Server
的执行流程。
日常开发中,如果不是特别复杂的动画,一般使用 UIView Animation 实现,iOS 将其处理过程分为如下三部阶段:
Step 1:调用 animationWithDuration:animations: 方法
Step 2:在 Animation Block 中进行 Layout,Display,Prepare,Commit 等步骤。
Step 3:Render Server 根据 Animation 逐帧进行渲染。
网友评论