React 之从视觉暂留到 FPS、刷新率再到显卡、垂直同步再到16ms的故事

简介: React 之从视觉暂留到 FPS、刷新率再到显卡、垂直同步再到16ms的故事

视觉暂留现象


当我们观看一个物体时,物体成像于视网膜上,经由视神经传给人脑,人才能感觉到物体的像。


但当物体移去时,视神经对物体的印象不会立即消失,而要延续 0.1 - 0.4 秒的时间,人眼的这种性质被称为“眼睛的视觉暂留现象”。



我们平时看到的视频,其实是由一张张静态画面组成,“帧”指的就是每一张静态的画面。由于人眼的视觉暂留现象,我们才感觉它是动态的。


帧率


帧率(frame rate)是用于测量显示帧数的度量。测量单位为“每秒显示帧数”(frame per second),也就是我们常说的 FPS。


通常,要避免动作不流畅,FPS 最低是 30,基本流畅则需要 60。太低的时候用户会感到卡顿,太高的时候,超过一个临界值(大约 100 左右),人眼会感受不到明显的差别。


PS:FPS 在游戏领域指的是第一人称射击游戏 (First-person shooter),比如使命召唤、守望先锋、战地、光环、无主之地、孤岛惊魂、绝地求生等。


刷新率


显示器的刷新率是指显示器每秒绘制新图像的次数。其单位为赫兹 (Hz)。


赫兹(符号:Hz)则是国际单位制中频率的单位,表示每一秒周期性事件发生的次数。


如果显示器刷新率为 144Hz,指的就是显示器每秒钟会刷新图像 144 次。


FPS 与 Hz


FPS 指的是內容展示,而 Hz 用于屏幕显示。换句话说,FPS 可以无限高,但刷新率决定了展示的上限。


举个例子,如果你在 120Hz 的手机上播放 60FPS 的影片,你看起來的感觉几乎与 60Hz 的手机是一样的。


还有一点,Hz 通常为恒定速率,不会随场景复杂度而变化。而 FPS,你可以理解为,GPU 每秒平均渲染的帧数,根据渲染情况,帧与帧之间的帧时间并非恒定不变。


画面撕裂


当 FPS 与 Hz 不匹配时,无论是帧率大于刷新率还是刷新率大于帧率,都有可能会出现“画面撕裂”。


所谓画面撕裂(Screen Tearing),是指显示器把两帧或更多帧同时显示在同一画面上的一个现象,比如这种:

image.png

为了解释画面撕裂这个问题,我们需要引入“显卡”这个概念。


显卡


显卡又称显示卡( Video card),是计算机中一个重要的组成部分,承担输出显示图形的任务。


主流显卡的显示芯片主要由 NVIDIA 和 AMD 两大厂商制造,通常将采用 NVIDIA 显示芯片的显卡称为 N 卡,而将采用 AMD 显示芯片的显卡称为 A 卡。


显卡的主要芯片叫“显示芯片”(Video chipset,也叫 GPU 或 VPU,图形处理器或视觉处理器),是显卡的主要处理单元。


简单来说,显卡中,GPU 负责制作画面,图像显示器负责呈现画面。


但显示器显示画面的时候,它是读取 GPU 制作的画面,从上到下,一行一行扫描显示出来的,如果 GPU 刚好制作出图片,显示器刚好开始扫描,这个配合天衣无缝。


但是怎么能总是更刚刚好呢?有的时候战斗比较激烈,GPU 渲染的画面速度慢了一点,显示器展示完上一个画面,下一个画面还没有来,显示器就接着扫描上一个画面,结果扫描到一半,下个画面来了,于是在当前的位置接着扫描下个画面的内容,于是这个画面一部分 A,一部分 B,显示出来,就是画面撕裂的效果。


反过来,GPU 渲染的非常快,显示器还未扫描完上张图,下张图就来了,于是接着上张图的位置扫描下张图,于是也发生了图片撕裂。


垂直同步


你可能想,难道就不能控制显示器,扫描完一张再扫描下一张吗?


当然是可以的,这就是很多游戏里会提供的垂直同步功能(Vertical Synchronization):当显示器在扫描数据时,会等到完全扫描数据后,再扫描 GPU 提交的新数据。


但是呢,这样做就慢了。依然以战斗激烈的时候为例,GPU 渲染的画面速度慢了一点,显示器展示完上一个画面,下一个画面还没有来,那就接着扫描上一个画面,结果扫描到一半,下个画面来了,但是开了垂直同步,于是显示器把上个画面扫描完,再开始扫描下个画面,这就造成了多余的一帧。这对于竞技游戏还是有点影响的。


电影的24帧


我们前面讲了:要避免动作不流畅,FPS 最低是 30,基本流畅则需要 60。


但我们也知道,电影最常见的帧数就是每秒 24 帧,为什么电影 24 帧就很流畅,游戏 24 FPS 就会显得卡顿呢?


最主要的是两个原因,一个是两者画面生成方式不同。


电影的拍摄,它是在每一秒内拍摄一定数量的照片,24 帧就是 24 张照片,每张照片的拍摄都会有一定的曝光时间,这样拍摄出来的图片,如果有高速移动的物体,它会出现模糊,这就跟人眼观察高速物体时的感觉相似,所以人们看电影时感觉自然流畅。如果你去截电影中的图片,尤其是有大幅度动作的场面,截出的图往往都会有模糊。


但是游戏不一样,它的每一帧都很清晰,如果帧数低,它就会让用户感觉是跳来跳去,如果帧数高,虽然每一帧都是清晰的,但因为有视觉暂留,前一张图片暂留,与下一张图片重叠,在大脑看来也会变得模糊,这就接近了自然状态。当然现在的很多游戏,也加入了动态模糊效果,就是让画面看起来更加自然一些。


另外一点就是,电影的帧率是稳定的,游戏则不一定。游戏并不是一秒有 60 帧就不卡,举个极端的例子,如果前 0.5 秒放了 59 帧,后 0.5 秒放了 1 帧,用户同样会感到卡顿。所以如果有比如 0.1 秒内没有稳定输出帧数,用户可能就会感到卡顿。而且游戏会涉及到用户的操作,如果用户做了动作,但帧没有跟上,也很容易感受到卡顿。


浏览器 60Hz?


聊了那么多,开始进入本篇的正题。


如果我们去看 React 的 Concurrent Mode、Fiber、Time Slicing 等知识点,我们一定会听过 16ms 这个说法,大致是浏览器的刷新频率是 60Hz,每帧就是 1000ms / 60Hz = 16.6ms,所以 JavaScript 的操作要在 16.6ms内完成,否则用户就会感到卡顿。


这其中有几个不算很准确的说法。


首先 16ms 这样的说法,是来自于显示器刷新频率为 60Hz,浏览器只是一个应用而已,它控制不了硬件的刷新率,它能做的就是不断的提供每帧的图像,如果提供的慢,跟不上刷新率,就会卡顿。而之所以是 60Hz,不是因为我们的显示器只是 60Hz,而是对于人眼来说,比 60Hz 更高我们可能不会有非常明显的感觉,但如果比 60Hz 低,我们感受到的就比较明显了。


而对于一帧来说,如果要保证流畅性,浏览器确实要在 16.6ms 内完成输出一个画面,但这个时间并不都是留给 JavaScript 的,我们学习浏览器渲染原理的时候,可能会看到这张图:

image.png

你可以看到,在一帧里,浏览器要处理用户输入事件、执行脚本、处理窗口变更、滚动、媒体查询、动画、执行 requestAnimationFrame 和 Intersection-Observer 回调、布局计算、绘制等,留给 JS 执行的时间并不多,在 React 中默认的时间切片时间也只有 5ms


好了,正题结束。下篇讲讲跟帧相关的两个 API: requestAnimationFrame 和 requestIdleCallBack。


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
5月前
|
前端开发
前端React篇之React setState 调用的原理、React setState 调用之后发生了什么?是同步还是异步?
前端React篇之React setState 调用的原理、React setState 调用之后发生了什么?是同步还是异步?
|
前端开发 JavaScript
React 中 setState 什么时候是同步的,什么时候是异步的
React 中 setState 什么时候是同步的,什么时候是异步的
119 0
|
前端开发 JavaScript
react的setState是异步还是同步
react的setState是异步还是同步
|
前端开发
前端项目实战玖拾伍react-admin+material ui-踩坑-List的用法之disableSyncWithLocation查询字符串同步
前端项目实战玖拾伍react-admin+material ui-踩坑-List的用法之disableSyncWithLocation查询字符串同步
64 0
|
前端开发
react中setState是同步还是异步
react中setState是同步还是异步
107 0
|
前端开发
React setState同步更新的几种方法
React setState同步更新的几种方法
733 0
|
存储 前端开发 JavaScript
React setState 同步异步的魅力
在之前的一篇文章【React setState 异步真的只是为了性能吗?】中为大家简述了 React setState 异步的一些更较深层次原因,保持一致性和为以后需的架构升级启动并发更新。文章发出之后,也收到了一位学长的思考,原话是“ 除了这个还可以思考什么是 Web,从最初的顶层设计就不可能是同步的,之后的 Fiber 也是要解决 idle 的问题,最后完成资源的完美调度”。这一句话也给出了更深次的见解,在这里感恩,后续会从这些角度深层次的挖掘。昨天聊完 React setState 异步的原因,今天我们来聊聊 React setState 同步异步的魅力。
212 0
|
前端开发 JavaScript API
React进阶笔记【7_setState(异步?同步?)】
setState 的表现会因为场景的不同而不同: • 在 React 的钩子函数及合成事件中,它表现为 异步。 • 在 setTimeout、setInterval 等函数中,包括在 DOM 原生事件中,它都表现为 同步。
198 0
|
JavaScript 前端开发 Android开发
React Native之js同步调用安卓原生方法@ReactMethod(isBlockingSynchronousMethod = true)
React Native之js同步调用安卓原生方法@ReactMethod(isBlockingSynchronousMethod = true)
525 0
React Native之js同步调用安卓原生方法@ReactMethod(isBlockingSynchronousMethod = true)
|
前端开发 JavaScript
这一次彻底搞懂React中的setState在更新状态是同步还是异步的?
这一次彻底搞懂React中的setState在更新状态是同步还是异步的?
678 0
这一次彻底搞懂React中的setState在更新状态是同步还是异步的?