OpenGL:图像的渲染、撕裂、掉帧

作为一个IOS开发者,日常我们更多的是接触业务,我们做的最多的是addSubView: 等等,那么我们的视图就显示出来了。那么它们是如何显示出来的,计算机内部是如何工作的,其底层原理是什么,有时候会卡顿掉帧,这些又究竟是什么原因?这篇文章来大概讲述一下这些内容。

CPU和GPU

CPU

现代计算机整个系统的运算核心、控制核心

GPU

可进行绘图运算工作的专用微处理器,是连接计算机和显示中断的纽带

可以发现 GPU 内部包含众多的ALU(算术逻辑单元) ,它是计算机内部芯片处理算术运算和逻辑运算的核心,由上图可见GPU 的运算能力远超CPU ,因为GPU处理的是大批量的并发运算,而CPU则更多的是串行的逻辑运算。

计算机渲染原理

首先我们得了解计算机是如何把图像渲染出来的:

现代的屏幕都是通过上述类似逐行扫描而把图像显示到屏幕上,而在其底层则是通过一个 帧缓冲区 映射到 屏幕显示器 上:

计算机系统内部结构:

也就是通过CPU对图像的数据进行处理,交给显示处理器,显示处理器再处理成图像数据存储到帧缓冲区 等待显示控制器的信号,将帧缓冲区的内容同步到显示器。

撕裂和掉帧

我们的屏幕是逐行扫描成像,当扫描到底部就会重新扫描下一帧的内容:

我们或许会看到过这样的情形:

这就是通常所讲的图像撕裂问题,为什么会出现这种撕裂问题呢?

图像显示其本质是拿到图像数据后,GPU进行渲染 -> 存储到帧缓冲区 - > 交给视频控制器 -> 读取帧缓冲区的信息(位图)-> 数模转换(数字信号转换为模拟信号) -> 屏幕逐行扫描显示,当第一帧图像扫描到某个位置的时候,GPU已经拿到新的数据并存到帧缓冲区,这个时候视频控制器就从帧缓冲区扫描的是新拿到的一帧图像,但是屏幕是逐行扫描的,他不会打断仍旧是从中间的位置继续扫描,这样就可能出现断层的现象,导致我们所说的撕裂问题,究其原因就是视频控制器的显示速度超过的GPU处理图形的速度。

为了解决撕裂问题,苹果引入了双缓冲区的概念,进而提出了 Vsync垂直同步技术:

GPU 开辟AB两个缓冲区,并对缓冲区进行同步加锁处理,执行流程就是当A缓冲区拿到第一帧数据,就给A缓冲区加上一把锁,屏幕控制器从A拿到数据并逐行扫描完成,A帧缓冲区解锁,并把屏幕控制器指向B缓冲区,B缓冲区加锁并逐行扫描显示,在屏幕控制器扫描B缓冲区的时候,A缓冲区拿到GPU传过来的新数据,以此类推,解决撕裂问题。

其实仔细观察上述图片我们会发现 双缓冲区+垂直同步会解决撕裂问题,但是A图像本来应该显示一帧,由于CPU和GPU的处理速度问题,导致了A的这一帧显示了两次,从而导致B晚一帧显示出来,这就导致了新的问题 掉帧,掉帧并不是丢失图片,而是屏幕 重复渲染了同一帧的数据

为了减少掉帧我们并不能解决掉帧问题,只能尽量通过时间偏移来尽量的减少,这个时候苹果提出了新的解决方案:三缓冲区,再开辟 一块缓冲区,三级缓冲区是为了充分利用CPU/GPU的空余时间,开辟ABC三个缓冲区,当A显示到屏幕上,B也渲染好了,C再从GPU拿新的渲染数据,当屏幕和帧缓冲区都弄好了视频控制器再指向帧缓冲区的另外一个,再显示,依次交替,达到减少掉帧的情况。

总结一下就是:

  • CPU/GPU渲染流水线耗时过长导致掉帧
  • 垂直同步 + 双缓冲区 解决图像撕裂问题,但是会造成掉帧问题
  • 三级缓冲区的开辟是为了合理使用CPU/GPU以达到减少掉帧的次数

IOS下的渲染框架

最后补充一下IOS的渲染框架构成:

以及:

总结

本篇文章主要是大致讲述了计算机CPU和GPU的构成,以及图像显示的过程和原理,渲染的流程以及造成卡顿的原因和解决方案等等,希望能够讲述清楚。

------------- 本文结束  感谢您的阅读 -------------