Chromium网页Graphics Layer Tree创建过程分析.docx
《Chromium网页Graphics Layer Tree创建过程分析.docx》由会员分享,可在线阅读,更多相关《Chromium网页Graphics Layer Tree创建过程分析.docx(63页珍藏版)》请在冰豆网上搜索。
Chromium网页GraphicsLayerTree创建过程分析
Chromium网页GraphicsLayerTree创建过程分析
在前面一文中,我们分析了网页RenderLayerTree的创建过程。
在创建RenderLayer的同时,WebKit还会为其创建GraphicsLayer。
这些GraphicsLayer形成一个GraphicsLayerTree。
GraphicsLayer可看作是一个图形缓冲区,被若干RenderLayer共用。
本文接下来就分析GraphicsLayerTree的创建过程。
网页的RenderLayerTree与GraphicsLayerTree的关系可以通过图1描述,如下所示:
在WebKit中,GraphicsLayer又称为CompositedLayer。
我们可以将GraphicsLayer看作是CompositedLayer的一种具体实现。
这种具体实现是由WebKit的使用者Chromium提供的。
CompositedLayer描述的是一个具有后端存储的图层,因此可以将它看作是一个图形缓冲区。
在软件渲染方式中,这个图形缓冲区就是一块系统内存;在硬件渲染方式中,这个图形缓冲区就是一个OpenGL里面的一个FrameBufferObject(FBO)。
CompositedLayer涉及到的一个重要概念是“LayerCompositing”。
LayerCompositing是现代UI框架普遍采用的一种渲染机制。
例如,Android系统的UI子系统(SurfaceFlinger)就是通过CompositingSurface来获得最终要显示在屏幕上的内容的。
这里的Surface就相当于是Chromium的Layer。
关于Android系统的SurfaceFlinger的详细分析,可以参考这个系列的文章。
LayerCompositing的三个主要任务是:
1.确定哪些内容应该在哪些CompositedLayer上绘制;
2.绘制每一个CompositedLayer;
3.将所有已经绘制好的CompositedLayer再次将绘制在一个最终的、可以显示在屏幕上进行显示的图形缓冲区中。
其中,第1个任务它完成之后就可以获得一个GraphicsLayerTree,第3个任务要求按照一定的顺序对CompositedLayer进行绘制。
注意,这个绘制顺序非常重要,否则最终合成出来的UI就会出现不正确的Overlapping。
同时,这个绘制顺序对理解GraphicsLayerTree的组成也非常重要。
因此,接下来我们首先介绍与这个绘制顺序有关的概念。
为了方便描述,本文将上述绘制顺序称为CompositedLayer的绘制顺序。
在介绍CompositedLayer的绘制顺序之前,我们还需要回答一个问题:
为什么要采用LayerCompositing这种UI渲染机制?
主要有两个原因:
1.避免不必要的重绘。
考虑一个网页有两个Layer。
在网页的某一帧显示中,Layer1的元素发生了变化,Layer2的元素没有发生变化。
这时候只需要重新绘制Layer1的内容,然后再与Layer2原有的内容进行Compositing,就可以得到整个网页的内容。
这样就可以避免对没有发生变化的Layer2进行不必要的绘制。
2.利用硬件加速高效实现某些UI特性。
例如网页的某一个Layer设置了可滚动、3D变换、透明度或者滤镜,那么就可以通过GPU来高效实现。
在默认情况下,网页元素的绘制是按照RenderObjectTree的先序遍历顺序进行的,并且它们在空间上是按照各自的display属性值依次进行布局的。
例如,如果一个网页元素的display属性值为"inline",那么它就会以内联元素方式显示,也就是紧挨在前一个绘制的元素的后面进行显示。
又如,如果一个网页元素的display属性值为"block",那么它就会以块级元素进行显示,也就是它的前后会各有一个换行符。
我们将这种网页元素绘制方式称为NormalFlow或者InFlow。
有默认情况,就会有例外情况。
例如,如果一个网页元素同时设置了position和z-index属性,那么它可能就不会以InFlow的方式进行显示,而是以OutofFlow的方式进行显示。
在默认情况下,一个网页元素的position和z-index属性值被设置为“static”和"auto"。
网页元素的position属性还可以取值为“relative”、“absolute”和“fixed”,这一类网页元素称为Positioned元素。
当一个Positioned元素的z-index属性值不等于"auto"时,它就会以OutofFlow的方式进行显示。
CSS2.1规范规定网页渲染引擎要为每一个z-index属性值不等于"auto"的Positioned元素创建一个StackingContext。
对于其它的元素,它们虽然没有自己的StackingContext,但是它们会与最近的、具有自己的StackingContext的元素共享同相同的StackingContext。
不同StackingContext的元素的绘制顺序是不会相互交叉的。
假设有两个StackingContext,一个包含有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
StackingContext的这个特性,使得它可以成为一个观念上的原子类型绘制层(AtomicConceptualLayerforPainting)。
也就是说,只要我们定义好StackingContext内部元素的绘制顺序,那么再根据拥有StackingContext的元素的z-index属性值,那么就可以得到网页的所有元素的绘制顺序。
我们可以通过图2所示的例子直观地理解StackingContext的上述特性,如下所示:
在图2的左边,一共有4个StackingContext。
最下面的StackingContext的z-index等于-1;中间的StackingContext的z-index等于0;最上面的StackingContext的z-index等于1,并且嵌套了另外一个z-index等于6的StackingContext。
我们观察被嵌套的z-index等于6的StackingContext,它包含了另外一个z-index也是等于6的元素,但是这两个z-index的含义是不一样的。
其中,StackingContext的z-index值是放在父StackingContext中讨论才有意义,而元素的z-index放在当前它所在的StackingContext讨论才有意义。
再者,我们是下面和中间的两个StackingContext,虽然它们都包含有三个z-index分别等于7、8和9的元素,但是它们是完全不相干的。
如果我们将图2左边中间的StackingContext的z-index修改为2,那么它就会变成最上面的StackingContext,并且会重叠在z-index等于1的StackingContext上,以及嵌套在这个StackingContext里面的那个StackingContext。
这样,我们就得到了StackingContext的绘制顺序。
如前所述,接下来只要定义好StackingContext内的元素的绘制顺序,那么就可以网页的所有元素的绘制顺序。
StackingContext内的元素的绘制顺序如下所示:
1.背景(Backgrounds)和边界(Borders),也就是拥有StackingContext的元素的背景和边界。
2.Z-index值为负数的子元素。
3.内容(Contents),也就是拥有StackingContext的元素的内容。
4.NormalFlow类型的子元素。
5.Z-index值为正数的子元素。
以上就是与CompositedLayer的绘制顺序有关的背景知识。
这些背景知识在后面分析GraphicsLayerTree的创建过程时就会用到。
从图1可以看到,GraphicsLayerTree是根据RenderLayerTree创建的。
也就是说,RenderLayer与GraphicsLayer存在对应关系,如下所示:
原则上,RenderLayerTree中的每一个RenderLayer都对应有一个CompositedLayerMapping,每一个CompositedLayerMapping又包含有若干个GraphicsLayer。
但是这样将会导致创建大量的GraphicsLayer。
创建大量的GraphicsLayer意味着需要耗费大量的内存资源。
这个问题称为”LayerExplosion“问题。
为了解决“LayerExplosion”问题,每一个需要创建CompositedLayerMapping的RenderLayer都需要给出一个理由。
这个理由称为“CompositingReason”,它描述的实际上是RenderLayer的特征。
例如,如果一个RenderLayer关联的RenderObject设置了3DTransform属性,那么就需要为该RenderLayer创建一个CompositedLayerMapping。
WebKit一共定义了54个CompositingReason,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
//Intrinsicreasonsthatcanbeknownrightawaybythelayer
constuint64_tCompositingReason3DTransform=UINT64_C
(1)<<0;
constuint64_tCompositingReasonVideo=UINT64_C
(1)<<1;
constuint64_tCompositingReasonCanvas=UINT64_C
(1)<<2;
constuint64_tCompositingReasonPlugin=UINT64_C
(1)<<3;
constuint64_tCompositingReasonIFrame=UINT64_C
(1)<<4;
constuint64_tCompositingReasonBackfaceVisibilityHidden=UINT64_C
(1)<<5;
constuint64_tCompositingReasonActiveAnimation=UINT64_C
(1)<<6;
constuint64_tCompositingReasonTransitionProperty=UINT64_C
(1)<<7;
constuint64_tCompositingReasonFilters=UINT64_C
(1)<<8;
constuint64_tCompositingReasonPositionFixed=UINT64_C
(1)<<9;
constuint64_tCompositingReasonOverflowScrollingTouch=UINT64_C
(1)<<10;
constuint64_tCompositingReasonOverflowScrollingParent=UINT64_C
(1)<<11;
constuint64_tCompositingReasonOutOfFlowClipping=UINT64_C
(1)<<12;
constuint64_tCompositingReasonVideoOverlay=UINT64_C
(1)<<13;
constuint64_tCompositingReasonWillChangeCompositingHint=UINT64_C
(1)<<14;
//Overlapreasonsthatrequireknowingwhat'sbehindyouinpaint-orderbeforeknowingtheanswer
constuint64_tCompositingReasonAssumedOverlap=UINT64_C
(1)<<15;
constuint64_tCompositingReasonOverlap=UINT64_C
(1)<<16;
constuint64_tCompositingReasonNegativeZIndexChildren=UINT64_C
(1)<<17;
constuint64_tCompositingReasonScrollsWithRespectToSquashingLayer=UINT64_C
(1)<<18;
constuint64_tCompositingReasonSquashingSparsityExceeded=UINT64_C
(1)<<19;
constuint64_tCompositingReasonSquashingClippingContainerMismatch=UINT64_C
(1)<<20;
constuint64_tCompositingReasonSquashingOpacityAncestorMismatch=UINT64_C
(1)<<21;
constuint64_tCompositingReasonSquashingTransformAncestorMismatch=UINT64_C
(1)<<22;
constuint64_tCompositingReasonSquashingFilterAncestorMismatch=UINT64_C
(1)<<23;
constuint64_tCompositingReasonSquashingWouldBreakPaintOrder=UINT64_C
(1)<<24;
constuint64_tCompositingReasonSquashingVideoIsDisallowed=UINT64_C
(1)<<25;
constuint64_tCompositingReasonSquashedLayerClipsCompositingDescendants=UINT64_C
(1)<<26;
//Subtreereasonsthatrequireknowingwhatthestatusofyoursubtreeisbeforeknowingtheanswer
constuint64_tCompositingReasonTransformWithCompositedDescendants=UINT64_C
(1)<<27;
constuint64_tCompositingReasonOpacityWithCompositedDescendants=UINT64_C
(1)<<28;
constuint64_tCompositingReasonMaskWithCompositedDescendants=UINT64_C
(1)<<29;
constuint64_tCompositingReasonReflectionWithCompositedDescendants=UINT64_C
(1)<<30;
constuint64_tCompositingReasonFilterWithCompositedDescendants=UINT64_C
(1)<<31;
constuint64_tCompositingReasonBlendingWithCompositedDescendants=UINT64_C
(1)<<32;
constuint64_tCompositingReasonClipsCompositingDescendants=UINT64_C
(1)<<33;
constuint64_tCompositingReasonPerspectiveWith3DDescendants=UINT64_C
(1)<<34;
constuint64_tCompositingReasonPreserve3DWith3DDescendants=UINT64_C
(1)<<35;
constuint64_tCompositingReasonReflectionOfCompositedParent=UINT64_C
(1)<<36;
constuint64_tCompositingReasonIsolateCompositedDescendants=UINT64_C
(1)<<37;
//Therootlayerisaspecialcasethatmaybeforcedtobealayer,butalsoitneedstobe
//alayerifanythingelseinthesubtreeiscomposited.
constuint64_tCompositingReasonRoot=UINT64_C
(1)<<38;
//CompositedLayerMappinginternalhierarchyreasons
constuint64_tCompositingReasonLayerForAncestorClip=UINT64_C
(1)<<39;
constuint64_tCompositingReasonLayerForDescendantClip=UINT64_C
(1)<<40;
constuint64_tCompositingReasonLayerForPerspective=UINT64_C
(1)<<41;
constuint64_tCompositingReasonLayerForHorizontalScrollbar=UINT64_C
(1)<<42;
constuint64_tCompositingReasonLayerForVerticalScrollbar=UINT64_C
(1)<<43;
constuint64_tCompositingReasonLayerForScrollCorner=UINT64_C
(1)<<44;
constuint64_tCompositingReasonLayerForScrollingContents=UINT64_C
(1)<<45;
constuint64_tCompositingReasonLayerForScrollingContainer=UINT64_C
(1)<<46;
constuint64_tCompositingReasonLayerForSquashingContents=UINT64_C
(1)<<47;
constuint64_tCompositingReasonLayerForSquashingContainer=UINT64_C
(1)<<48;
constuint64_tCompositingReasonLayerForForeground=UINT64_C
(1)<<49;
constuint64_tCompositingReasonLayerForBackground=UINT64_C
(1)<<50;
constuint64_tCompositingReasonLayerForMask=UINT64_C
(1)<<51;
constuint64_tCompositingReasonLayerForClippingMask=UINT64_C
(1)<<52;
constuint64_tCompositingReasonLayerForScrollingBlockSelection=UINT64_C
(1)<<53;
这些CompositingReason定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/CompositingReasons.h中。
其中,有3个CompositingReason比较特殊,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
constuint64_tCompositingReasonComboSquashableReasons=
CompositingReasonOverlap
|CompositingReasonAssumedOverlap
|CompositingReasonOverflowScrollingParent;
它们是CompositingReasonOverlap、CompositingReasonAssumedOverlap和CompositingReasonOverflowScrollingParent,称为SquashableReason。
WebKit不会为具有这三种特征的RenderLayer之一的RenderLayer创建CompositedLayerMapping。
如果启用了OverlapTesting,那么WebKit会根据上述的StackingContext顺序计算每一个RenderLayer的后面是否有其它的RenderLayer与其重叠。
如果有,并且与其重叠的RenderLayer有一个对应的CompositedLayerMapping,那么就会将位于上面的RenderLayer的CompositingReason设置为CompositingReasonOverlap。
如果没有启用OverlapTesting,那么WebKit会根据上述的StackingContext顺序检查每一个RenderLayer的后面是否有一个具有CompositedLayerMapping的RenderLayer。
只要有,不管它们是否重叠,那么就会将位于上面的RenderLayer的CompositingReason设置为CompositingReasonAssumedOverlap。
最后,如果一个RenderLayer包含在一个具有overflow属性为"scrol