原文地址:software.intel.com/content/www…
原文作者:
发布时间:2016年02月05日 最后更新时间:2016年02月04日
本文是对网页浏览器将网站信息栅格化为你能看到的实际像素的方法的总体概述。当网络浏览器下载一个页面时,它会解析源码并创建DOM。然后,它需要弄清楚哪些图像/文本/框架要显示在哪里。这些信息在内部被表示为层树。你可以把层树看成是简化的DOM树,因为它们只包含页面的可见元素,并且可以把一些DOM元素组合在一起,形成一个单一的层。
想象一下,你已经有了图层树,你想在屏幕上显示它们。现在,每个图层已经包含了如何绘制它的信息,所以你只需要按照正确的顺序调用正确的绘制说明。然而,每次用户滚动或播放动画时,你都必须对整个页面进行这样的操作,这显然是低效的。因此,我们将页面划分为瓷砖,组成大小约为256x256像素的方块网格[1],你可以在下图中看到这些瓷砖,瓷砖的边界使用Chromium*开发工具可视化[2]。
栅格化单个瓷砖比栅格化整个网站更容易,因为我们可以忽略那些在受影响的瓷砖上不可见的绘画命令。如果用户点击磁贴或播放一些动画,只有受影响的磁贴需要再次栅格化。在简单的网页上,磁贴通常只需栅格化一次,但交互性较强的网站或有动画的网站可能会导致一些磁贴每一帧都要重新计算。
栅格化一个磁贴主要有两种方法--老的方法是在CPU上完成,然后以纹理的形式发送给GPU;新的方法是使用GPU与OpenGL进行栅格化。每种方法都有自己的优缺点,每种方法都最适合某些类型的网页。GPU光栅化不会很快取代软件(CPU)光栅化,我在后面会解释。
软件(CPU)光栅化
Chromium使用Skia库进行光栅化,最终使用scanline算法来创建位图。通常情况下,如果要将结果发送到GPU上绘制,我们可以通过调用glTexImage2D()来上传,但Chromium的安全模型让它变得更加复杂。
由于渲染进程是沙盒式的,不能直接访问GPU,所以Chromium使用了一个单独的GPU进程,其主要目的是在渲染进程和GPU之间充当代理,接受OpenGL命令并传递给图形驱动。因此,我们必须将栅格化的结果放到共享内存中,并向Chromium GPU进程发送一条消息,让它调用glTexImage2D()。
这有一个明显的缺点--每次磁贴变化时都要进行上传,这意味着要传输大量的数据,GPU进程会非常忙碌。简单的网页不会受此影响太大,但使用大量动画或JavaScript效果的交互式网页,例如,几乎每一帧都必须重新绘制(每秒高达60次)。这在移动设备上尤其是一个问题,因为屏幕较小,设计师通常会将元素隐藏起来,直到用户提出要求,这就会产生很多过渡效果。
零拷贝的软件光栅化
零拷贝纹理上传是由我的同事Dongseong Hwang和Tiago Vignatti完成的一项优化。它试图最大限度地减少每次更换瓷砖时将纹理上传到GPU的低效过程。栅格化的方式和之前的一样,但我们并没有在每次更换磁贴时用glTexImage2D()手动上传纹理,而是告诉GPU在主内存中内存映射纹理的位置,让GPU直接读取它们。这样一来,Chromium GPU进程只需要做初始的内存映射设置,之后就可以保持空闲。这提高了移动设备的性能,并节省了大量的电池寿命。
GPU光栅化
通过GPU光栅化,部分工作负载从CPU转移到GPU,。所有的多边形都必须使用OpenGL基元(三角形和线条)进行渲染,这也是由Skia通过名为Skia Ganesh的GPU后端执行的。这也是由Skia通过称为Skia Ganesh的GPU后端执行的。结果从不保存在主内存中,因此不必复制到任何地方,因为这一切都发生在 GPU 上。虽然这略微减少了主内存的使用,但会使用更多的GPU内存。
GPU栅格化的主要问题是字体,或者其他小而复杂的形状。OpenGL没有任何原生的文本渲染基元,所以你必须使用现有的库(或者痛苦地实现你自己的库),它可以使用各种方法对它们进行光栅化。例如,使用三角形来表示字符,使用预计算的纹理,或者其他机制。要让字体好看的同时,还要有一个高效的算法,这不是小事。试着想象一下,你需要多少个三角形才能画出一堵小小的中文字。
我们为什么不把之前用的栅格化算法复制过来,然后粘贴到GPU着色器上呢?CPU上使用的扫描线算法是非常线性的,并且依赖于之前的结果,这使得它很难并行化。虽然GPU在技术上可以运行它,但这样做会完全消除当初使用GPU的优势,因为GPU非常擅长运行成千上万个独立的并行任务,但非常不擅长运行单个任务。
与其用三角形渲染文本,我们可以预先计算单个字符,并将它们放入纹理中,本质上创建一个字体图谱,如下图所示。然后这个纹理会被上传到GPU上,它的碎片会被映射到由两个三角形组成的方块上。然而这仍然不能解决我们对中文文本的问题,因为它几乎有不同的字符。纹理最终会变得很大,这就消除了我们使用GPU的优势,否则就不用传输大量数据了。Chromium*做的是为每个网页创建一个新的字体图谱,不过这也有自己的缺点--如果用户放大,就必须再次进行光栅化,否则字体会显得很模糊。
(CC来源)
结论
虽然利用GPU来加快所有网页的渲染速度是很好的,但就是不适合某些任务。在Chromium中解决这个问题的计划是,对于零拷贝优化的网页,在该方法比较快的情况下,使用软件(CPU)渲染,但在其他情况下使用GPU渲染。我们可以通过一些启发式的方法来确定应该使用哪种方法。例如,如果网站有很多文字(尤其是中文或阿拉伯文),我们可以使用CPU,而对于有很多动画和过渡效果的页面则使用GPU。对于有意义的情况下,使用GPU加速渲染可以让动画效果更完美,性能更好。
1 这是一种简化。瓷砖不一定是正方形,有时可能会在一些动画中重叠甚至移动。瓷砖通常独立于图层树的结构,但有时一个图层可以得到自己的瓷砖,例如当动画在屏幕上移动它时。
- 其他名称和品牌可能会被声称是他人的财产。
2 要查看任何页面的可视化,打开Chromium开发工具(Ctrl+Shift+I),显示控制台(按Esc键),查看底部的标签,打开 "渲染"。从那里,勾选 "显示合成图层边框"。