屏幕显示图像的原理
- 位图(Bitmap) : 是一种数据结构。一个位图是由若干个像素组成,每个像素的颜色信息由RGB组合或者灰度值表示。根据位深度,可将位图分为1、4、8、16、24及32位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。
显示器显示的图像就是将位图中的数据映射到屏幕上,映射的过程是有一个电子枪,从屏幕的左上角开始进行逐行的扫描,当右下角的最后一个像素被显示在屏幕上之后,电子枪又会回到左上角进行下一帧图片的显示。屏幕的刷新频率是固定的,通常是60Hz,即一秒钟会刷新60次。
画面撕裂产生的原因是由于显卡的性能提升,输出的帧率非常高,如果显卡输出的帧率超过60fps,两者不同步,就会产生画面撕裂。
如果电子枪扫描到屏幕的一半时,这时新一帧要显示的画面数据已经准备好了,就会将旧的数据给替换掉,那么此时电子枪扫描出来的数据就是新的一帧画面。屏幕的上下两个部分显示的画面就不是同一帧的数据,就会造成画面撕裂。
垂直同步+双缓冲
为了解决画面撕裂的问题,这里引入了一个新的技术,垂直同步和双缓冲区。
- 垂直同步:而当一帧画面绘制完成后,电子枪回复到原位,准备画下一帧前,显示器会发出一个垂直同步信号(vertical synchronization),简称 VSync。
通常来说,计算机系统中 CPU、GPU、显示器是以上面这种方式协同工作的。CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会逐行读取帧缓冲区的数据,经过数模转换传递给显示器显示。
有了双缓冲区,两个缓冲区协同工作,视频控制器先从一个缓冲区中读取数据,新的一帧的数据会被放在另一个缓冲区中,当视频控制器收到VSync
后,视频控制器就会交换两个缓冲区,读取新的画面。
通过垂直同步和双缓冲解决了画面撕裂的问题,但是又引出了一个新的问题,那就是掉帧。当我们的手机在快速滑动的过程中感觉到卡顿的现象就是掉帧引起的。
我们知道每一帧画面计算都是由CPU和GPU一同计算得到的,在刷新率为60Hz
设备上,每个刷新周期是16.7ms
, 如果在这段时间内新的一帧应该显示的内容还没有被计算出来,那么这一帧就会被丢弃,显示器显示的内容依然是上一帧的内容。知道下一帧被计算出来,才会在下个VSync显示。
图像渲染过程
Core Animation 流水线
在介绍 Core Animation
流水线之前,我们先了解一下 Core Animation
。
Core Animation
Core Animation
源自于 Layer Kit
,动画只是 Core Animation
特性的冰山一角。
Core Animation
是一个复合引擎,其职责是 尽可能快地组合屏幕上不同的可视内容,这些可视内容可被分解成独立的图层
(即 CALayer),这些图层会被存储在一个叫做图层树的体系之中。从本质上而言,CALayer
是用户所能在屏幕上看见的一切的基础。
事实上,App本身不负责渲染,渲染是由一个独立的进程 Render Server
负责。
App通过IPC(进程间通信)将渲染任务和相关数据提交给 Render Server
。 Render Server
处理完数据后,在传递给GPU。最后由GPU调用iOS的图像设备进行显示。
Core Animation 流水线的详细过程如下:
- 首先,App处理事件,如:用户点击事件,在此过程中App可能需要更新
视图树
,相应地,图层树
也会被更新。 - 接着,App通过CPU完成对需要显示内容的计算,如:视图的创建、布局计算、图片解码、文本绘制等,在完成其内容的显示计算之后,App对图层进行打包,并在下一次
Runloop
将其发送至Render Server
, 即完成了一次Comit Transaction
操作。 -
Render Server
主要执行OpenGL、 Core Graphics
相关程序,并调用GPU - GPU在物理层上完成对图形的渲染。
- 最终,GPU通过 Frame Buffer、视频控制器等相关部件, 将图像显示在屏幕上。
Commit Transaction
App将渲染相关的数据提交给 Render Server
前的最后一步 Commit Transaction
可以细分为4个步骤:
- Layout
Layout
阶段主要包括视图构建,包括:layoutSubviews
方法的重载, addSubview:
方法填充子视图等。
- Display
Display
阶段主要进行视图绘制,这里仅仅设置要成像的图元数据。重载视图的drawRect:
方法自定义UIView
的显示,其原理是在drawRect:
方法内部绘制寄宿图,该过程使用CPU和内存。
- Prepare
Prepare
阶段属于附加步骤,一般用于图像的解码和转换操作。
- Commit
Commit
阶段主要将图层进行打包,并将它们发送至 Render Server
。该过程会递归执行,因为图层和视图都是以树形结构存在。
GPU渲染过程
顶点数据 -> 顶点着色器 -> 图元装配 -> 光栅化 -> 片元着色器 -> 位图 -> 显示