OpenGL入门 -- 屏幕卡顿/撕裂 及 渲染流程解读
屏幕卡顿/撕裂
屏幕卡顿/撕裂 是指屏幕在显示图形图像时出现的一些显示异常的现象。
- 撕裂:是图形图片显示错位
- 掉帧:是重复显示同一帧图片数据
那么为什么会出现这样的显示问题呢?接下来详细解说一下。
在分析之前,先了解一下 CPU和GPU的一些知识,以及 屏幕成像 的演变过程
CPU 和 GPU
- CPU:是整个计算机的运算核心和控制核心,处理复杂的逻辑和数据,依赖非常高,靠时间片切换实现并发
- GPU:绘图运算工作准用的微处理器,单元计算,高并发,依赖非常低
屏幕成像
计算机的显示方式是经过最开始的 随机扫描显示
演变到现在的光栅扫描显示
的
图形图像
是有像素阵列
组成的,图像的显示和图像的复杂度无关,光栅扫描
都是从左上角开始扫描显示,是显示整个光栅的时间。
简单扫描光栅系统结构如下:
比如:要显示一个60 * 60 的位图
,需要开辟的内存为60 * 60 * 4(RGBA) = 14400
,在早期没有显卡时期,图片要先通过CPU
解码到内存中,然后显示控制器通过系统总线从系统主存中读去显示的图片,这样读取慢,就演变出了常用扫描光栅系统结构
,结构如下:
常用扫描光栅系统结构
是在系统主存中开辟一块作内存为帧缓存,来存储显示的数据,但是依然通过系统总线中读取,依然读取受限,然后演变出了下面的高级扫描光栅系统结构
图片解码到内存中,copy
到帧缓存区
,GPU
直接操作显存中的帧缓存区的数据
了解完这些之后,开始分析一下屏幕撕裂的问题。
屏幕撕裂
光栅扫描系统
主要分三个部分:
- 显示器(来源于帧缓存区),
- 视频控制器(负责控制刷新的部件,帧缓存区和显示器的对应关系),
- 帧缓存区(显示的内容, 帧缓存/显存主要存储图像刷新数据)
显示器显示的是帧缓存区的数据,在光栅扫描的过程中,由于人眼的视觉暂留,当刷新的频率超过16帧
时,人眼就觉得是连贯的。简单的屏幕成像过程如下:
屏幕撕裂的原因
-
在屏幕显示图像过程中,是不断从帧缓存区获取一帧一帧是数据进行显示
-
然后在渲染的过程中,帧缓存区中仍是旧的数据,屏幕拿到旧的数据去进行显示
-
在旧的数据没有读取完时,新的一帧数据处理好了,放入了缓存区,这时就会导致屏幕另一部分的显示是获取的新数据,从而导致屏幕上呈现图片不匹配,人物、景象等错位显示的情况。如下图:
撕裂的原因:
解决撕裂
苹果官方,采用垂直同步+双缓存区
来解决撕裂问题,下面是简单图解:
- 垂直同步:是指给帧缓冲加锁,当电子光束扫描的过程中,只有扫描完成了才会读取下一帧的数据,而不是只读取一部分
-
双缓冲区:采用两个帧缓冲区来存储
GPU
处理结果,当屏幕显示其中一个缓存区内容时,另一个缓冲区继续等待下一个缓冲结果,两个缓冲区依次交替
下面是苹果官方示意图:
使用垂直同步+双缓存区
,从根本上解决了屏幕撕裂的问题,但是随之有产生了另一个问题——— 屏幕卡顿掉帧。
屏幕卡顿
掉帧:简单说就是屏幕重复显示同一帧数据,当接收到Vsync
时,由于CPU和GPU
的图片数据还没处理完,拿不到frameBuffer
的数据,而重复渲染同一帧的数据。
为了减少掉帧(减少而不是解决掉帧),引入了三缓冲区,充分合理利用CPU/GPU
减少掉帧次数。
小结:
-
CPU/GPU
渲染流⽔线耗时过⻓->掉帧 - 垂直同步
Vsync
+ 双缓存区DoubleBuffering
以掉帧作为代价解决屏幕撕裂问题 - 三缓存区: 合理使⽤
CPU/GPU
减少掉帧次数
iOS 渲染流程
iOS
中整体渲染流程如下:
- App通过调用
CoreGraphics
、CoreAnimation
、CoreImage
等框架的接口触发图形渲染 - 将渲染交由
OpenGL ES
或者Metal
,驱动GPU
做渲染,最后显示到屏幕上 - 由于
OpenGL ES
是跨平台的,所以在实现中,需让各自的平台为OpenGL ES
提供载体。在iOS
中,就是通过CoreAnimation
提供窗口,让App
可以去调用。
View 和 CALayer 的关系
-
View
View
属于UIKit
,负责绘制和动画,负责布局和子View
的管理,负责处理点击事件 -
CALayer
属于
CoreAnimation
框架,负责渲染显示,既用于UIKit
,也用于APPKit
-
二者的关系
二者基于的框架不同,
UIVIew
基于UIKit
框架,可以处理触摸事件,可以管理子视图,CALayer
基于CoreAnimation
,而CoreAnimation
是基于QuartzCode
,只负责渲染,不能处理用户事件。从继承关系来说,
CALayer
继承NSObject
,而UIView
是直接继承自UIResponder
的,UIView
属于UIKit
的组件,而UIKit
的组件到最后都会被分解成layer
,存储到图层树中,需要与用户交互时,用UIView
,不需要交互时,都可以
CoreAnimation
CoreAnimation官方解释:Render、Compose,and animate visual elements
,本质是一个复合引擎,主要的职责包括 渲染、构建和动画实现。
iOS
中基于CoreAnimation
构建的框架有两个,分别是UIKit
和APPKit
,而CoreAnimation
底层是基于Metal
和CoreGraphics
CoreAnimation的渲染流程
流程如如下:
主要可以分为两部分:
-
Coreanimation
部分-
APP
处理UIView
等的事件,通过CPU
完成对显示内容的计算,将计算后的图层打包,在下一次runloop
时提交到渲染服务器 -
Ren der Server
中主要对收到内容解码,然后执行OPenGL
相关程序,调用GPU
渲染,Render Server
操作分析如下:
-
-
GPU
渲染部分-
GPU
通过顶点着色器、片元着色器完成对显示内容的渲染,将结果存入帧缓存区 -
GPU
通过帧缓存区、视频控制器等相关部件,将内容显示到屏幕
-