渲染基础

308 阅读6分钟

此内容为笔者学习和自我总结,有错误的地方请谅解和指出,笔者定会及时更正。学习的路上,一起互助前行。

——————————————————————————————————————————

我们回顾下网页渲染的简单过程: html--->构建DOM树|构建CssOM树-> RenderObject树---(compute & layout)--->RenderLayer树----a:-->paint--->graphicsContext(绘制上下文)--->位图 || b:(renderLayerBacking对象:ps在硬件加速阶段)----GraphicsTree(graphicsLayer对象)->合成层(后端存储)----->位图--(Composite)-->合并位图

关于‘基础’,我们知道,‘基础’则是上层所需要的,完备的资源。在上面的流程中我们看到,RenderObject 树是第一个包含DOM节点和css属性‘逻辑树’,而网页渲染的资源就是dom节点和对应节点的css属性。(js也只是操作dom树的。以及执行浏览器相关事件。这里是针对渲染

  1. RenderObject树
    一个DOM树对象保存了为绘制DOM节点所需要的各种信息。(比如样式布局信息,这些信息经过webkit的处理之后,RenderObject对象就知道如何绘制自己了)
    这些RenderObject对象所构成的树,就做RenderObject树。

    DOM树节点和RenderObject树节点并不是一一对应的关系。

    那什么情况下,DOM树节点会创建一个新的RenderObject对象呢?
    1)DOM树的document节点。 2)DOM树的可视化节点。 3) 某些情况下为了webkit处理需要所创建的匿名RenderObject节点。该节点不对应于DOM树中的节点。比如匿名的RenderBlock节点。

    什么是RenderBlock节点? 简单理解为块元素节点。为了处理上的方便,Webkit在某些情况下需要建立的匿名RenderBlock对象,因为这种对象的子女必须都是内嵌的元素或者都是非内嵌的元素。所以当RenderBlock对象包含两种元素的时候,Webkit会为相邻的内嵌元素创建一个块节点,也就是RenderBlock对象,然后设置该对象为原先内嵌元素父亲的子女,最后设置这些内嵌元素为RenderBlock对象子女。

    创建一个RenderObject节点的RenderObject类函数,包含以下几类函数: 1)遍历和修改RenderObject树所涉及的函数。遍历如:parent(),firstChild(),nextSibling(),previousSibling()等。修改如addChild(),removeChild();

     2)用来计算布局和获取布局相关信息的函数:如layout(),style().enClosingBox();  
    
     3)判断RenderObject对象所属类型。知道类型后做相应类型的转变。   
    
     4)关于RenderObject对象与相关的RenderLayer对象相关的操作。具体不如:???   
    
     5)坐标和绘图相关的操作,WebKit使用这些操作让RenderObject对象将内容传入的绘制结果对象中。例如:paint(),repaint()等;  
    

    RenderObject节点种类:
    1)RenderView 节点,RenderObject树的根节点。对应 DOM树的HTMLDocument节点
    2)RenderBlock 节点,块节点,对应DOM树的HTMLHtmlElement节点,某些只是表示层级关系的div节点。
    3)RenderBody节点,对应DOM树的HTMLBodyElement节点
    4)RenderHTMLCanvas节点:对应canvas.
    ...

    需要注意 DOM树的head节点并没有对应的RenderObject对象,因为它是非可视化元素。

总结一下RenderObject树的功能: 1)包含DOM和CssOM的属性信息。 2)根据以上信息提供计算布局的能力,包括遍历,获取布局相关属性,获取坐标,绘图等。

  1. RenderLayer树
    什么是RenderLayer树: WebKit根据需要把对一些RenderObject节点建立新的RenderLayer对象,并且该enderObject节点下的子女都属于RenderLayer对象(除非这些子女中有节点单独创建了新的RenderLayer对象)。根据enderObject树中的enderObject节点创建的RenderLayer对象集合也存在类似enderObject树的关系结构,所以称为RenderLayer树。

    为什么要建立RenderLayer树
    上面对于RenderLayer树的说明,我们可以理解,RenderLayer树是把更为复杂的RenderObject树信息进行整合,减小了关注数量,对于整体计算来说,效率是提高了的。布局绘制都可以以合理的单位级(RenderLayer对象)来处理。

    什么情况下一个RenderObject节点需要新建一个RenderLayer对象?
    1)DOM树的Document节点对应的RenderView节点。
    2)DOM树的子女节点,也就是HTML 对应的RenderBlock节点。 3)有透明效果的 RenderObject 节点。 4)显式的指定 CSS 位置的RenderObject节点。
    5)节点有overflow,alpha,reflect 等效果的RenderObject节点。 6)使用Canvas技术的RenderObject节点。
    7)Video 节点对应的RenderObject节点。

  2. 绘制上下文 GraphicsContext
    提供基本绘制单元的绘制接口以及设置绘图的样式。 绘图接口包括:画点,画图,画多边形,画文字等。 RenderObject对象调用的绘图函数,实际是调用绘制上下文提供的函数集合。

  3. 渲染方式
    1)软件绘制:
    使用CPU来完成绘图工作。

     如本文一开始a路径描述,软件绘制按‘每层信息’,从下往上绘制,可以绘制在一个层级上,就没有合并图层的说法。当然也可以绘制在多个层上,但没有必要。  
     无论是一个图层还是多个图层,软件绘制的方式对于变动的样式,都是先计算变动区域,然后在变动区域层次上重新绘制。多个图层的化,还需要考虑区分图层以及图层的合并算法。   
    

    软件绘制某一个节点的简要过程:
    1.先绘制该节点背景(不包括轮廓)。
    2.绘制节点内容。如果有z-index<0的子女,递归的从下到上绘制这些子女。
    3.绘制上一步子女的轮廓。
    4.绘制浮动元素;
    5.递归的绘制z-index>0的子女元素。
    6.绘制该节点轮廓。

    以上绘制步骤,不是绝对的,根据浏览器不同,主要的目的是绘制的效率,以及权衡资源的占用。

    2)硬件绘制: 使用GPU来完成绘图工作。

    优点和选择:
    1)软件绘制可以使用CPU来缓存机制来重新绘制的开销,一定情况下减少内存开销。
    2)在更新操作时,软件绘制需要先根据新的树,遍历找出涉及到变动层次节点以及变动属性,再依次绘制这些层次的变动区域。
    而硬件加速使用GPU,会在GPU中存储每个层级的后端存储,只需要重新绘制涉及层次,不需要遍历和比较计算。但可能重新绘制变更涉及到的一层或多层,以及合成,可能内存开支更大,效率更低。所以什么情况下应用硬件加速需要更合理的策略。
    3)GPU的优势在处理能力强,缺点在成本高,内存资源相比CPU少。所以,如果网页没有特别需要计算的地方,如:view,canvas,动画等。可尽量使用软件渲染(没有必要不使用css开启硬件渲染)。需要的地方,如3D渲染等,应当使用硬件加速来提高效率。