互联网架构

一文读懂iOS图像显示原理与优化

2020-07-31  本文已影响0人  iOS开发面试题技术合集

原文地址

站在巨人的肩膀上,总结原理实现与优化及卡顿监测,一气呵成,气脉通畅,还要啥自行车huaixiao

图像图形渲染原理

图形渲染主要是利用GPU并行运算能力,实现图形渲染并显示在屏幕的每一个像素上。渲染过程最常用的就是光栅化,即将数据转化为可见像素的过程。GPU及相关驱动实现了图形处理的OpenGL和DirectX模型,其实OpenGL不是函数API而是一种标准,制定了相关函数API及其实现的功能,具体的函数库由第三方来实现,通常是由显卡制造商来提供。

GPU渲染过程如下图所示:

image

主要包括:顶点着色器(包含了3D坐标系的转换,每个顶点属性值设定)、形状(图元)装配(形成基本的图形)、几何着色器(构造新的顶点来形成其他形状,如上图的另一个三角形)、光栅化(将形状映射到屏幕的相应的像素生成片段,片段包含了像素结构所有的数据)、片段着色器(丢弃超过视图以外的像素并着色)、测试与混合(判断像素位置如是否在其他像素的后面及透明度等决定是否丢弃及混合)。

要想图形更加真实逼真需要更多的顶点及颜色属性,这样就增加了性能开销,为提升成产和执行效率,经常会使用纹理来表现细节。

纹理是一个 2D 图片(甚至也有 1D 和 3D 的纹理),纹理一般可以直接作为图形渲染流水线的第五阶段(即片段着色器)的输入;

GPU内部包含了若干处理核来实现并发执行,其内部使用了二级缓存(L1、L2 cache),其与CPU的架构模型包含如下两种形式:分离式及耦合式,如下图所示:

image

CPU 和 GPU 拥有各自的存储系统,两者通过 PCI-e 总线进行连接。这种结构的缺点在于 PCI-e 相对于两者具有低带宽和高延迟,数据的传输成了其中的性能瓶颈。目前使用非常广泛,如PC、智能手机等。

CPU 和 GPU 共享内存和缓存。AMD 的 APU 采用的就是这种结构,目前主要使用在游戏主机中,如 PS4。

屏幕图形显示结构如下:

image

CPU将图形数据通过总线BUS提交至GPU,GPU经过渲染处理转化为一帧帧的数据并提交至帧缓冲区,视频控制器会通过垂直同步信号VSync逐帧读取帧缓冲区的数据并提交至屏幕控制器最终显示在屏幕上。为解决一个帧缓冲区效率问题(读取和写入都是一个无法有效的并发处理),采用双缓冲机制,在这种情况下,GPU 会预先渲染一帧放入一个缓冲区中,用于视频控制器的读取。当下一帧渲染完毕后,GPU 会直接把视频控制器的指针指向第二个缓冲器,如下图所示:

image

双缓冲机制虽然提升了效率但也引入了画面撕裂问题,即当视频控制器还未读取完成时,即屏幕内容刚显示一半时,GPU 将新的一帧内容提交到帧缓冲区并把两个缓冲区进行交换后,视频控制器就会把新的一帧数据的下半段显示到屏幕上,造成画面撕裂现象,如下图:

image

为了解决这个问题,GPU 通常有一个机制叫做垂直同步(简写也是 V-Sync),当开启垂直同步后,GPU 会等待显示器的 VSync 信号发出后,才进行新的一帧渲染和缓冲区更新。这样能解决画面撕裂现象,也增加了画面流畅度,但需要消费更多的计算资源,也会带来部分延迟。

iOS 设备会始终使用双缓存,并开启垂直同步。而安卓设备直到 4.1 版本,Google 才开始引入这种机制,目前安卓系统是三缓存+垂直同步。

卡顿

image

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

图像显示

图形渲染技术栈

整个图形渲染技术栈:App 使用 Core Graphics、Core Animation、Core Image 等框架来绘制可视化内容,这些软件框架相互之间也有着依赖关系。这些框架都需要通过 OpenGL 来调用 GPU 进行绘制,最终将内容显示到屏幕之上,结构如下图所示:

image

框架介绍:

UIKit 自身并不具备在屏幕成像的能力,其主要负责对用户操作事件的响应(UIView 继承自 UIResponder),事件响应的传递大体是经过逐层的 视图树 遍历实现的。

Core Animation 是一个复合引擎,其职责是 尽可能快地组合屏幕上不同的可视内容,这些可视内容可被分解成独立的图层(即 CALayer),这些图层会被存储在一个叫做图层树的体系之中。从本质上而言,CALayer 是用户所能在屏幕上看见的一切的基础。

Core Graphics 基于 Quartz 高级绘图引擎,主要用于运行时绘制图像。开发者可以使用此框架来处理基于路径的绘图,转换,颜色管理,离屏渲染,图案,渐变和阴影,图像数据管理,图像创建和图像遮罩以及 PDF 文档创建,显示和分析。

Core Image 与 Core Graphics 恰恰相反,Core Graphics 用于在运行时创建图像,而 Core Image 是用来处理运行前创建的图像 的。Core Image 框架拥有一系列现成的图像过滤器,能对已存在的图像进行高效的处理。

OpenGL ES(OpenGL for Embedded Systems,简称 GLES),是 OpenGL 的子集。

Metal 类似于 OpenGL ES,也是一套第三方标准,具体实现由苹果实现。大多数开发者都没有直接使用过 Metal,但其实所有开发者都在间接地使用 Metal。Core Animation、Core Image、SceneKit、SpriteKit 等等渲染框架都是构建于 Metal 之上的。当在真机上调试 OpenGL 程序时,控制台会打印出启用 Metal 的日志。根据这一点可以猜测,Apple 已经实现了一套机制将 OpenGL 命令无缝桥接到 Metal 上,由 Metal 担任真正于硬件交互的工作。

UIView与CALayer关系

UIKit中的每一个视图控件其内部都有一个关联的CALayer,即backing layer;由于这种一一对应的关系,视图采用视图树形式呈现,与之对应的图层也是采用图层树形式。

视图的职责是创建并管理图层,以确保当子视图在层级关系中 添加或被移除 时,其关联的图层在图层树中也有相同的操作,即保证视图树和图层树在结构上的一致性。

苹果采用这种结构的目的是保证iOS/Mac平台底层CALayer通用,避免重复代码且职责分离,毕竟采用多点触摸形式与基于鼠标键盘的交互有着本质的区别;

CALayer

CALayer基本等同于纹理,本质上是一张图片,因此 CALayer 也包含一个 contents 属性指向一块缓存区,称为 backing store,可以存放位图(Bitmap)。iOS 中将该缓存区保存的图片称为 寄宿图。

位图(英语:Bitmap,台湾称为点阵图),又称栅格图(Raster graphics),是使用像素阵列(Pixel-array/Dot-matrix点阵)来表示的图像。位图也可指:一种数据结构,代表了有限域中的稠集(dense set),每一个元素至少出现一次,没有其他的数据和元素相关联。在索引,数据压缩等方面有广泛应用,位图的像素都分配有特定的位置和颜色值。

image

图形渲染流水线支持从顶点开始进行绘制(在流水线中,顶点会被处理生成纹理),也支持直接使用纹理(图片)进行渲染。相应地,在实际开发中,绘制界面也有两种方式:一种是 手动绘制(custom drawing);另一种是 使用图片(contents image)。

Contents Image 是指通过 CALayer 的 contents 属性来配置图片,典型的是通过CGImage来指定其内容。Custom Drawing 是指使用 Core Graphics 直接绘制寄宿图。实际开发中,一般通过继承 UIView 并实现 -drawRect:方法来自定义绘制。

虽然 -drawRect: 是一个 UIView 方法,但事实上都是底层的 CALayer 完成了重绘工作并保存了产生的图片。下图所示为 -drawRect: 绘制定义寄宿图的基本原理。

image

需要重绘指:比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法;

资料推荐

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

上一篇 下一篇

猜你喜欢

热点阅读