GPU与CPU
CPU: 计算的运算核心、控制核心;
GPU: 可进行绘制运算的专用微处理器。
屏幕扫描
光栅扫描过程:
屏幕扫描过程是横向一行一行进行的。
扫描显示到屏幕
如上图所示,左侧是帧缓冲区,右侧为显示器。帧缓冲区的是位图。视频控制器经过数模转换将位图显示到屏幕上。
屏幕撕裂
屏幕撕裂类似于下图,屏幕上半部分和下班部分出现撕裂的现象:
那么屏幕撕裂是怎么产生的呢?我们需要先了解下屏幕是如何成像的。
屏幕成像过程
- 需要显示的图片经过GPU渲染,将渲染后的结果存入帧缓冲区,帧缓冲区存储的为位图;
- 然后视频控制器从帧缓冲区读取位图;
- 视频控制器将读取到的位图交给显示器显示,从左上角逐行扫描并显示。
屏幕撕裂原因
- 在屏幕显示图像的过程中,是不断的从帧缓冲区获取一帧帧的数据显示的;
- 然后在渲染一帧数据的时候,扫描到一半的时候,也就是一帧数据还没有渲染完成,然后新的一帧数据放入了帧缓冲区;
- 然后图片下半部分读取的数据就是新一阵的图形数据了,这样就产生了图像上下不匹配,人物、景色错误的现象。 图示如下:
苹果官方的解决方案
苹果针对撕裂现象的解决方方案是:双缓存区+垂直同步
。该方案是给帧缓冲区加锁,可以解决撕裂问题。但是会导致新的掉帧问题。
以下是垂直同步+双缓存的一个图解过程,如有描述错误的地方,欢迎留言指出
- 垂直同步:给帧缓存区加锁,在电子束扫描的过程中,不允许对该帧缓存区进行数据存储;
- 双缓存区:采用两个帧缓存区用于GPU处理结果的存储,当屏幕显示其中一个缓存区内容时候,另一个缓存区继续等待下一个缓存结果的存入。两个缓存区依次进行交替。
掉帧
采用苹果的双缓存区+垂直同步
后引发了新的掉帧问题。掉帧就是屏幕重复显示同一帧数据的情况。
如图所示:当前屏幕显示的是A,当扫描下一帧图像的时候,由于CPU和GPU处理的B还没有准备好,所以扫描显示的仍然是A。
- 导致掉帧的原因,可能是CPU或者GPU处理超时导致的;
- 针对掉帧情况,我们可以在采用三缓存区进行优化。三缓存区可以更加合理充分的利用GPU/CPU。三缓存区不能解决掉帧问题,只是减少出现掉帧的次数。
iOS中的渲染
在iOS中的渲染流程如下所示:
- App通过调用CoreGraphics、CoreAnimation、CoreImage等框架的接口触发图形渲染操作;
- CoreGraphics、CoreAnimation、CoreImage等框架将渲染交由OpenGL ES,由OpenGL ES来驱动GPU做渲染,最后显示到屏幕上;
- 由于OpenGL ES 是跨平台的,所以在他的实现中,是不能有任何窗口相关的代码,而是让各自的平台为OpenGL ES提供载体。在ios中,如果需要使用OpenGL ES,就是通过CoreAnimation提供窗口,让App可以去调用。
iOS中渲染框架总结
主要由以下六种框架:
UIView和CALayer的关系
UIView
- UIView属于UIKit;
- 负责绘制图形和动画操作;
- 用于界面布局和子视图的管理;
- 处理用户的点击事件。
CALayer
- CALayer属于CoreAnimation;
- 只负责显示,且显示的是位图;
- CALayer既用于UIKit,也用于APPKit,UIKit是iOS平台的渲染框架,APPKit是Mac OSX系统下的渲染框架,由于iOS和Mac两个系统的界面布局并不是一致的,iOS是基于多点触控的交互方式,而Mac OSX是基于鼠标键盘的交互方式,且分别在对应的框架中做了布局的操作,所以并不需要layer载体去布局,且不用迎合任何布局方式。
UIView和CALayer的关系
- UIView基于UIKit框架,可以处理用户触摸事件,并管理子视图;
- CALayer基于CoreAnimation,而CoreAnimation是基于QuartzCode的。所以CALayer只负责显示,不能处理用户的触摸事件;
- 从父类来说,CALayer继承的是NSObject,而UIView是直接继承自UIResponder的,所以UIVIew相比CALayer而言,只是多了事件处理功能;
- 在应用层面来说,需要与用户交互时,使用UIView,不需要交互时,使用两者都可以。
CoreAnimation
我们常常认为CoreAnimation是用来指定动画的,其实它是一个复合引擎,只要的职责是渲染、构建和动画(Render、Compose,and animate)。
- ios中基于CoreAnimation构建的框架有两个:UIKit和APPKit;
- CoreAnimation 又是基于Metal 、CoreGraphics封装的。