ios图像显示原理

CRT 和 LCD

屏幕的渲染可能要从 CRT(Cathode ray tube)显示器和 LCD(Liquid-crystal display)显示器讲起。

CRT

ios_screen_scan

CRT 显示器是比较古老的技术,它使用阴极电子枪发射电子,在阴极高压的作用下,电子由电子枪射向荧光屏,使荧光粉发光,将图像显示在屏幕上,这也就是用磁铁靠近一些老式电视机的屏幕会让它们变色的原因。

而 FPS 就是 CRT 显示器的刷新频率,电子枪每秒会对显示器上内容进行 60 - 100 次的刷新,哪怕在我们看来没有任何改变。

CRT 的电子枪按照上面方式,从上到下一行行扫描,扫描完成后显示器就呈现一帧画面,随后电子枪回到初始位置继续下一次扫描。为了把显示器的显示过程和系统的视频控制器进行同步,显示器(或者其他硬件)会用硬件时钟产生一系列的定时信号。当电子枪换到新的一行,准备进行扫描时,显示器会发出一个水平同步信号(horizonal synchronization),简称 HSync;而当一帧画面绘制完成后,电子枪回复到原位,准备画下一帧前,显示器会发出一个垂直同步信号(vertical synchronization),简称 VSync。显示器通常以固定频率进行刷新,这个刷新率就是 VSync 信号产生的频率。

lcd

但是 LCD 的原理与 CRT 非常不同,LCD 的成像原理跟光学有关:

  • 在不加电压下,光线会沿着液晶分子的间隙前进旋转 90°,所以光可以通过;
  • 在加入电压之后,光沿着液晶分子的间隙直线前进,被滤光板挡住。

LCD原理讲解, 看视频: https://www.bilibili.com/video/BV1Sb411W7Zf

CRT+LCD 看不懂, 看这个: https://www.bilibili.com/video/BV1CJ411q7EB

LCD 的成像原理虽然与 CRT 截然不同,每一个像素的颜色可以在需要改变时才去改变电压,也就是不需要刷新频率,但是由于屏幕撕裂问题,LCD 仍然需要按照一定的刷新频率向 GPU 获取新的图像用于显示。

屏幕撕裂

但是显示器只是用于将图像显示在屏幕上,谁又是图像的提供者呢?图像都是我们经常说的 GPU 提供的。

而这导致了另一个问题,由于 GPU 生成图像的频率与显示器刷新的频率是不相关的,那么在显示器刷新时,GPU 没有准备好需要显示的图像怎么办;或者 GPU 的渲染速度过快,显示器来不及刷新,GPU 就已经开始渲染下一帧图像又该如何处理?

screen-tearing

如果解决不了这两个问题,就会出现上图中的屏幕撕裂(Screen Tearing)现象,屏幕中一部分显示的是上一帧的内容,另一部分显示的是下一帧的内容。

我们用两个例子来说明可能出现屏幕撕裂的两种情况:

  • 如果显示器的刷新频率为 75 Hz,GPU 的渲染速度为 100 Hz,那么在两次屏幕刷新的间隔中,GPU 会渲染 4/3 个帧,后面的 1/3 帧会覆盖已经渲染好的帧栈,最终会导致屏幕在 1/3 或者 2/3 的位置出现屏幕撕裂效果;
  • 那么 GPU 的渲染速度小于显示器呢,比如说 50 Hz,那么在两次屏幕刷新的间隔中,GPU 只会渲染 2/3 帧,剩下的 1/3 会来自上一帧,与上面的结果完全相同,在同样的位置出现撕裂效果。

到这里,有人会说,如果显示器的刷新频率与 GPU 的渲染速度完全相同,应该就会解决屏幕撕裂的问题了吧?其实并不是。显示器从 GPU 拷贝帧的过程依然需要消耗一定的时间,如果屏幕在拷贝图像时刷新,仍然会导致屏幕撕裂问题。

那么如何解决屏幕撕裂呢? 帧缓存区+垂直同步

引入多个缓冲区可以有效地缓解屏幕撕裂,也就是同时使用一个帧缓冲区(frame buffer)和多个后备缓冲区(back buffer);在每次显示器请求内容时,都会从帧缓冲区中取出图像然后渲染。

虽然缓冲区可以减缓这些问题,但是却不能解决;如果后备缓冲区绘制完成,而帧缓冲区的图像没有被渲染,后备缓冲区中的图像就会覆盖帧缓冲区,仍然会导致屏幕撕裂。

解决这个问题需要另一个机制的帮助,也就是垂直同步(Vertical synchronization),简称 V-Sync 来解决。

V-Sync

V-Sync 的主要作用就是保证只有在帧缓冲区中的图像被渲染之后,后备缓冲区中的内容才可以被拷贝到帧缓冲区中,理想情况下的 V-Sync 会按这种方式工作:

normal-vsyn

每次 V-Sync 发生时,CPU 以及 GPU 都已经完成了对图像的处理以及绘制,显示器可以直接拿到缓冲区中的帧。但是,如果 CPU 或者 GPU 的处理需要的时间较长,就会发生掉帧的问题:

lag-vsyn

在 V-Sync 信号发出时,CPU 和 GPU 并没有准备好需要渲染的帧,显示器就会继续使用当前帧,这就加剧了屏幕的显示问题,而每秒显示的帧数会少于 60。

现实中的情况:

ios_screen_display

此图更为形象的反映了屏幕成像的原理流程是怎么样的。CPU计算显示内容,例如视图创建,布局计算、图片解码、文本绘制等;接着 CPU 会将计算好的内容提交到 GPU进行合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待VSync 信号到来时, 视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,传递给显示器显示。如果此时下一个VSync 信号到来时,CPU或GPU都没有完成相应的工作时,则那一帧将会丢失,则就是我们看到屏幕卡顿的原因。

在最简单的情况下,帧缓冲区只有一个,这时帧缓冲区的读取和刷新都都会有比较大的效率问题。为了解决效率问题,显示系统通常会引入两个缓冲区,即双缓冲机制。在这种情况下,GPU 会预先渲染好一帧放入一个缓冲区内,让视频控制器读取,当下一帧渲染好后,GPU 会直接把视频控制器的指针指向第二个缓冲器。如此一来效率会有很大的提升。

那么目前主流的移动设备是什么情况呢?iOS 设备会始终使用双缓存,并开启垂直同步。而安卓设备直到 4.1 版本,Google 才开始引入这种机制,目前安卓系统是三缓存+垂直同步。

页面卡顿产生的原因

ios_frame_drop

在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

从上面的图中可以看到,CPU 和 GPU 不论哪个阻碍了显示流程,都会造成掉帧现象。所以开发时,也需要分别对 CPU 和 GPU 压力进行评估和优化。

CPU资源消耗分析

1、对象创建:对象的创建会分配内存、调整属性、甚至还有读取文件等操作,比较消耗CPU资源。尽量采取轻量级对象,尽量放到后台线程处理,尽量推迟对象的创建时间。(如UIView / CALayer)

2、对象调整:frame、bounds、transform及视图层次等属性调整很耗费CPU资源。尽量减少不必要属性的修改,尽量避免调整视图层次、添加和移除视图。

3、布局计算:随着视图数量的增长,Autolayout带来的CPU消耗会呈指数级增长,所以尽量提前算好布局,在需要时一次性调整好对应属性。

4、文本渲染:屏幕上能看到的所有文本内容控件,包括UIWebView,在底层都是通过CoreText排版、绘制为位图显示的。常见的文本控件,其排版与绘制都是在主线程进行的,显示大量文本是,CPU压力很大。对此解决方案唯一就是自定义文本控件,用CoreText对文本异步绘制。(很麻烦,开发成本高)

5、图片解码:当用UIImage或CGImageSource创建图片时,图片数据并不会立刻解码。图片设置到UIImageView或CALayer.contents中去,并且CALayer被提交到GPU前,CGImage中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。SD_WebImage处理方式:在后台线程先把图片绘制到CGBitmapContext中,然后从Bitmap直接创建图片。

6、图像绘制:图像的绘制通常是指用那些以CG开头的方法把图像绘制到画布中,然后从画布创建图片并显示的一个过程。CoreGraphics方法是线程安全的,可以异步绘制,主线程回调。

7、控制一下线程的最大并发数量

GPU资源消耗分析

1、纹理混合:尽量减少短时间内大量图片的显示,尽可能将多张图片合成一张进行显示。GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸

2、视图混合:尽量减少视图层次和数量,减少透明的视图(alpha<1),不透明的就设置opaque为YES。

3、图形生成:尽量避免离屏渲染(离屏渲染详细介绍),尽量采用异步绘制,尽量避免使用圆角、阴影、遮罩等属性。必要时用静态图片实现展示效果,也可尝试光栅化缓存复用属性。

读者也可以从 iOS 保持界面流畅的技巧 , iOS界面渲染这篇文章中了解更多的相关内容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值