原文:https://software.intel.com/content/www/us/en/develop/articles/software-vs-gpu-rasterization-in-chromium.html

已发布:02/05/2016 最后更新时间:02/04/2016

本文概述了web浏览器将网站信息光栅化为实际像素的方法。当web浏览器下载一个页面时,它解析源代码并创建DOM。然后它需要弄清楚哪些图像/文本/帧显示在哪里。该信息在内部表示为层树。您可以将层树视为简化的DOM树,因为它们只包含页面的可见元素,并且可以将一些DOM元素组合到一个层中。

假设您已经有了图层树,并且希望在屏幕上显示它们。到目前为止,每个层都已经包含了有关如何绘制的信息,因此您只需要按正确的顺序调用正确的绘制说明。但是,每次用户滚动或播放动画时,必须对整个页面执行此操作,这显然是低效的。因此,我们将页面划分为多个平铺,这些平铺由大小约为256x256像素的正方形组成。[1]您可以在下图中看到平铺,并使用Chromium*开发工具将平铺边界可视化。[2]

alt 图片

光栅化单个平铺比光栅化整个站点更容易,因为我们可以忽略在受影响的平铺上看不到的绘制命令。如果用户单击平铺或播放某些动画,则只需再次光栅化受影响的平铺。在简单的网页上,平铺通常只光栅化一次,但更多的交互式网站或带有动画的网站可能会导致每帧重新计算一些平铺。

有两种主要的方法来光栅化一个平铺,旧的方法是做在它的CPU上,并把它作为一个纹理发送到GPU;较新的方法是使用GPU和OpenGL对其进行光栅化。每种方法都有各自的优缺点,每种方法都最适合某些类型的网页。GPU光栅化不会很快取代软件(CPU)光栅化,我稍后解释。

软件(CPU)光栅化

Chromium使用Skia库进行光栅化,最终使用扫描线算法创建位图。通常,要将结果发送到GPU并在屏幕上绘制,我们可以通过调用glTexImage2D()将其上传,但Chromium的安全模型使其更加复杂。

由于渲染过程是沙盒式的,无法直接访问GPU,Chromium使用一个单独的GPU进程,其主要目的是充当渲染过程和GPU之间的代理,接受OpenGL命令并将其传递给图形驱动程序。因此,我们必须将光栅化结果放入共享内存中,并向Chromium GPU进程发送一条消息,以便对其调用glTexImage2D()。

这有一个明显的缺点,在平铺的变化时,上传必须每次都做,这意味着大量的数据必须传输和GPU进程可以保持非常繁忙。简单的web页面不会受到这种影响,但是使用大量动画或JavaScript效果的交互式web页面几乎每帧都必须重新绘制(每秒最多60次)。这在移动设备上尤其是一个问题,因为屏幕更小,设计师通常在用户请求之前隐藏元素,这会产生许多过渡效果。

零拷贝软件光栅化

零拷贝纹理上传是我的同事Dongseong Hwang和Tiago Vignatti所做的优化。它试图最大限度地减少上传纹理到GPU的每一个平铺变化效率低下的过程。光栅化的方式与以前相同,但是我们没有为每个平铺更改使用glTexImage2D()手动上载纹理,而是告诉GPU内存映射纹理在主内存中的位置,这样GPU就可以直接读取纹理。这样,ChromiumGPU进程只需进行初始内存映射设置,之后就可以保持空闲。这提高了性能,节省了移动设备的大量电池寿命。

GPU光栅化

通过GPU光栅化,部分工作负载从CPU移动到GPU,所有多边形都必须使用OpenGL基本体(三角形和线)进行渲染。这也是由Skia通过GPU后端Skia Ganesh执行的。结果永远不会保存在主内存中,因此不必复制到任何地方,因为这一切都发生在GPU上。虽然这稍微减少了主内存的使用,但它将使用更大数量的GPU内存。

GPU光栅化的主要问题是字体或其他小而复杂的形状。OpenGL没有任何原生的文本呈现原语,因此您必须使用现有的库(或者痛苦地实现自己的库),该库可以使用各种方法对它们进行光栅化。例如,使用三角形表示角色、使用预计算的纹理或其他机制。在拥有高效算法的同时,让字体看起来很好并不是件小事。试着想象一下你需要多少个三角形来画一堵小小的中文墙。

为什么我们不复制之前使用的光栅化算法,然后粘贴到GPU着色器中呢?在CPU上使用的扫描线算法是非常线性的,并且依赖于以前的结果,这使得它很难并行化。虽然GPU在技术上可以运行它,但这样做将完全消除使用GPU的优势,因为GPU非常擅长运行数千个独立的并行任务,但却非常不擅长运行单个任务。

我们可以预先计算单个字符并将它们放入一个纹理中,基本上创建一个字体图集,如下图所示,而不是用三角形渲染文本。这个纹理然后被上传到GPU,它的片段被映射到由两个三角形组成的正方形上。然而,这仍然不能解决中文文本的问题,因为几乎所有的文字都有不同的字符。纹理最终将是巨大的,这消除了我们使用GPU的优势,否则就不必传输大量数据。Chromium*所做的是为每个网页创建一个新的字体图集,尽管这有其自身的缺点,但如果用户放大,则必须再次进行光栅化,否则字体会显得模糊。

alt 图片

结论

虽然使用GPU来加速所有web页面的呈现会很好,但它并不适合某些任务。在Chromium中解决这个问题的计划是使用带有零拷贝优化的软件(CPU)渲染方法来优化那些速度更快的页面,但在其他情况下使用GPU渲染。我们可以通过一些启发式的方法来决定应该使用哪种方法。例如,如果站点有大量文本(特别是中文或阿拉伯语),我们可以使用CPU,并且在具有许多动画和过渡效果的页面上使用GPU。对于使用它有意义的情况,GPU加速渲染允许更无缝的动画和更好的性能。

[1]. 这是一种简化。平铺不必是正方形,有时可以重叠,甚至在某些动画中移动。平铺通常与层树的结构无关,但有时层可以获得自己的平铺,例如当动画在屏幕上移动时。

*其他名称和品牌可被视为他人的财产。

[2]. 要查看任何页面的可视化效果,请打开Chromium dev tools(Ctrl+Shift+I),显示控制台(按Esc),查看底部的选项卡,然后打开“Rendering”。从那里,检查“显示合成层边界。”