App图形图像的渲染流程
我们在使用UIVIew,Image等组件的时候,是怎么一步步渲染到屏幕上的呢?动画又是怎么实现的?
这里我们先了解几个概念:
1.CPU:CPU主要负责的是逻辑运算,如图像的布局,文本的计算和排版,图片格式的转换和解码,图像的绘制等。
2.GPU:GPU主要负责图片渲染的运算,它可以把CPU计算好的显示内容进行纹理渲染,然后把渲染后的内容放入Frame Buffer(帧缓存区)。
注意:GPU相比通用计算的CPU来讲,其有两个优势:
- 处理图片计算的能力要强的多,因为硬件支持T&L(Transform and Lighting,多边形转换与光源处理)
- 并发能力比CPU也要大 详细对比如下:
3.帧缓存区(双缓冲机制):GPU渲染后的内容会被放到当前屏幕的帧缓存区,不需要额外开辟空间,我们知道 iPhone 的屏幕刷新率是 60Hz,也就是刷新一帧的时间是 16.67 ms, 每隔这段时间视频控制器就会去读一次缓存区的内容来显示。 如果GPU性能出现问题,无法在这段时间将更新渲染结果到帧缓存区,那么下一个时间段还是会呈现上一帧的画面,这就是掉帧。
4.视频控制器:视频控制器会读取帧缓冲区中的内容,GPU在开始时会绘制后缓存区中的内容,然后视频控制器读取完内容后,就会去读取后缓存区的内容,然后GPU再绘制前缓存区的内容。也就是说对前后缓存区的读取是交替进行。
其基本渲染流程如下:
GPU中的数据从哪里来?
对于iOS的UIKit,答案就是OpenGL和Metal
OpenGL是苹果最初的采用的渲染框架,其具有跨平台、性能好,包大小可控等优点,但是随着图形技术的发展,其他也暴露出很多问题:
- 现代 GPU 的渲染管线已经发生变化。
- 不支持多线程操作。
- 不支持异步处理。
因此苹果重新设计了Metal框架,其优势如下:
- 更高效的 GPU 交互,更低的 CPU 负荷。
- 支持多线程操作,以及线程间资源共享能力。
- 支持资源和同步的控制。
在UIKit中,OpenGL的数据主要从以下三个库获得:
- Core Graphics : 基于Quartz的绘图引擎,用于运行时绘制图像
- Core Image : 处理图像
- Core Animation : 核心动画和图层渲染能力
那么我们可以知道iOS平台的渲染框架如下:
这时候你可能会有疑问:我们实际开发中好像并没有直接调用Core Graphics, Core Animation这些框架,而是使用UIIMageView,UIView这些类,那么UIKit框架又是怎么调用Core框架来渲染的呢?
UIKit实现
在介绍UIKit是怎么一步步渲染之前,得先了解UIView和CALayer的区别
UIVIew和CALayer
UIVIew和CALayer体现出了职责分离的思想,其中,UIView负责触摸事件的处理(继承自UIResponder ),CALayer负责渲染层(继承自NSObject)。之所以这样设计,是因为Mac系统使用的是AppKit,但是无论是UIKit还是AppKit,底层渲染都是使用CoreAnimation。
在创建UIVIew时,系统会创建一个layer绑定在View的属性上,当我们修改View的圆角、边框等属性时,其实是UIVIew封装了Layer的修改方法,最终的渲染还是在Layer上,而触摸响应的实现,还是有UIVIew来完成。
CoreAnimation流水线
最终的绘制都是通过CALayer来完成,那么Core Animation是如何一步步将CALayer绘制出来呢?
- 当App需要变更UI的时候,即视图树(也就是UIVIew)发生变化,这时候对应的图层树(也就是Layer)也会跟随变化,CoreAnimation会提交事务来渲染最终的效果
- 这时候RenderServer会把数据反序列化为渲染树的内容,然后提交给GPU来渲染
- GPU收到数据,进行它最擅长的图形数据计算,然后把数据给屏幕
- 最终,屏幕完成了RGBA在每个像素点上的变更
这里只是一帧的工作,而系统是通过流水线的方式,使得每一帧都能有最新的内容呈现,如图: