面试宝点

iOS图形学(三):屏幕成像原理

2021-08-04  本文已影响0人  康小曹

一、GPU和CPU架构

1. GPU由来

无 GPU 的时代,所有任务都在 CPU 中进行计算和处理,但是随着时代的进步,图片和视频大行其道,而这种计算有几个特点:

  1. 计算过程类似,相当于工厂流水线(Graphic Pipline);
  2. 浮点型计算;
  3. 对并发要求比较高;

CPU 也是硬件,但是其处理的事件相对偏软件层面。因为 CPU 是中央处理器,这就意味着它能干的事情很多,也就是功能全面,兼容性更强。而图形相关的计算任务类型比较单一,所以 GPU 应运而生。

2. 架构

GPU 是专门为图形处理而设计的硬件,因为像素点很多且每个像素点会经过相同的流水线处理,只是数据不同导致渲染结果不同而已。

因此,单论纯计算能力,肯定是 GPU 强过 CPU 的。GPU 就是个毫无感情的计算机器。

CPU 是处理中枢,主要工作不是绘图,除了执行相关的机器指令,还涉及到操作系统、内存等的管理、条件预测、异常处理等。对应到汇编上就是 int 指令处理中断,fp,sp 开辟和管理栈空间,cmp 寄存器用于进行条件判断等等。

GPU和CPU架构图

总结:CPU 的能力种类更多,需要适应更多的场景,而 GPU 则是专精,纯粹的图形计算。CPU 和 GPU 都可以对图元数据进行计算生成最终的 bitmap。使用 GPU 绘图根本原因不是能不能,而是速度。

CPU 不是不能进行图形展示相关的计算,只是使用 GPU 更适合,效率更高。这也就直接导致了 iOS 上两种图形处理的划分:Core Graphic 代表着 CPU 绘制,而 Core Animation 和 CALayer 则代表着 GPU 绘制。保持绘图功能流畅高效运行的本质就是让 CPU 和 GPU 维持在合适的负载均衡。

二、屏幕成像原理

1. CRT和LCD

CRT 显示器的工作原理如图:

CRT显示器原理
  1. 电子枪发射通过一定方式发射电子到荧光层形成亮点;
  2. 所有点汇集在屏幕上进行一帧图像的成像;
  3. CRT 无重叠显示的最多点数称为分辨率;

以上就不再赘述,而 LCD 的原理大致相同,如下图:

LCD原理 LCD原理
  1. 发射型显示器是将动能转化成光能的显示器,如 CRT 将电子的动能通过击打在荧光粉上进行成像;
  2. 非发射显示器是将太阳光或者其他光源转化成图像,LCD 就是这种;
  3. 背光板发射最基础的放射性光源;
  4. 下偏光板将放射性光源转化成方向一致的光线;
  5. 内部填充的液晶分子在 TFT 的控制下发生扭转,从而控制光线的明暗变化;
  6. 彩色滤光片控制颜色;
  7. 经过上述处理之后,原本方向一致的光线方向又发生了变化,上偏光片就是将这些光线重新转化成方向一致的光线,最终成像;

CRT 和 LCD的光源都是白色,具体如何转化成彩色可以自行研究;

2. 光栅扫描

光栅扫描器 光栅扫描器
  1. 光栅扫描器的工作原理决定了屏幕的扫描是从上至下从左至右的逐行扫描;
  2. 帧缓存存储的内容不要局限死,可以存储很多内容,但是最后都需要转化成显示器所需要的信号,可能是数字信号,也可能是模拟信号,这要看具体场景。另外,这个数模转换是由 GPU 来提供接口还是视频控制器(后文会提到)来转换,这个未知,但是重点在于视频控制器在一帧的图像成像的开始到结束的整个过程中是要一直使用到帧缓存了,这也是后来为什么会出现双缓冲机制;

与光栅扫描器对应的就是随机扫描器:

随机扫描

3. 光栅系统的实现

光栅系统的作用就是实现逐行扫描,也就是逐行向显示器发送对应的信号来完成显示器的成像。

简单的光栅系统如下:

简单的光栅系统

此种模型下,帧缓存位于系统存储器内部,并未实现分离,分离出帧缓存之后的光栅系统如下:

帧缓存分离

GPU 的出现则形成了第三种模型:

GPU

如上图中的显示处理器一般就是指 GPU。

4. 视频控制器

上面是光栅系统宏观上的组织模型,而视频控制器就是位于帧缓存和显示器之间的媒介。视频控制器按照一定规则从帧缓存中取出数据并计算,最终将信号输出给显示器,显示器接收到信号之后完成成像,其操作流程如下:

视频控制器工作流程
  1. x=0,y=0,读取帧缓存中对应位置的像素信息存入像素寄存器最终以电压强度或者其他形式输出;
  2. x++ ,重复步骤 1,直到增 x 增加至分辨率的最大横坐标,y++;
  3. x=0,x++;
  4. 重复

本质上就是逐行扫描,没什么好多说的~~~ 但是还是要提一嘴,视频控制器读取帧缓存不是一次就读取完了,而是一个像素点一个像素点读取,后期优化成了按块读取,也有的是整行读取,总之,在这一帧画面在显示器上呈现之前,这个帧缓存都不能变,否则就会出现屏幕撕裂,这也是为什么后来推出双缓冲机制的原因。

另外,双缓存的切换也是通过视频控制器来改变存储器地址,从而分别读取 back frame buffer 和 frame buffer;

最后,视频控制器的这个操作是一直在进行的,不是说 frame buffer 未发生改变时就不需要向显示器传递信号了,大部分显示器都是需要通过一定频率(如60HZ、144HZ)来不断激活激发光线以维持成像。

5. DAC/ADC

DAC 和 ADC 是指数模转换和模数转换。

信号:信息的物理表现形式,比如红绿灯、电压等;

模拟信号:自然界中的信号基本都是模拟信号,其特点是时间连续,幅值连续;

数字信号:模拟信号因为连续,所以可以认为其值是无限多的,但是研究时不可能输入无限多的数据,只能用一些离散的点来大致表现出模拟信号的某种关系。数字信号的特点是时间离散,值离散;

显示器接收到的一般都是模拟信号,这样可以最大程度保证画面的流畅度等。而计算机中的信息都是以数字的形式进行存储、计算、输出的,此时如果需要输出到显示器中,就需要数模转换。

显示器的接口格式我们最常见的有 VGA、DVI、HDMI :

主流接口

其中,VGA 是模拟信号,DVI 和 HDMI 是数字信号,所以才有了各种转接线。

其实要理解 VGA、DVI、HDMI ,需要有输入和输出的概念。比如显示器就是图像输出设备,其他设备作为输入设备向显示器传入信号。

如果显示器只有 VGA 接口,那么表示该显示器只接受 VGA 格式的模拟信号,所有的信号的格式、传输、解析等都需要按照 VGA 的规范来。

但是我的 mac 电脑只有 HDMI 的接口,这是什么意思呢?也就是说,电脑作为画面信息的输入设备,向显示器传递信号时,只能传送 HDMI 格式的信号,此时该显示器无法接收,所以此时就需要一个转接线。

转接线内部一般有芯片来对信号进行计算和转换,而一般的连接线是不包含芯片的。比如两头都是 VGA 口的连接线就要求输入和输出设备都是有 VGA 格式的接口。

刚刚说了,显示器画面的输出最终都是模拟信号的形式进行输出,那么为什么还有显示器既支持模拟信号如 VGA,又支持数字信号如 HDMI 呢?这是因为显示器内置了 DAC 功能,即:你把 DVI/HDMI 接口的数字信号传给我就好了,我自己能给解析成模拟信号;

总之:数据格式对等就可以直接输出,不对等就必须进行转换,至于 DAC/ADC 模块位于流程中的哪一部分都是可以的;

一张图总结:

信号转换

三、渲染流程

宏观渲染流程如下:

渲染流程

渲染流程总结:

  1. CPU 计算得到图元传送给 GPU;
  2. GPU 经过几何处理丰富图元数据;
  3. GPU 获取图元的位置信息,通过光栅化生成像素模板;
  4. GPU 根据图元的其他信息,向像素模板中的每一个点填充颜色生成 bitmap;
  5. GPU 将渲染完成的 bitmap 存入帧缓存;
  6. 视频控制器

1. 图元的生成

此时还处于 CPU 阶段,CPU 经过一些计算得到图元。此时 CPU 的计算主要是一些布局信息,如 frame 等;

2. 几何处理

对输入的图元进行处理,这些处理包括着色、纹理、光照处理等等,其本质上就是为了让图元的信息更加丰富,完成 2D 到 3D 的视觉效果转换。经过一些处理后,图元就包含了成像所需要的完整的信息。

其完整过程可能包含:

图元处理

从这一步开始,就已经进入了 GPU 的处理范围,这些步骤包含着大量的浮点计算,这也是为什么 GPU 在进行图形渲染拥有比 CPU 更高效率的原因。

3. 光栅化

上一步处理完毕之后,图元包含了很丰富的信息,但是图元不能直接展示。因为当前成像大豆采用光栅扫描系统,这就需要将图元信息转化成 bitmap 类型,这个步骤就是光栅化,即:根据图元信息计算整块 bitmap 中哪些像素点需要展示;

光栅化的计算过程就不深究了,大体就是根据像素的中心点是否位于图元内部,进而决定该像素点是否需要展示;

总之,这一步就是构建出一个所有需要展示的像素点的模板,供下一步使用;

4. 像素处理

确定了哪些像素点需要进行处理之后,接下来就只需要往这些像素点中填充颜色就好了。这一步就是单纯的通过图元计算出像素点中的色值并填充到像素模板中,最终生成包含完整色值的 bitmap 输出到显示器(一般是 frame buffer)以供展示;

5. 屏幕撕裂

产生的原因:

  1. 第一帧渲染完毕,存入 frame buffer,视频控制器读取之后转化成信号传递给显示器,完成第一帧的成像;
  2. 视频控制器进入到第二个成像周期时,原本应该读取第二帧画面时,但是第二帧画面还未渲染好,新的 bitmap 还没有存入 frame buffer;
  3. 视频控制器读取 frame buffer 进行成像的操作是按照固定频率进行的,不是说 frame buffer 更新了才读取,即使 frame buffer 不变,也会一直重复读取并向显示器发送信号,重复展示这个图像。如果真是那样 frame buffer 一直不更新,屏幕播放完一帧就变黑了;
  4. 当第二个成像周期读取第一帧的 frame buffer 读取到一半时,第二帧画面渲染好了,bitmap 存入 frame buffer,假设此时 x = x1,y = y1;
  5. 此时视频控制器还是按照自己的节奏来读取 frame buffer(x,y的值没有重置),导致第二帧画面的前一半(x1和y1之前的像素点)的画面时第一帧,后一半(x1和y1之后的像素点)的画面是第二帧,由此形成撕裂;
屏幕撕裂

6. Vsync + 双缓冲机制

Vsync 信号:当电子束完成一帧的扫描,将要从头开始扫描时,就会发出一个垂直同步信号。只有当视频控制器接收到 Vsync 之后,才会将帧缓冲器中的位图更新为下一帧,进而将帧缓存中的数据传输给显示器。

但是这样会有一个问题:视频控制器接收到 Vsync 之后,怎么办到立马更新下一帧的 bitmap 到帧缓存呢?直接走完一个渲染流程?显然不可能,由此出现了两个 frame buffer:

双缓冲机制
  1. 第 n 帧成像完毕,发送 Vsync 信号;
  2. 视频控制器接收到 Vsync 信号,此时 GPU 用于存储渲染完成的 bitmap 的指针是指向 back buffer 的,视频控制器中的指针指向 primary buffer,两者互不干扰;
  3. 假设 n 帧之前的渲染都正常且此时第 n+1 帧画面仍然没有渲染完成,即 back buffer 是 n-1 帧的画面;
  4. 交换指针,即视频控制器指向 back buffer,GPU 指向 primary buffer,因为指针交换可以使用位运算来完成,可以认为是瞬间完成该操作;
  5. 视频控制器开始读取 back buffer 展示 n-1 帧的画面;
  6. n+1 帧渲染完毕,但是 GPU 此时指向 primary buffer ,所以不影响视频控制器对 back buffer 的读取,因此不会出现屏幕撕裂;

大概是这么个意思,具体实现肯定会因为平台的不同而进行一些特殊处理和优化。

这个 Vsync 信号相当于加锁,虽然无法解决因为渲染慢导致帧缓存更新不及时的问题,但是确保了每次扫描的都是同一帧。渲染频率为跟上刷新频率而导致的屏幕撕裂问题解决了,但是却引入了一个新的问题:掉帧;

7. 掉帧

上述的 Vsync 流程为了方便理解双缓冲机制,是按照正常模拟的,但是实际上,如果视频控制器接收到 Vsync 时,下一帧还没有渲染完成,那么视频控制器是不会去改变内部指针的,也就是会继续读取上一帧的 frame buffer,从而出现掉帧的情况,如下:

掉帧

注意:图中 A 和 B 表示双缓存,即:back frame buffer 和 primary frame buffer;

另外,还有三缓冲机制,原理差不多,具体就不赘述了~~~

网上很多文章仍然在说 iOS 任何情况下都会使用双缓冲机制,其实这个是老版本了。至少在 iOS12 iphone7 上,使用 instrument 测试时,就已经会出现三缓冲机制了~~~

参考:

  1. https://juejin.cn/post/6844904162765832206#heading-3
  2. http://www.360doc.com/content/14/0919/21/17799864_410802031.shtml
  3. https://www.sohu.com/a/326992448_120158199
上一篇下一篇

猜你喜欢

热点阅读