目录
渲染原理
一、屏幕刷新率是60FPS,且UI在主线程刷新
1秒是1000毫秒,每秒进行60次屏幕刷新,也就是60帧图像,所以每帧图像停留的时间是16.17毫秒
UI为什么要在主线程刷新?
1、UIKit框架不是线程安全的,在不同的线程下分别更新UI会有问题,比如一个线程要把lable显示成功两个字,一个线程要把lable显示失败两个字。
所以需要放在一个线程内,这个线程就是主线程
2、用户的触摸、手势等事件都是在主线程上处理的,如果 UI 操作在其他线程上执行,可能导致用户界面的不同部分出现不同的响应速度,影响用户体验。
xy:比如我对一个view进行了两次操作,一次缩放,一次移动,如果这两次UI更新放在两个线程执行有可能出现先移动后缩放的效果
二、屏幕的刷新是三者配合的共同结果
1、CPU(计算)
视图的创建、布局计算、图片解码、文本绘制等
2、GPU(渲染)
则在物理层上完成了对图像的渲染(顶点着色器、片元着色器等)
数据提交到帧缓冲区
3、垂直同步信号
Frame Buffer、视频控制器等相关部件,将图像显示在屏幕上
在runloop即将休眠时进行UI刷新
update cycle
kCFRunLoopBeforeWaiting = (1UL « 5), // 即将进入休眠时进行UI刷新
此处的UI刷新指的是CPU将计算好的数据提交给GPU,真正的屏幕刷新还是垂直同步信号
渲染流水线
事实上,app 本身并不负责渲染,渲染则是由一个独立的进程负责,即 Render Server 进程。
App 通过 IPC 将渲染任务及相关数据提交给 Render Server。
Render Server 处理完数据后,再传递至 GPU。
最后由 GPU 调用 iOS 的图像设备进行显示。
Core Animation 流水线的详细过程如下:
1、cpu
首先,由 app 处理事件(Handle Events)如:用户的点击操作,在此过程中app可能需要更新视图树,相应地,图层树也会被更新。
其次,app 通过 CPU 完成对显示内容的计算,如:视图的创建、布局计算、图片解码、文本绘制等。
在完成对显示内容的计算之后,app对图层进行打包,并在下一次 RunLoop时将其发送至 Render Server,即完成了一次 Commit Transaction 操作。
2、GPU
Render Server 主要执行 Open GL、Core Graphics相关程序,并调用GPU
GPU则在物理层上完成了对图像的渲染,将数据提交给 Frame Buffer
3、垂直同步信号
视频控制器等相关部件,将图像显示在屏幕上。
对上述步骤进行串联,它们执行所消耗的时间远远超过16.67ms
因此为了满足对屏幕的60FPS刷新率的支持,需要将这些步骤进行分解,通过流水线的方式进行并行执行
如下图所示:
重要总结
[你写的代码]
↓
UIKit 生成 UIView Tree 【CPU】
↓
UIKit 创建并同步生成 CALayer Tree【CPU】
↓
UIKit(Core Animation) 收集 Layer Tree 快照,准备合成信息【CPU】
↓
UIKit(Core Animation) 将快照提交给系统的 Render Server(backboardd)【CPU】
↓
Render Server 利用 Metal 进行图层合成 & 光栅化,将图像写入 CAMetalLayer 的 Framebuffer【GPU】
↓
系统在下一帧 VSync 到来时提交 Framebuffer 到 Display Controller
↓
屏幕刷新
行者常至,为者常成!