1、Chromium网页GraphicsLayerTree创建过程分析全解Chromium网页Graphics Layer Tree创建过程分析在前面一文中,我们分析了网页Render Layer Tree的创建过程。在创建Render Layer的同时,WebKit还会为其创建Graphics Layer。这些Graphics Layer形成一个Graphics Layer Tree。Graphics Layer可看作是一个图形缓冲区,被若干Render Layer共用。本文接下来就分析Graphics Layer Tree的创建过程。网页的Render Layer Tree与Graphics
2、Layer Tree的关系可以通过图1描述,如下所示:在WebKit中,Graphics Layer又称为Composited Layer。我们可以将Graphics Layer看作是Composited Layer的一种具体实现。这种具体实现是由WebKit的使用者Chromium提供的。Composited Layer描述的是一个具有后端存储的图层,因此可以将它看作是一个图形缓冲区。在软件渲染方式中,这个图形缓冲区就是一块系统内存;在硬件渲染方式中,这个图形缓冲区就是一个OpenGL里面的一个Frame Buffer Object(FBO)。 Composited Layer涉及到的一个重
3、要概念是“Layer Compositing”。Layer Compositing是现代UI框架普遍采用的一种渲染机制。例如,Android系统的UI子系统(Surface Flinger)就是通过Compositing Surface来获得最终要显示在屏幕上的内容的。这里的Surface就相当于是Chromium的Layer。关于Android系统的Surface Flinger的详细分析,可以参考这个系列的文章。 Layer Compositing的三个主要任务是: 1. 确定哪些内容应该在哪些Composited Layer上绘制; 2. 绘制每一个Composited Layer; 3
4、. 将所有已经绘制好的Composited Layer再次将绘制在一个最终的、可以显示在屏幕上进行显示的图形缓冲区中。 其中,第1个任务它完成之后就可以获得一个Graphics Layer Tree,第3个任务要求按照一定的顺序对Composited Layer进行绘制。注意,这个绘制顺序非常重要,否则最终合成出来的UI就会出现不正确的Overlapping。同时,这个绘制顺序对理解Graphics Layer Tree的组成也非常重要。因此,接下来我们首先介绍与这个绘制顺序有关的概念。为了方便描述,本文将上述绘制顺序称为Composited Layer的绘制顺序。 在介绍Composited
5、 Layer的绘制顺序之前,我们还需要回答一个问题:为什么要采用Layer Compositing这种UI渲染机制?主要有两个原因: 1. 避免不必要的重绘。考虑一个网页有两个Layer。在网页的某一帧显示中,Layer 1的元素发生了变化,Layer 2的元素没有发生变化。这时候只需要重新绘制Layer 1的内容,然后再与Layer 2原有的内容进行Compositing,就可以得到整个网页的内容。这样就可以避免对没有发生变化的Layer 2进行不必要的绘制。 2. 利用硬件加速高效实现某些UI特性。例如网页的某一个Layer设置了可滚动、3D变换、透明度或者滤镜,那么就可以通过GPU来高效
6、实现。 在默认情况下,网页元素的绘制是按照Render Object Tree的先序遍历顺序进行的,并且它们在空间上是按照各自的display属性值依次进行布局的。例如,如果一个网页元素的display属性值为inline,那么它就会以内联元素方式显示,也就是紧挨在前一个绘制的元素的后面进行显示。又如,如果一个网页元素的display属性值为block,那么它就会以块级元素进行显示,也就是它的前后会各有一个换行符。我们将这种网页元素绘制方式称为Normal Flow或者In Flow。 有默认情况,就会有例外情况。例如,如果一个网页元素同时设置了position和z-index属性,那么它可能
7、就不会以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。对于其
8、它的元素,它们虽然没有自己的Stacking Context,但是它们会与最近的、具有自己的Stacking Context的元素共享同相同的Stacking Context。不同Stacking Context的元素的绘制顺序是不会相互交叉的。假设有两个Stacking Context,一个包含有A和B两个元素,另一个包含有C和D两个元素,那么A、B、C和D四个元素的绘制顺序只可能为: 1. A、B、C、D 2. B、A、C、D 3. A、B、D、C 4. B、A、D、C 5. C、D、A、B 6. C、D、B、A 7. D、C、A、B 8. D、C、B、A Stacking Context
9、的这个特性,使得它可以成为一个观念上的原子类型绘制层(Atomic Conceptual Layer for Painting)。也就是说,只要我们定义好Stacking Context内部元素的绘制顺序,那么再根据拥有Stacking Context的元素的z-index属性值,那么就可以得到网页的所有元素的绘制顺序。 我们可以通过图2所示的例子直观地理解Stacking Context的上述特性,如下所示:在图2的左边,一共有4个Stacking Context。最下面的Stacking Context的z-index等于-1;中间的Stacking Context的z-index等于0;
10、最上面的Stacking Context的z-index等于1,并且嵌套了另外一个z-index等于6的Stacking Context。我们观察被嵌套的z-index等于6的Stacking Context,它包含了另外一个z-index也是等于6的元素,但是这两个z-index的含义是不一样的。其中,Stacking Context的z-index值是放在父Stacking Context中讨论才有意义,而元素的z-index放在当前它所在的Stacking Context讨论才有意义。再者,我们是下面和中间的两个Stacking Context,虽然它们都包含有三个z-index分别等于
11、7、8和9的元素,但是它们是完全不相干的。 如果我们将图2左边中间的Stacking Context的z-index修改为2,那么它就会变成最上面的Stacking Context,并且会重叠在z-index等于1的Stacking Context上,以及嵌套在这个Stacking Context里面的那个Stacking Context。 这样,我们就得到了Stacking Context的绘制顺序。如前所述,接下来只要定义好Stacking Context内的元素的绘制顺序,那么就可以网页的所有元素的绘制顺序。Stacking Context内的元素的绘制顺序如下所示: 1. 背景(Bac
12、kgrounds)和边界(Borders),也就是拥有Stacking Context的元素的背景和边界。 2. Z-index值为负数的子元素。 3. 内容(Contents),也就是拥有Stacking Context的元素的内容。 4. Normal Flow类型的子元素。 5. Z-index值为正数的子元素。 以上就是与Composited Layer的绘制顺序有关的背景知识。这些背景知识在后面分析Graphics Layer Tree的创建过程时就会用到。 从图1可以看到,Graphics Layer Tree是根据Render Layer Tree创建的。也就是说,Render
13、Layer与Graphics Layer存在对应关系,如下所示:原则上,Render Layer Tree中的每一个Render Layer都对应有一个Composited Layer Mapping,每一个Composited Layer Mapping又包含有若干个Graphics Layer。但是这样将会导致创建大量的Graphics Layer。创建大量的Graphics Layer意味着需要耗费大量的内存资源。这个问题称为”Layer Explosion“问题。 为了解决“Layer Explosion”问题,每一个需要创建Composited Layer Mapping的Rende
14、r Layer都需要给出一个理由。这个理由称为“Compositing Reason”,它描述的实际上是Render Layer的特征。例如,如果一个Render Layer关联的Render Object设置了3D Transform属性,那么就需要为该Render Layer创建一个Composited Layer Mapping。 WebKit一共定义了54个Compositing Reason,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片/ Intrinsic reasons that can be known right away by t
15、he layer const uint64_t CompositingReason3DTransform = UINT64_C(1) 0; const uint64_t CompositingReasonVideo = UINT64_C(1) 1; const uint64_t CompositingReasonCanvas = UINT64_C(1) 2; const uint64_t CompositingReasonPlugin = UINT64_C(1) 3; const uint64_t CompositingReasonIFrame = UINT64_C(1) 4; const u
16、int64_t CompositingReasonBackfaceVisibilityHidden = UINT64_C(1) 5; const uint64_t CompositingReasonActiveAnimation = UINT64_C(1) 6; const uint64_t CompositingReasonTransitionProperty = UINT64_C(1) 7; const uint64_t CompositingReasonFilters = UINT64_C(1) 8; const uint64_t CompositingReasonPositionFix
17、ed = UINT64_C(1) 9; const uint64_t CompositingReasonOverflowScrollingTouch = UINT64_C(1) 10; const uint64_t CompositingReasonOverflowScrollingParent = UINT64_C(1) 11; const uint64_t CompositingReasonOutOfFlowClipping = UINT64_C(1) 12; const uint64_t CompositingReasonVideoOverlay = UINT64_C(1) 13; co
18、nst uint64_t CompositingReasonWillChangeCompositingHint = UINT64_C(1) 14; / Overlap reasons that require knowing whats behind you in paint-order before knowing the answer const uint64_t CompositingReasonAssumedOverlap = UINT64_C(1) 15; const uint64_t CompositingReasonOverlap = UINT64_C(1) 16; const
19、uint64_t CompositingReasonNegativeZIndexChildren = UINT64_C(1) 17; const uint64_t CompositingReasonScrollsWithRespectToSquashingLayer = UINT64_C(1) 18; const uint64_t CompositingReasonSquashingSparsityExceeded = UINT64_C(1) 19; const uint64_t CompositingReasonSquashingClippingContainerMismatch = UIN
20、T64_C(1) 20; const uint64_t CompositingReasonSquashingOpacityAncestorMismatch = UINT64_C(1) 21; const uint64_t CompositingReasonSquashingTransformAncestorMismatch = UINT64_C(1) 22; const uint64_t CompositingReasonSquashingFilterAncestorMismatch = UINT64_C(1) 23; const uint64_t CompositingReasonSquas
21、hingWouldBreakPaintOrder = UINT64_C(1) 24; const uint64_t CompositingReasonSquashingVideoIsDisallowed = UINT64_C(1) 25; const uint64_t CompositingReasonSquashedLayerClipsCompositingDescendants = UINT64_C(1) 26; / Subtree reasons that require knowing what the status of your subtree is before knowing
22、the answer const uint64_t CompositingReasonTransformWithCompositedDescendants = UINT64_C(1) 27; const uint64_t CompositingReasonOpacityWithCompositedDescendants = UINT64_C(1) 28; const uint64_t CompositingReasonMaskWithCompositedDescendants = UINT64_C(1) 29; const uint64_t CompositingReasonReflectio
23、nWithCompositedDescendants = UINT64_C(1) 30; const uint64_t CompositingReasonFilterWithCompositedDescendants = UINT64_C(1) 31; const uint64_t CompositingReasonBlendingWithCompositedDescendants = UINT64_C(1) 32; const uint64_t CompositingReasonClipsCompositingDescendants = UINT64_C(1) 33; const uint6
24、4_t CompositingReasonPerspectiveWith3DDescendants = UINT64_C(1) 34; const uint64_t CompositingReasonPreserve3DWith3DDescendants = UINT64_C(1) 35; const uint64_t CompositingReasonReflectionOfCompositedParent = UINT64_C(1) 36; const uint64_t CompositingReasonIsolateCompositedDescendants = UINT64_C(1)
25、37; / The root layer is a special case that may be forced to be a layer, but also it needs to be / a layer if anything else in the subtree is composited. const uint64_t CompositingReasonRoot = UINT64_C(1) 38; / CompositedLayerMapping internal hierarchy reasons const uint64_t CompositingReasonLayerFo
26、rAncestorClip = UINT64_C(1) 39; const uint64_t CompositingReasonLayerForDescendantClip = UINT64_C(1) 40; const uint64_t CompositingReasonLayerForPerspective = UINT64_C(1) 41; const uint64_t CompositingReasonLayerForHorizontalScrollbar = UINT64_C(1) 42; const uint64_t CompositingReasonLayerForVertica
27、lScrollbar = UINT64_C(1) 43; const uint64_t CompositingReasonLayerForScrollCorner = UINT64_C(1) 44; const uint64_t CompositingReasonLayerForScrollingContents = UINT64_C(1) 45; const uint64_t CompositingReasonLayerForScrollingContainer = UINT64_C(1) 46; const uint64_t CompositingReasonLayerForSquashi
28、ngContents = UINT64_C(1) 47; const uint64_t CompositingReasonLayerForSquashingContainer = UINT64_C(1) 48; const uint64_t CompositingReasonLayerForForeground = UINT64_C(1) 49; const uint64_t CompositingReasonLayerForBackground = UINT64_C(1) 50; const uint64_t CompositingReasonLayerForMask = UINT64_C(
29、1) 51; const uint64_t CompositingReasonLayerForClippingMask = UINT64_C(1) 52; const uint64_t CompositingReasonLayerForScrollingBlockSelection = UINT64_C(1) 53; 这些Compositing Reason定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/CompositingReasons.h中。 其中,有3个Compositing Reason比较
30、特殊,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片const uint64_t CompositingReasonComboSquashableReasons = CompositingReasonOverlap | CompositingReasonAssumedOverlap | CompositingReasonOverflowScrollingParent; 它们是CompositingReasonOverlap、CompositingReasonAssumedOverlap和CompositingReasonOverflowScrollin
31、gParent,称为Squashable Reason。WebKit不会为具有这三种特征的Render Layer之一的Render Layer创建Composited Layer Mapping。 如果启用了Overlap Testing,那么WebKit会根据上述的Stacking Context顺序计算每一个Render Layer的后面是否有其它的Render Layer与其重叠。如果有,并且与其重叠的Render Layer有一个对应的Composited Layer Mapping,那么就会将位于上面的Render Layer的Compositing Reason设置为CompositingReasonOverlap。 如果没有启用Overlap Testing,那么WebKit会根据上述的Stacking Context顺序检查每一个Render Layer的后面是否有一个具有Composited Layer Mapping的Render Layer。只要有,不管它们是否重叠,那么就会将位于上面的Render Layer的Compositing Reason设置为CompositingReasonAssumedOverlap。 最后,如果一个Render Layer包含在一个具有overflow属性为scrol
copyright@ 2008-2023 冰点文库 网站版权所有
经营许可证编号:鄂ICP备19020893号-2