[TOC]
前言
最近在 iOS 开发中做了较多动画相关的功课,在进一步探索动画及渲染相关原理的时候,随着逐步深入了解发现这里面涉及到从硬件底层到软件框架等一系列相关知识。
为了了解
iOS系统的图像渲染原理,我们首先对计算机图形渲染原理进行了一定了解并输出了01-计算机原理|计算机图形渲染原理这篇文章;我们在01-计算机原理|计算机图形渲染原理这篇文章可以了解到
终端设备图形渲染的流水线以及屏幕图像显示原理;紧接着,我们将沿着屏幕图像显示原理的话题,进入
深入了解屏幕成像与卡顿这个专题,为后续进一步对相关专题的研究做知识储备。
一、屏幕成像过程
我们在01-计算机原理|计算机图形渲染原理这篇文章可以了解到终端设备图形渲染的流水线以及屏幕图像显示原理;
我们首先简单回顾一下屏幕成像的过程
1. 将图像渲染得到的像素信息显示在物理屏幕
-
在图像渲染流程结束之后,接下来就
需要将得到的像素信息显示在物理屏幕上了。 -
经过 GPU 处理之后的
像素集合,也就是位图,会被帧缓冲器缓存起来,供之后的显示使用。 -
GPU 最后一步
渲染结束之后像素信息,被存在帧缓冲器(Framebuffer)中,之后视频控制器(Video Controller)会读取帧缓冲器中的信息,经过数模转换传递给显示器(Monitor),进行显示。完整的流程如下图所示:- ①渲染→②帧缓冲器(存储像素信息)→③视频控制器读取缓存→数模转换、显示器显示
2.像素信息显示过程中,显示器电子束的扫描
-
显示器的电子束会从屏幕的左上角开始逐行扫描,屏幕上的每个点的图像信息都从帧缓冲器中的位图进行读取,在屏幕上对应地显示。扫描的流程如下图所示:
-
电子束扫描的过程中,屏幕就能呈现出对应的结果,每次整个屏幕被扫描完一次后,就相当于呈现了一帧完整的图像。
-
屏幕不断地刷新,不停呈现新的帧,就能呈现出连续的影像。
-
而这个屏幕刷新的频率,就是帧率(Frame per Second,FPS)。由于人眼的视觉暂留效应,当屏幕刷新频率足够高时(FPS 通常是 50 到 60 左右),就能让画面看起来是连续而流畅的。对于 iOS 而言,app 应该尽量保证 60 FPS 才是最好的体验
3.屏幕呈现连续影像良好体验的重要依赖
- 经过前面的介绍铺垫,我们不难清楚,屏幕成像的过程是“渲染→成像→扫描→呈现在物理屏幕”。而
屏幕呈现连续影像则是每一帧图片的画面成像完毕,被完整扫描后,上下两帧图像连续切换的过程。 - 所以我们不难得出结论:要想获得屏幕呈现连续影像良好体验,需要保证“上下两帧图像被完整扫描,顺畅切换”。然而,要创造这样的良好体验的中间,首次碰到这些问题的伟大工程师朋友们,遇到了
"屏幕撕裂"、"掉帧"等问题,不过我们聪明且勤奋的工程师朋友也提出了"Double Buffering"、"Triple Buffering"、"Vsync"、"Hsync"等方案来解决遇到的难题。我们接下来将一一铺展开来去了解它们
二、屏幕撕裂 Screen Tearing
- 所谓屏幕撕裂,即是“已扫描的部分就是上一帧的画面,而未扫描的部分则会显示新的一帧图像”上下两帧图像各自的一部分同时出现屏幕上造成画面非同一帧的现象。
- 在这种单一缓存的模式下,最理想的屏幕成像情况就是一个流畅的流水线:
- 每次电子束从头开始新的一帧的扫描时,CPU+GPU 对于该帧的渲染流程已经结束,渲染好的位图已经放入帧缓冲器中。
- 但这种完美的情况是非常脆弱的,很容易产生屏幕撕裂:
- CPU+GPU 的渲染流程是一个非常耗时的过程。如果在电子束开始扫描新的一帧时,位图还没有渲染好,而是在扫描到屏幕中间时才渲染完成,被放入帧缓冲器中 ---- 那么已扫描的部分就是上一帧的画面,而未扫描的部分则会显示新的一帧图像,这就造成屏幕撕裂。
三、垂直同步 Vsync + 双缓冲机制 Double Buffering
- 解决屏幕撕裂、提高显示效率的一个策略就是使用垂直同步信号 Vsync 与双缓冲机制 Double Buffering。
- 根据苹果的官方文档描述,iOS 设备会始终使用 Vsync + Double Buffering 的策略。
- Vsync: 垂直同步信号(vertical synchronisation,Vsync)相当于给帧缓冲器加锁。
- 当电子束完成一帧的扫描,将要从头开始扫描时,就会发出一个垂直同步信号。
- 只有当视频控制器接收到 Vsync 之后,才会将帧缓冲器中的位图更新为下一帧,这样就能保证每次显示的都是同一帧的画面,因而避免了屏幕撕裂。
- Double Buffering: 双缓冲机制
- 但是Vsync帧缓冲器加锁这种情况下,视频控制器在接受到 Vsync 之后,就要将下一帧的位图传入,这意味着整个 CPU+GPU 的渲染流程都要在一瞬间完成,这是明显不现实的;
- 所以双缓冲机制会增加一个新的备用缓冲器(back buffer)。
- 渲染结果会预先保存在 back buffer 中
- 在接收到 Vsync 信号的时候,视频控制器会将 back buffer 中的内容置换到 frame buffer 中,此时就能保证置换操作几乎在一瞬间完成(实际上是交换了内存地址);
四、掉帧 Jank
- 启用 Vsync 信号以及双缓冲机制之后,能够解决屏幕撕裂的问题,但是会引入新的问题:掉帧。
- 何为掉帧呢?
- 掉帧: 掉帧就是如果在接收到 Vsync 之时 CPU 和 GPU 还没有渲染好新的位图,视频控制器就不会去替换 frame buffer 中的位图。这时屏幕就会重新扫描呈现出上一帧一模一样的画面。相当于两个周期显示了同样的画面,这就是所谓掉帧的情况。
- 如图所示,A、B 代表两个帧缓冲器,当 B 没有渲染完毕时就接收到了 Vsync 信号,所以屏幕只能再显示相同帧 A,这就发生了第一次的掉帧。
五、三缓冲 Triple Buffering
-
事实上上述策略还有优化空间。我们注意到在发生掉帧的时候,CPU 和 GPU 有一段时间处于闲置状态:
- 当 A 的内容正在被扫描显示在屏幕上,而 B 的内容已经被渲染好,此时 CPU 和 GPU 就处于闲置状态。
- 那么如果我们增加一个帧缓冲器,就可以利用这段时间进行下一步的渲染,并将渲染结果暂存于新增的帧缓冲器中。
-
如图所示,由于增加了新的帧缓冲器,可以一定程度上地利用掉帧的空档期,合理利用 CPU 和 GPU 性能,从而减少掉帧的次数。
六、屏幕卡顿的本质
- 手机使用卡顿的直接原因,就是掉帧。
- 前文也说过,屏幕刷新频率必须要足够高才能流畅。对于 iPhone 手机来说,屏幕最大的刷新频率是 60 FPS,一般只要保证 50 FPS 就已经是较好的体验了。
- 但是如果掉帧过多,导致刷新频率过低,就会造成不流畅的使用体验。
- 这样看来,我们可以大概总结一下:
- 屏幕卡顿的根本原因:CPU 和 GPU 渲染流水线耗时过长,导致掉帧。
- Vsync 与双缓冲的意义:强制同步屏幕刷新,以掉帧为代价解决屏幕撕裂问题。
- 三缓冲的意义:合理使用 CPU、GPU 渲染性能,减少掉帧次数。
相关阅读(共计14篇文章)
iOS相关专题
- 01-iOS底层原理|iOS的各个渲染框架以及iOS图层渲染原理
- 02-iOS底层原理|iOS动画渲染原理
- 03-iOS底层原理|iOS OffScreen Rendering 离屏渲染原理
- 04-iOS底层原理|因CPU、GPU资源消耗导致卡顿的原因和解决方案
webApp相关专题
跨平台开发方案相关专题
阶段性总结:Native、WebApp、跨平台开发三种方案性能比较
Android、HarmonyOS页面渲染专题
小程序页面渲染专题
总结
-
01-项目方案的大前端技术搭配选型 (
待输出) -
02-大前端工程师技术栈积累的思考 (
待输出)