Graphics Layer Tree创建

站在老罗的肩膀上:https://blog.csdn.net/luoshengyang/article/details/50661553Graphics Layer形成一个Graphics Layer Tree。Graphics Layer可看作是一个图形缓冲区,被若干Render Layer共用。本文接下来就分析Graphics Layer Tree的创建过程。网页的Render Lay...
摘要由CSDN通过智能技术生成

站在老罗的肩膀上:https://blog.csdn.net/luoshengyang/article/details/50661553

Graphics Layer形成一个Graphics Layer Tree。Graphics Layer可看作是一个图形缓冲区,被若干Render Layer共用。本文接下来就分析Graphics Layer Tree的创建过程。

网页的Render Layer Tree与Graphics Layer Tree的关系可以通过图1描述,如下所示:

图1 Graphics Layer Tree与DOM Tree、Render Object Tree和Render Layer Tree的关系

       在WebKit中,Graphics Layer又称为Composited Layer。我们可以将Graphics Layer看作是Composited Layer的一种具体实现。这种具体实现是由WebKit的使用者Chromium提供的。Composited Layer描述的是一个具有后端存储的图层,因此可以将它看作是一个图形缓冲区。在软件渲染方式中,这个图形缓冲区就是一块系统内存;在硬件渲染方式中,这个图形缓冲区就是一个OpenGL里面的一个Frame Buffer Object(FBO)。

       Composited Layer涉及到的一个重要概念是“Layer Compositing”。Layer Compositing是现代UI框架普遍采用的一种渲染机制。例如,Android系统的UI子系统(Surface Flinger)就是通过Compositing Surface来获得最终要显示在屏幕上的内容的。这里的Surface就相当于是Chromium的Layer。关于Android系统的Surface Flinger的详细分析,可以参考Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划这个系列的文章。

       Layer Compositing的三个主要任务是:

       1. 确定哪些内容应该在哪些Composited Layer上绘制;

       2. 绘制每一个Composited Layer;

       3. 将所有已经绘制好的Composited Layer再次将绘制在一个最终的、可以显示在屏幕上进行显示的图形缓冲区中。

       其中,第1个任务它完成之后就可以获得一个Graphics Layer Tree,第3个任务要求按照一定的顺序对Composited Layer进行绘制。注意,这个绘制顺序非常重要,否则最终合成出来的UI就会出现不正确的Overlapping。同时,这个绘制顺序对理解Graphics Layer Tree的组成也非常重要。因此,接下来我们首先介绍与这个绘制顺序有关的概念。为了方便描述,本文将上述绘制顺序称为Composited Layer的绘制顺序。

       在介绍Composited Layer的绘制顺序之前,我们还需要回答一个问题:为什么要采用Layer Compositing这种UI渲染机制?主要有两个原因:

       1. 避免不必要的重绘。考虑一个网页有两个Layer。在网页的某一帧显示中,Layer 1的元素发生了变化,Layer 2的元素没有发生变化。这时候只需要重新绘制Layer 1的内容,然后再与Layer 2原有的内容进行Compositing,就可以得到整个网页的内容。这样就可以避免对没有发生变化的Layer 2进行不必要的绘制。

       2. 利用硬件加速高效实现某些UI特性。例如网页的某一个Layer设置了可滚动、3D变换、透明度或者滤镜,那么就可以通过GPU来高效实现。

       在默认情况下,网页元素的绘制是按照Render Object Tree的先序遍历顺序进行的,并且它们在空间上是按照各自的display属性值依次进行布局的。例如,如果一个网页元素的display属性值为"inline",那么它就会以内联元素方式显示,也就是紧挨在前一个绘制的元素的后面进行显示。又如,如果一个网页元素的display属性值为"block",那么它就会以块级元素进行显示,也就是它的前后会各有一个换行符。我们将这种网页元素绘制方式称为Normal Flow或者In Flow。

       有默认情况,就会有例外情况。例如,如果一个网页元素同时设置了position和z-index属性,那么它可能就不会以In Flow的方式进行显示,而是以Out of Flow的方式进行显示。在默认情况下,一个网页元素的position和z-index属性值被设置为“static”和"auto"。网页元素的position属性还可以取值为“relative”、“absolute”和“fixed”,这一类网页元素称为Positioned元素。当一个Positioned元素的z-index属性值不等于"auto"时,它就会以Out of Flow的方式进行显示。

 CSS 2.1规范规定网页渲染引擎要为每一个z-index属性值不等于"auto"的Positioned元素创建一个Stacking Context。对于其它的元素,它们虽然没有自己的Stacking Context,但是它们会与最近的、具有自己的Stacking Context的元素共享同相同的Stacking Context。不同Stacking Context的元素的绘制顺序是不会相互交叉的。

Stacking Context的这个特性,使得它可以成为一个观念上的原子类型绘制层(Atomic Conceptual Layer for Painting)。也就是说,只要我们定义好Stacking Context内部元素的绘制顺序,那么再根据拥有Stacking Context的元素的z-index属性值,那么就可以得到网页的所有元素的绘制顺序。

       我们可以通过图2所示的例子直观地理解Stacking Context的上述特性,如下所示:

  

图2 Stacking Context

 从图1可以看到,Graphics Layer Tree是根据Paint Layer Tree创建的。也就是说,Paint Layer与Graphics Layer存在对应关系,如下所示:

图3 Paint Layer Tree与Graphics Layer的关系

       原则上,Paint Layer Tree中的每一个Paint Layer都对应有一个Composited Layer Mapping,每一个Composited Layer Mapping又包含有若干个Graphics Layer。但是这样将会导致创建大量的Graphics Layer。创建大量的Graphics Layer意味着需要耗费大量的内存资源。这个问题称为”Layer Explosion“问题。

       为了解决“Layer Explosion”问题,每一个需要创建Composited Layer Mapping的Render Layer都需要给出一个理由。这个理由称为“Compositing Reason”,它描述的实际上是Render Layer的特征。例如,如果一个Paint Layer关联的LayoutObject设置了3D Transform属性,那么就需要为该Paint Layer创建一个Composited Layer Mapping

       Blink一共定义了54个Compositing Reason,如下所示:

#define FOR_EACH_COMPOSITING_REASON(V)                                        \
  /* Intrinsic reasons that can be known right away by the layer. */          \
  V(3DTransform)                                                              \
  ...                                                                         \
  V(InlineTransform)

// generate shift bits via enum
 enum {
#define V(name) kE##name,
    FOR_EACH_COMPOSITING_REASON(V)
#undef V
};

// shift for each compositing reason
#define V(name) k##name = UINT64_C(1) << kE##name,
    FOR_EACH_COMPOSITING_REASON(V)
#undef V

// need to squash
kComboSquashableReasons =
        kOverlap | kAssumedOverlap | kOverflowScrollingParent,

如果启用了Overlap Testing,那么Blink会根据上述的Stacking Context顺序计算每一个Paint Layer的后面是否有其它的Paint Layer与其重叠。如果有,并且与其重叠的Paint Layer有一个对应的Composited Layer Mapping,那么就会将位于上面的Paint Layer的Compositing Reason设置为kOverlap。

       如果没有启用Overlap Testing,那么WebKit会根据上述的Stacking Context顺序检查每一个Render Layer的后面是否有一个具有Composited Layer Mapping的Paint Layer。只要有,不管它们是否重叠,那么就会将位于上面的Paint Layer的Compositing Reason设置为kAssumedOverlap。

       最后,如果一个Paint Layer包含在一个具有overflow属性为"scroll"的Render Block中,并且该Layout Block所对应的Paint Layer具有Composited Layer Mapping,那么该Paint Layer的Compositing Reason就会被设置为kOverflowScrollingParent。

       Blink会将位于一个具有Composited Layer Mapping的Render Layer的上面的那些有着Squashable Reason的Paint Layer绘制在同一个Graphics Layer中。这种Graphics Layer称为Squashing Graphics Layer。这种机制也相应地称为“Layer Squashing”。通过Layer Squashing机制,就可以在一定程度上减少Graphics Layer的数量,从而在一定程度上解决“Layer Explosion”问题。

       我们思考一下,为什么WebKit会将具有上述3种Compositing Reason的Render Layer绘制在一个Squashing Graphics Layer中?考虑具有kOverlap和kAssumedOverlap的Render Layer,当它们需要重绘,或者它们下面的具有Composited Layer Mapping的Render Layer重绘时,都不可避免地对它们以及它们下面的具有Composited Layer Mapping的Paint Layer进行Compositing。这是由于它们相互之间存在重叠区域,只要其中一个发生变化,就会牵一发而动全身。类似地,当一个overflow属性为"scroll"的Layout Block滚动时,包含在该Layout Block内的Paint Layer在执行完成重绘操作之后,需要参与到Compositing操作去。

       Blink定义了两个函数,用来判断一个Paint Layer是需要Compositing还是Squashing,如下所示:

// Any reasons other than overlap or assumed overlap will require the layer to
// be separately compositing.
inline bool RequiresCompositing(CompositingReasons reasons) {
  return reasons & ~CompositingReason::kComboSquashableReasons;
}

// If the layer has overlap or assumed overlap, but no other reasons, then it
// should be squashed.
inline bool RequiresSquashing(CompositingReasons reasons) {
  return !RequiresCompositing(reasons) &&
         (reasons & CompositingReason::kComboSquashableReasons);
}

参数reasons描述的是一个Paint Layer的Compositing Reason,函数requiresCompositing判断该Paint Layer是否需要Compositing,也就是是否要为该Render Layer创建一个Composited Layer Mapping,而函数RequiresSquashing判断该Paint Layer需要Squashing,也就是绘制在一个Squashing Graphics Layer中。

      除了Compositing Paint Layer和Squashing Paint Layer,剩下的其它Paint Layer称为Non-Compositing Render Layer。这些Render Layer将会与离其最近的具有Composited Layer Mapping的父Paint Layer绘制同样的Graphics Layer中

      Blink是根据Graphics Layer Tree来绘制网页内容的。在绘制一个Graphics Layer的时候,除了绘制Graphics Layer本身所有的内容之外,还会在Paint Layer Tree中。找到与该Graphics Layer对应的Paint Layer,并且从该Paint Layer开始,将那些Non-Compositing类型的子Paint Layer也一起绘制,直到遇到一个具有Composited Layer Mapping的子Paint Layer为止。这个过程在后面的文章中分析网页内容的绘制过程时就会看到。

      前面提到,Composited Layer Mapping包含有若干个Graphics Layer,这些Graphics Layer在Composited Layer Mapping,也是形成一个Graphics Layer Sub Tree的,如图4所示:

图4 Composited Layer Mapping

       图4的左边是一个Render Layer Tree。其中红色的Render Layer有对应的Composited Layer Mapping。每一个Composited Layer Mapping内部都有一个Graphics Layer Sub Tree。同时,这些Graphics Layer Sub Tree又会组合在一起,从而形成整个网页的Graphics Layer Tree。

       一个典型的Composited Layer Mapping对应的部分Graphics Layer Sub Tree如图5所示:

图5 一个Composited Layer Mapping对应的Graphics Layer Sub Tree的一部分

       注意,图5描述的是仅仅是一个Composited Layer Mapping对应的Graphics Layer Sub Tree的一部分。例如,如果拥有该Composited Layer Mapping的Render Layer的上面存在Squashing Paint Layer,那么上述Graphics Layer Sub Tree还包含有一个Squashing Graphics Layer。不过这一部分Graphics Layer Sub Tree已经足于让我们理解Composited Layer Mapping的组成。

       在图5中,只有Main Layer是必须存在的,它用来绘制一个Paint Layer自身的内容。其它的Graphics Layer是可选,其中:

       1. 如果一个Render Layer被父Render Layer设置了裁剪区域,那么就会存在Clip Layer。

       2. 如果一个Render Layer为子Render Layer设置了裁剪区域,那么就会存在Children Clip Layer。

       3. 如果一个Render Layer是可滚动的,那么就会存在Scrolling Container。

      4. Negative z-order children、Normal flow children和Positive z-order children描述的是按照Stacking Context规则排序的子Paint Layer对应的Composited Layer Mapping描述的Graphics Layer Sub Tree,它们均以父Paint Layer的Scrolling Container为父Graphics Layer。

      5. 如果一个Paint Layer是根Paint Layer,并且它的背景被设置为固定的,即网页的body标签的CSS属性background-attachment被设置为“fixed”,那么就会存在Background Layer。

       6. 当Negative z-order children存在时,就会存在Foreground Layer。从前面描述的Stacking Context规则可以知道,Negative z-order children对应的Graphics Layer Sub Tree先于当前Graphics Layer Sub Tree绘制。Negative z-order children对应的Graphics Layer Sub Tree在绘制的时候可能会设置了偏移位置。这些偏移位置不能影响后面的Normal flow children和Positive z-order children对应的Graphics Layer Sub Tree的绘制,因此就需要在中间插入一个Foreground Layer,用来抵消Negative z-order children对应的Graphics Layer Sub Tree设置的偏移位置。

       7. 如果一个Paint Layer的上面存在Squashing Paint Layer,那么就会存在Squashing Layer。

       了解了Composited Layer Mapping对应的Graphics Layer Sub Tree的结构之后,接下来我们就可以结合源码分析网页的Graphics Layer Tree的创建过程了。网页的Graphics Layer Tree的创建主要是分三步进行:

       1. 计算各个Paint Layer Tree中的Paint Layer的Compositing Reason;

       2. 为有需要的Paint Layer创建Composited Layer Mapping;

       3. 将各个Composited Layer Mapping描述Graphics Layer Sub Tree连接起来形成Graphics Layer Tree。

Graphic Layer通过GraphicLayerTreeBuilder::Rebuild创建,调用栈如下


  1. 创建Composited Layer Mapping   

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值