请选择 进入手机版 | 继续访问电脑版
Mozilla

火狐社区

登录    注册

用新浪微博连接 QQ互联

WebRender:让网页渲染如丝顺滑(中)

yingliu Mozilla员工 发表于 2017-10-25 17:39:58 | 显示全部楼层 |阅读模式 [复制链接]
0 4541
本帖最后由 yingliu 于 2017-12-7 14:37 编辑

《WebRender:让网页渲染如丝顺滑(上)》

GPU 加速绘制

因此,浏览器也开始将绘制工作转移到 GPU。



这项转变工作仍在进行中。一些浏览器一直通过 GPU 绘制,另一些浏览器只能在某些平台上(如 Windows 或移动设备)这么做。

GPU 绘制能够解决一些问题。CPU 得以解放,专心处理 JavaScript 和布局工作。此外,GPU 绘制像素比 CPU 快得多,因此它可以加快绘制速度。这也意味着从 CPU 复制到 GPU 的数据要更少了。

但是,在绘制与合成工作之间保持这种区分仍然会产生一定的成本,即使它们都在 GPU 上进行。这么区分,还限制了能够采用的优化类型,它们本可以使 GPU工作更快。

这就是WebRender 所要解决的问题。它从根本上改变了渲染方式,消除了绘制和合成之间的区别。这种解决渲染器性能的方法,能够在当下网络中提供最佳用户体验,并为未来网络提供最好的支持。

这意味着,我们要做的不仅仅是想使帧渲染更快...我们希望使渲染更加一致,不会发生闪动。即便有大量需要绘制的像素,如 4k 显示器或 WebVR 设备,我们仍希望体验能够平滑一些。

当前的浏览器何时会发生闪动 ?


在某些情况下,上述优化能够加速页面渲染。当页面上没有太多变化时(如只有光标在闪烁),浏览器将进行尽量少的工作。



将页面分成图层,增加了这种优化的收益(扩大了最佳情形数)。绘制数个图层,并让它们相对于彼此移动,则“绘画+合成”架构效果非常好。



但图层的使用也需要有所权衡。这将占用不少内存,实际可能会减慢工作。浏览器需要组合有意义的图层。但是很难区分怎样是有意义的。

这意味着,如果页面中有很多不同的东西在移动,图层可能会过多。这些图层占满内存,需要花费很长时间才能传输到合成器。



另一些时候,需要多个图层时,却可能只得到一个图层。这个图层将会不断重绘并转移到合成器,进行合成工作而不改变任何东西。

这意味着你已经将绘制量翻了一番,每个像素都处理了两遍,毫无益处。跨过合成这一步,直接渲染页面会更快。



还有很多情况下,图层用处不大。如对背景色使用动画效果,则整个图层都必须重绘。这些图层只对少量的 CSS 属性有用。

即使大部分帧都是最佳情形(也就是说,它们只占用了帧预算的一小部分), 动作仍可能不稳定。只要三两帧落入最坏情况,就会产生可感知的闪动。



这些情况称为性能悬崖(performance cliffs)。应用程序一直平稳运行,直到遇到这些最坏情况(如背景色动画),帧率瞬间濒临边缘。



不过,这些性能悬崖是可以规避的。

如何做到这一点呢?紧随3D 游戏引擎的脚步。

像游戏引擎一样使用 GPU

如果停止尝试猜测需要什么图层呢?如果不考虑区分绘制与**,仅考虑每一帧绘制像素呢?

这听起来似乎很荒谬,但实际有先例可循。现代视频游戏重新绘制每个像素,并且比浏览器更可靠地保持每秒 60 帧。他们以一种意想不到的方式做到了这一点...他们只是重绘整个屏幕,无需创建那些用于最小化绘制内容的失效处理矩形和图层。

这样渲染网页不会更慢吗?

如果在 CPU 上绘制的话,的确会更慢。但 GPU 就是用来做这事的。

GPU 正是用于极端并行处理的。我在上一篇[关于 Stylo 的文章](https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/#parallel)中谈到过并行的问题。通过并行,机器可以同时执行多种操作。它可以一次完成的任务数量,取决于内核数量。

CPU 通常有 2 到 8 个内核。GPU 往往至少有几百个内核,通常有超过 1,000 个内核。

虽然这些内核的工作方式有所不同。它们不能像 CPU 内核那样完全独立地运行。相反,它们通常一起工作,在数据的不同部分执行相同指令。



填充像素时, 我们正需要这样。每个像素可以由不同的内核填充。一次能够操作数百个像素,GPU 在像素处理方面上比 CPU 要快很多...当所有内核都在工作时确实如此。

由于内核需要同时处理相同的事情,因此 GPU 具有非常严格的步骤,它们的 API 非常受限。我们来看看这是如何工作的。

首先,你需要告诉 GPU 需要绘制什么。这意味着给它传递形状,并告知如何填充。

要达到目的,首先将绘图分解成简单形状(通常是三角形)。这些形状处于 3D 空间中,所以一些形状可以在其他形状背后。然后将三角形所有角顶点的 x、y、z 坐标组成一个数组。



然后发出一个绘图调用 —— 告诉GPU来绘制这些形状。



接下来由 GPU 接管。所有的内核将同时处理同一件事情。它们会:

*   找到形状的所有角顶点位置。这被称为顶点着色(vertex shading)。



*   找出连接这些角顶点的线条。由此可以得到哪些像素被形状所覆盖。这就是所谓的光栅化(rasterization)。



*   已经知道形状所覆盖的像素了,就可以遍历每个像素,确定该像素的颜色。这称为像素着色(pixel shading)。



最后一步可以通过不同的方式完成。要告诉 GPU 如何处理,可以传给 GPU 一个称为像素着色器的程序。像素着色是 GPU 中可编程的几个部分之一。

一些像素着色器很简单。例如形状是单一颜色的,则着色器程序只需要为形状中的每个像素返回同一个颜色。

另外一些情况更复杂,例如有背景图像的时候,需要搞清楚图像对应于每个像素的部分。可以像艺术家缩放图像一样…在图像上放置一个网格,与每个像素相对应。这样一来,只需知道某个像素所对应的区域,然后对该区域进行颜色取样即可。这被称为纹理映射(texture mapping),因为它将图像(称为纹理)映射到像素。



针对每个像素,GPU 会调用像素着色器程序。不同内核可以同时在不同的像素上并行工作,但是它们都需要使用相同的像素着色器程序。命令 GPU 绘制形状时,你会告诉它使用哪个像素着色器。

对几乎所有网页来说,页面的不同部分将需要使用不同的像素着色器。

在一次绘制中,着色器会作用于所有形状,所以通常需要将绘制工作分为多个组。这些称为批处理(batches)。为了尽可能利用所有内核,创建一定数量的批处理工作,每个批次包括大量形状。



这就是 GPU 如何在数百或数千个内核上切分工作的。正是因为这种极端的并行性,我们才能想到在每一帧中渲染所有内容。即便有这样极端的并行性,要做的工作还是很多。解决起来还需要费些脑筋。该 WebRender 出场了……


(未完待续!由于发帖字数限制,又只能分为上中下集了,前往下集


关于作者
Lin Clark (http://code-cartoons.com)
Lin 是 Mozilla 开发者关系团队的工程师。她专注于 JavaScript,WebAssembly,Rust 和 Servo,以及绘制代码漫画。

本文转载自:[众成翻译](http://www.zcfy.cc)
译者:[文蔺](http://www.zcfy.cc/@wemlin)
审校:[huangxiaolu](http://www.zcfy.cc/@huangxiaolu)
链接:[http://www.zcfy.cc/article/4386]
原文:[https://hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank]

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表