考古挖掘:高刷显示器下的 requestAnimationFrame

4,765 阅读3分钟

起因

今天有一位同学,在群里问了这一个问题:requestAnimationFrame 的执行机制如何

image.png

这个问题当然不陌生。requestAnimationFrame 在浏览器每一帧开始绘制之前会执行。借助 requestAnimationFrame 高效的执行效率,我们可以使用requestAnimationFrame 进行动画操作、FPS 的计算、甚至可以通过算每一帧所需要的真实时间,来增加帧数。 image.png

MDN中,还有这样一句话:在多数遵循W3C建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。于是yck同学此时问了一句:我屏幕刷新率特别快怎么办?

image.png

这句话令人深思。在如今高刷显示器盛行的年代,我依然在使用60hz的MacBook。虽然MDN写着匹配,但这个也不一定对。带着这个疑问,我开始了探索之旅。

作为一个半数码党,对于现在数码产品显示器的刷新率种类还是懂一点的。有120hz,144hz等等。我抱着试试看的心态,去搜了144hz下 requestAnimationFrame 的状况

现状

果然不出所料。我通过搜索,找到了一篇问答帖:这位网友讲,它使用了165hz的显示器,但通过requestAnimationFrame 计算出来的FPS依然只有30-60fps

image.png

那就证明了,的确在一部分用户下,刷新率和 requestAnimationFrame 存在不同步问题。可是在回答区,有一部分用户也反馈,他们屏幕刷新率和 requestAnimationFrame 是同步的。这样也印证了大概率是一个Bug。

真的是 Bug

于是我去Chromium Bugs网站内去查找,找到了这样的一个Issue。内容也在写,使用了144hz刷新率的显示器,但FPS上限依然只有60。

image.png

于是我抛弃掉一部分争论,直接找修复的代码和备注。继续向下翻,找到了chromium官方人员关于只有60fps的解释:

On Linux Nvidia we use GLX_SGI_video_sync to time vsyncs. Unfortunatelyit's difficult to calculate an accurate refresh rate with it because itsvideo sync counter is wrong. Before, we hardcoded 60 FPS. Now insteadwe use xrandr to get the refresh rate of the primary monitor.

其大意是,由于在Linux下的Nvdia驱动,在使用GLX_SGI_video_sync进行计算vsyncs(垂直同步)的时间时,由于计数器错误,于是官方直接将60FPS进行硬编码。现在,他们换成了使用xrandr进行获取刷新率计算。

xrandr 是一款Linux官方的 RandR (Resize and Rotate)。它可以设置屏幕显示的大小、方向、镜像等。wiki.archlinux.org/index.php/X…

既然是Bug,那我们就看下Chromium到底是怎么修复的

修复逻辑

找到回答中具体的commit记录,然后链接到Chromium Gerrit平台。来到了这个CR详情

来到 gl_surface_glx.cc这个文件。glxChromium中硬件加速相关的代码

image.png

可以看到,其中的一个float变量叫 refresh_rate,这个就是最后计算刷新率的值。然后使用 (1 / refresh_rate),计算出刷新一次所控制的秒。如果是60hz,则 1000ms / 60次 = 16.66ms 1次。

这里我们继续跟 refresh_rate 的计算方法 => GetRefreshRateFromXRRModeInfo。找到 x11_display_util.cc 文件可以看到逻辑

image.png

这里可以看到,其计算逻辑是 Pixel Clock / (Horizontal Total * Vertical Total)。那么这三个数值代表什么意思呢?

  • Pixel Clock 时钟频率,是显示器每秒钟绘制的像素数。
  • Horizontal Total 每一帧绘制的水平像素数量
  • Vertical Total 每一帧绘制的垂直像素数量

时钟每秒处理的像素数量 / (水平像素数量) * (行像素数量)

关于多显示器

Chromium Gerrit平台提交的代码注释中可以看到,多显示器支持其实是存在问题的。这里可以参考另外一个Bug。这位同学使用了144hz + 60hz的显示器,但输出依然是60fps

image.png

目前官方人员只提出了解决方案,但没有看到任何commit有产出。所以这还是个Bug

其他参考