Chromium网页渲染机制介绍

  作为一个浏览器,快速地将网页渲染出来是最重要的工作。Chromium为了做到这一点,费尽了心机,做了大量优化工作。这些优化工作是卓有成效的,代表了当今最先进的网页渲染技术。值得一提的是,这些渲染技术不仅适用于网页渲染,也可以应用在原生系统的UI渲染上。例如,在Android系统上,我们就可以看到两者在渲染技术上的相似之处。本文接下来就对Chromium的网页渲染机制进行简要介绍,并且制定学习计划。

  Chromium的网页渲染机制可以用八个字来描述:纵向分层,横向分块。其中,分层是由WebKit完成的,就是把网页抽象成一系列的Tree。Tree由Layer组成,Layer就是我们所说的层。从前面Chromium网页加载过程简要介绍和学习计划这个系列的文章可以知道,WebKit为网页依次创建了DOM Tree、Render Object Tree、Render Layer Tree和Graphics Layer Tree四棵Tree。其中,与渲染相关的是后面三棵Tree。将网页进行分层,好处有两个。一是减少不必要的绘制操作,二是利用硬件加速渲染动画。

  第一个好处得益于WebKit将网页一帧的渲染分为绘制和合成两个步骤。绘制是将绘图操作转化为图像的过程,合成是将所有图像混合在一起后显示在屏幕上的过程。注意,对于屏幕来说,不管它某一个区域的内容是否发生变化,在它的下一帧显示中,总是需要进行刷新的。这意味着系统总是需要给它一个完整的屏幕内容。考虑这样的一个网页全屏显示的场景,并且网页被抽象为两个层。在下一帧显示中,只有一个层的内容发生了变化。这时候,只需要对内容发生变化的层执行绘制操作即可,然后与另一个层上一帧已经绘制得到的图像进行合成就可以得到整个屏幕的内容。这样就避免了不必要的绘制操作,额外付出的代价是一个合成操作。但是请注意,相对于绘制来说,合成是一个很轻量级的操作,尤其是对硬件加速渲染来说,它仅仅就是一个贴纹理的过程,并且纹理内容本身已经是Ready好了的。第二个好处将某些动画单独放在一层,然后对这个层施加某种变换,从而形成动画。某些变换,例如平移、旋转、缩放,或者Alpha渐变,对硬件加速来说,是轻易实现的。

  与分层相比,分块是一个相对微观的概念,它是针对每一个层而言的。一般来说,一个网页的内容要比屏幕大很多,因此,用户会经常性地进行滚动或者缩放浏览。这种情况在移动设备上表现尤其特出。如果所有内容都是可见的一刻再进行绘制,那么就会让用户觉得很卡顿。另一方面,如果一开始就对网页所有的内容,不管可见还是不可见,都进行绘制,那么就会让用户觉得网页加载很慢,而且会非常耗费资源。这两种方案都不合适。最理想的方式是尽快显示当前可见的内容,并且在有富余劳动力的时候,预先绘制那些接下来最有可能可见的内容。这意味着要赋予一个层的不同区域赋予不同的绘制优先级。每一个区域就是一个块(Tile),每一个层都由若干个块组成。其中,位于当前可见区域中的块的优先级最高的,它们需要最优先进行绘制。

  Threaded Proxy是通过一个调度器(Scheduler)来安排LayerTreeHostImpl对象什么时候该执行什么操作的。调度器又是通过一个状态机(State Machine)来记录LayerTreeHostImpl对象的状态流转的,从而为调度器提供调度安排。典型地,在一个网页的浏览期间,调度器会被依次调度执行以下操作:

  1. 如果还没有创建绘图表面,那么调度器就会发出一个ACTION_BEGIN_OUTPUT_SURFACE_CREATE的操作,这时候LayerTreeHostImpl对象就会为网页创建一个绘图表面。关于网页的绘图表面及其创建过程,可以参考前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文。

  2. 当Layer Tree发生变化需要重绘时,调度器就会发出一个ACTION_SEND_BEGIN_MAIN_FRAME操作,这时候LayerTreeHost对象就会对网页进行重绘。这里有两点需要注意。第一点是这里所说的绘制,实际上只是将绘图命令记录在了一个命令缓冲区中。第二点是绘制操作是在Render Thread中执行的。

  3. LayerTreeHost对象重绘完Layer Tree之后,Render Thread会处于等待状态。接下来调用器会发出一个ACTION_COMMIT操作,通知LayerTreeHostImpl对象将Layer Tree的内容同步到Pending Layer Tree中去。这个同步操作是在Compositor Thread中执行的。同步完成之后,Render Thread就会被唤醒,而Compositor Thread继续对Pending Layer Tree中的分块进行更新,例如更新分块的优先级。

  4. 对Pending Layer Tree中的分块进行更新之后,调度器发出一个ACTION_MANAGE_TILES操作,通知LayerTreeHostImpl对象对Pending Layer Tree中的分块进行光栅化。

  5. Pending Layer Tree完成光栅化操作之后,调度器继续发出一个ACTION_ACTIVATE_PENDING_TREE操作,这时候Pending Layer Tree就变成Active Layer Tree。

  6. Pending Layer Tree就变成Active Layer Tree之后,调度器再发出一个ACTION_DRAW_AND_SWAP_FORCED,这时候LayerTreeHostImpl对象就会将已经光栅化好的分块信息收集起来,并且发送给Browser进程,以便Browser进程可以将这些分块合成在浏览器窗口中显示。这一点可以参考前面Chromium硬件加速渲染的UI合成过程分析一文。

  其中,第2到第6个操作就是网页一帧的完整渲染过程,这个过程在网页的浏览期间不断地重复进行着。

你可能感兴趣的:(Chromium网页渲染机制介绍)