Chromium网页Graphics Layer Tree创建过程分析.doc_第1页
Chromium网页Graphics Layer Tree创建过程分析.doc_第2页
Chromium网页Graphics Layer Tree创建过程分析.doc_第3页
Chromium网页Graphics Layer Tree创建过程分析.doc_第4页
Chromium网页Graphics Layer Tree创建过程分析.doc_第5页
已阅读5页,还剩36页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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 Layer Tree的关系可以通过图1描述,如下所示:在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的详细分析,可以参考这个系列的文章。 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,一个包含有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的这个特性,使得它可以成为一个观念上的原子类型绘制层(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;最上面的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分别等于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. 背景(Backgrounds)和边界(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 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的Render 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 the 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 uint64_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 CompositingReasonPositionFixed = 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; const 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 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 = UINT64_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 CompositingReasonSquashingWouldBreakPaintOrder = 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 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 CompositingReasonReflectionWithCompositedDescendants = 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 uint64_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) 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 CompositingReasonLayerForAncestorClip = 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 CompositingReasonLayerForVerticalScrollbar = 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 CompositingReasonLayerForSquashingContents = 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(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比较特殊,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片const uint64_t CompositingReasonComboSquashableReasons = CompositingReasonOverlap | CompositingReasonAssumedOverlap | CompositingReasonOverflowScrollingParent; 它们是CompositingReasonOverlap、CompositingReasonAssumedOverlap和CompositingReasonOverflowScrollingParent,称为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属性为scroll的Render Block中,并且该Render Block所对应的Render Layer具有Composited Layer Mapping,那么该Render Layer的Compositing Reason就会被设置为CompositingReasonOverflowScrollingParent。 WebKit会将位于一个具有Composited Layer Mapping的Render Layer的上面的那些有着Squashable Reason的Render 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中?考虑具有CompositingReasonOverlap和CompositingReasonAssumedOverlap的Render Layer,当它们需要重绘,或者它们下面的具有Composited Layer Mapping的Render Layer重绘时,都不可避免地对它们以及它们下面的具有Composited Layer Mapping的Render Layer进行Compositing。这是由于它们相互之间存在重叠区域,只要其中一个发生变化,就会牵一发而动全身。类似地,当一个overflow属性为scroll的Render Block滚动时,包含在该Render Block内的Render Layer在执行完成重绘操作之后,需要参与到Compositing操作去。 WebKit定义了两个函数,用来判断一个Render Layer是需要Compositing还是Squashing,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片/ Any reasons other than overlap or assumed overlap will require the layer to be separately compositing. inline bool requiresCompositing(CompositingReasons reasons) return reasons & CompositingReasonComboSquashableReasons; / 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 & CompositingReasonComboSquashableReasons); 这两个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/CompositingReasons.h中。 参数reasons描述的是一个Render Layer的Compositing Reason,函数requiresCompositing判断该Render Layer是否需要Compositing,也就是是否要为该Render Layer创建一个Composited Layer Mapping,而函数requiresSquashing判断该Render Layer需要Squashing,也就是绘制在一个Squashing Graphics Layer中。 除了Compositing Render Layer和Squashing Render Layer,剩下的其它Render Layer称为Non-Compositing Render Layer。这些Render Layer将会与离其最近的具有Composited Layer Mapping的父Render Layer绘制同样的Graphics Layer中。 WebKit是根据Graphics Layer Tree来绘制网页内容的。在绘制一个Graphics Layer的时候,除了绘制Graphics Layer本身所有的内容之外,还会在Render Layer Tree中。找到与该Graphics Layer对应的Render Layer,并且从该Render Layer开始,将那些Non-Compositing类型的子Render Layer也一起绘制,直到遇到一个具有Composited Layer Mapping的子Render Layer为止。这个过程在后面的文章中分析网页内容的绘制过程时就会看到。 前面提到,Composited Layer Mapping包含有若干个Graphics Layer,这些Graphics Layer在Composited Layer Mapping,也是形成一个Graphics Layer Sub Tree的,如图4所示:图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的一部分。例如,如果拥有该Composited Layer Mapping的Render Layer的上面存在Squashing Render Layer,那么上述Graphics Layer Sub Tree还包含有一个Squashing Graphics Layer。不过这一部分Graphics Layer Sub Tree已经足于让我们理解Composited Layer Mapping的组成。 在图5中,只有Main Layer是必须存在的,它用来绘制一个Render 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规则排序的子Render Layer对应的Composited Layer Mapping描述的Graphics Layer Sub Tree,它们均以父Render Layer的Scrolling Container为父Graphics Layer。 5. 如果一个Render Layer是根Render 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. 如果一个Render Layer的上面存在Squashing Render Layer,那么就会存在Squashing Layer。 了解了Composited Layer Mapping对应的Graphics Layer Sub Tree的结构之后,接下来我们就可以结合源码分析网页的Graphics Layer Tree的创建过程了。网页的Graphics Layer Tree的创建主要是分三步进行: 1. 计算各个Render Layer Tree中的Render Layer的Compositing Reason; 2. 为有需要的Render Layer创建Composited Layer Mapping; 3. 将各个Composited Layer Mapping描述Graphics Layer Sub Tree连接起来形成Graphics Layer Tree。 上述过程主要是发生在网页的Layout过程中。对网页进行Layout是网页渲染过程的一个重要步骤,以后我们分析网页的渲染过程时就会看到这一点。在WebKit中,每一个正在加载的网页都关联有一个FrameView对象。当需要对网页进行Layout时,就会调用这个FrameView对象的成员函数updateLayoutAndStyleForPainting,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void FrameView:updateLayoutAndStyleForPainting() / Updating layout can run script, which can tear down the FrameView. RefPtr protector(this); updateLayoutAndStyleIfNeededRecursive(); if (RenderView* view = renderView() . view-compositor()-updateIfNeededRecursive(); . . 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/FrameView.cpp中。 FrameView类的成员函数updateLayoutAndStyleForPainting是通过调用另外一个成员函数updateLayoutAndStyleIfNeededRecursive对网页进行Layout的。 执行完成Layout操作之后,FrameView类的成员函数updateLayoutAndStyleForPainting又调用成员函数renderView获得一个RenderView对象。从前面一文可以知道,网页的DOM Tree的根节点对应的Render Object就是一个RenderView对象。因此,前面获得的RenderView对象描述的就是正在加载的网页的Render Layer Tree的根节点。 再接下来,FrameView类的成员函数updateLayoutAndStyleForPainting又调用上述RenderView对象的成员函数compositor获得一个RenderLayerCompositor对象,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片RenderLayerCompositor* RenderView:compositor() if (!m_compositor) m_compositor = adoptPtr(new RenderLayerCompositor(*this); return m_compositor.get(); 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderView.cpp中。 这个RenderLayerCompositor对象负责管理网页的Render Layer Tree,以及根据Render Layer Tree创建Graphics Layer Tree。 回到FrameView类的成员函数updateLayoutAndStyleForPainting中,它获得了正在加载的网页对应的RenderLayerCompositor对象之后,接下来就调用这个RenderLayerCompositor对象的成员函数updat

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论