在前面一文中,我们分析了网页Render Layer Tree的创建过程。在创建Render Layer的同时,WebKit还会为其创建Graphics Layer。这些Graphics Layer形成一个Graphics Layer Tree。Graphics Layer可看作是一个图形缓冲区,被若干Render Layer共用。本文接下来就分析Graphics Layer Tree的创建过程。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!
网页的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,一个包含有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 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存在对应关系,如下所示:
图3 Render Layer Tree与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,如下所示:
// 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 what's 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比较特殊,如下所示:
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,如下所示:
// 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 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 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,它的实现如下所示:
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对象。从前面Chromium网页DOM Tree创建过程分析一文可以知道,网页的DOM Tree的根节点对应的Render Object就是一个RenderView对象。因此,前面获得的RenderView对象描述的就是正在加载的网页的Render Layer Tree的根节点。
再接下来,FrameView类的成员函数updateLayoutAndStyleForPainting又调用上述RenderView对象的成员函数compositor获得一个RenderLayerCompositor对象,如下所示:
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对象的成员函数updateIfNeededRecursive根据Render Layer Tree创建或者更新Graphics Layer Tree。
RenderLayerCompositor类的成员函数updateIfNeededRecursive的实现如下所示:
void RenderLayerCompositor::updateIfNeededRecursive()
{
......
updateIfNeeded();
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。
RenderLayerCompositor类的成员函数updateIfNeededRecursive调用另外一个成员函数updateIfNeeded创建Graphics Layer Tree,如下所示:
void RenderLayerCompositor::updateIfNeeded()
{
CompositingUpdateType updateType = m_pendingUpdateType;
m_pendingUpdateType = CompositingUpdateNone;
if (!hasAcceleratedCompositing() || updateType == CompositingUpdateNone)
return;
RenderLayer* updateRoot = rootRenderLayer();
Vector layersNeedingRepaint;
if (updateType >= CompositingUpdateAfterCompositingInputChange) {
bool layersChanged = false;
......
CompositingRequirementsUpdater(m_renderView, m_compositingReasonFinder).update(updateRoot);
{
......
CompositingLayerAssigner(this).assign(updateRoot, layersChanged, layersNeedingRepaint);
}
......
if (layersChanged)
updateType = std::max(updateType, CompositingUpdateRebuildTree);
}
if (updateType != CompositingUpdateNone) {
......
GraphicsLayerUpdater updater;
updater.update(layersNeedingRepaint, *updateRoot);
if (updater.needsRebuildTree())
updateType = std::max(updateType, CompositingUpdateRebuildTree);
......
}
if (updateType >= CompositingUpdateRebuildTree) {
GraphicsLayerVector childList;
{
......
GraphicsLayerTreeBuilder().rebuild(*updateRoot, childList);
}
if (childList.isEmpty())
destroyRootLayer();
else
m_rootContentLayer->setChildren(childList);
......
}
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。
当RenderLayerCompositor类的成员变量m_pendingUpdateType的值不等于CompositingUpdateNone的时候,就表明网页的Graphics Layer Tree需要进行更新。此外,要对网页的Graphics Layer Tree需要进行更新,还要求浏览器开启硬件加速合成。当调用RenderLayerCompositor类的成员函数hasAcceleratedCompositing得到的返回值等于true的时候,就表明浏览器开启了硬件加速合成。
当RenderLayerCompositor类的成员变量m_pendingUpdateType的值大于等于CompositingUpdateAfterCompositingInputChange的时候,表示Graphics Layer Tree的输入发生了变化,例如,Render Layer Tree中的某一个Render Layer的内容发生了变化。在这种情况下,RenderLayerCompositor类的成员函数updateIfNeeded会构造一个CompositingRequirementsUpdater对象,并且调用这个CompositingRequirementsUpdater对象的成员函数update从网页的Render Layer Tree的根节点开始,递归计算每一个Render Layer的Compositing Reason,主要就是根据各个Render Layer包含的Render Object的CSS属性来计算。
计算好网页的Render Layer Tree中的每一个Render Layer的Compositing Reason之后,RenderLayerCompositor类的成员函数updateIfNeeded接着再构造一个CompositingLayerAssigner对象,并且调用这个CompositingLayerAssigner对象的成员函数assign根据每一个Render Layer新的Compositing Reason决定是否需要为它创建一个新的Composited Layer Mapping或者删除它原来拥有的Composited Layer Mapping。如果有的Render Layer原来是没有Composited Layer Mapping的,现在有了Composited Layer Mapping,或者原来有Composited Layer Mapping,现在没有了Composited Layer Mapping,那么本地变量layersChanged的值就会被设置为true。这时候本地变量updateType的值会被更新为CompositingUpdateRebuildTree。
在本地变量updateType的值不等于CompositingUpdateNone的情况下,RenderLayerCompositor类的成员函数updateIfNeeded接下来又会构造一个GraphicsLayerUpdater对象,并且调用这个GraphicsLayerUpdater对象的成员函数update检查每一个拥有Composited Layer Mapping的Render Layer更新它的Composited Layer Mapping所描述的Graphics Layer Sub Tree。如果有Render Layer更新了它的Composited Layer Mapping所描述的Graphics Layer Sub Tree,那么调用上述GraphicsLayerUpdater对象的成员函数needsRebuildTree获得的返回值就会等于true。这时候本地变量updateType的值也会被更新为CompositingUpdateRebuildTree。
一旦本地变量updateType的值被更新为CompositingUpdateRebuildTree,或者它本来的值,也就是RenderLayerCompositor类的成员变量m_pendingUpdateType的值,原本就等于CompositingUpdateRebuildTree,那么RenderLayerCompositor类的成员函数updateIfNeeded又会构造一个GraphicsLayerTreeBuilder对象,并且调用这个GraphicsLayerTreeBuilder对象的成员函数rebuild从Render Layer Tree的根节点开始,递归创建一个新的Graphics Layer Tree。
注意,前面调用GraphicsLayerTreeBuilder类的成员函数rebuild的时候,传递进去的第一个参数updateRoot是Render Layer Tree的根节点,第二个参数childList是一个输出参数,它里面保存的是Graphics Layer Tree的根节点的子节点。Graphics Layer Tree的根节点由RenderLayerCompositor类的成员函数m_rootContentLayer指向的GraphicsLayer对象描述,因此当参数childList描述的Vector不为空时,它里面所保存的Graphics Layer都会被设置为RenderLayerCompositor类的成员函数m_rootContentLayer指向的GraphicsLayer对象的子Graphics Layer。
接下来我们主要分析CompositingLayerAssigner类的成员函数assign、GraphicsLayerUpdater类的成员函数update以及GraphicsLayerTreeBuilder类的成员函数rebuild的实现,以及了解Graphics Layer Tree的创建过程。
CompositingLayerAssigner类的成员函数assign主要是为Render Layer创建或者删除Composited Layer Mapping,它的实现如下所示:
void CompositingLayerAssigner::assign(RenderLayer* updateRoot, bool& layersChanged, Vector& layersNeedingRepaint)
{
SquashingState squashingState;
assignLayersToBackingsInternal(updateRoot, squashingState, layersChanged, layersNeedingRepaint);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositingLayerAssigner.cpp中。
从前面的分析可以知道,参数updateRoot描述的是Render Layer Tree的根节点,CompositingLayerAssigner类的成员函数assign主要是调用另外一个成员函数assignLayersToBackingsInternal从这个根节点开始,递归是否需要为每一个Render Layer创建或者删除Composited Layer Mapping。
CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal的实现如下所示:
void CompositingLayerAssigner::assignLayersToBackingsInternal(RenderLayer* layer, SquashingState& squashingState, bool& layersChanged, Vector& layersNeedingRepaint)
{
......
CompositingStateTransitionType compositedLayerUpdate = computeCompositedLayerUpdate(layer);
if (m_compositor->allocateOrClearCompositedLayerMapping(layer, compositedLayerUpdate)) {
layersNeedingRepaint.append(layer);
layersChanged = true;
}
// Add this layer to a squashing backing if needed.
if (m_layerSquashingEnabled) {
if (updateSquashingAssignment(layer, squashingState, compositedLayerUpdate, layersNeedingRepaint))
layersChanged = true;
......
}
if (layer->stackingNode()->isStackingContext()) {
RenderLayerStackingNodeIterator iterator(*layer->stackingNode(), NegativeZOrderChildren);
while (RenderLayerStackingNode* curNode = iterator.next())
assignLayersToBackingsInternal(curNode->layer(), squashingState, layersChanged, layersNeedingRepaint);
}
if (m_layerSquashingEnabled) {
// At this point, if the layer is to be "separately" composited, then its backing becomes the most recent in paint-order.
if (layer->compositingState() == PaintsIntoOwnBacking || layer->compositingState() == HasOwnBackingButPaintsIntoAncestor) {
ASSERT(!requiresSquashing(layer->compositingReasons()));
squashingState.updateSquashingStateForNewMapping(layer->compositedLayerMapping(), layer->hasCompositedLayerMapping());
}
}
RenderLayerStackingNodeIterator iterator(*layer->stackingNode(), NormalFlowChildren | PositiveZOrderChildren);
while (RenderLayerStackingNode* curNode = iterator.next())
assignLayersToBackingsInternal(curNode->layer(), squashingState, layersChanged, layersNeedingRepaint);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositingLayerAssigner.cpp中。
CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal首先调用成员函数computeCompositedLayerUpdate计算参数layer描述的Render Layer的Compositing State Transition,如下所示:
CompositingStateTransitionType CompositingLayerAssigner::computeCompositedLayerUpdate(RenderLayer* layer)
{
CompositingStateTransitionType update = NoCompositingStateChange;
if (needsOwnBacking(layer)) {
if (!layer->hasCompositedLayerMapping()) {
update = AllocateOwnCompositedLayerMapping;
}
} else {
if (layer->hasCompositedLayerMapping())
update = RemoveOwnCompositedLayerMapping;
if (m_layerSquashingEnabled) {
if (!layer->subtreeIsInvisible() && requiresSquashing(layer->compositingReasons())) {
// We can't compute at this time whether the squashing layer update is a no-op,
// since that requires walking the render layer tree.
update = PutInSquashingLayer;
} else if (layer->groupedMapping() || layer->lostGroupedMapping()) {
update = RemoveFromSquashingLayer;
}
}
}
return update;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositingLayerAssigner.cpp中。
一个Render Layer的Compositing State Transition分为4种:
1. 它需要Compositing,但是还没有创建Composited Layer Mapping,这时候Compositing State Transition设置为AllocateOwnCompositedLayerMapping,表示要创建一个新的Composited Layer Mapping。
2. 它不需要Compositing,但是之前已经创建有Composited Layer Mapping,这时候Compositing State Transition设置为RemoveOwnCompositedLayerMapping,表示要删除之前创建的Composited Layer Mapping。
3. 它需要Squashing,这时候Compositing State Transition设置为PutInSquashingLayer,表示要将它绘制离其最近的一个Render Layer的Composited Layer Mapping里面的一个Squashing Layer上。
4. 它不需要Squashing,这时候Compositing State Transition设置为RemoveFromSquashingLayer,表示要将它从原来对应的Squashing Layer上删除。
注意,后面2种Compositing State Transition,只有在CompositingLayerAssigner类的成员变量m_layerSquashingEnabled的值在等于true的时候才会进行设置。默认情况下,浏览器是开启Layer Squashing机制的,不过可以通过设置“disable-layer-squashing”选项进行关闭,或者通过设置“enable-layer-squashing”选项显式开启。
判断一个Render Layer是否需要Compositing,是通过调用CompositingLayerAssigner类的成员函数needsOwnBacking进行的,它的实现如下所示:
bool CompositingLayerAssigner::needsOwnBacking(const RenderLayer* layer) const
{
if (!m_compositor->canBeComposited(layer))
return false;
// If squashing is disabled, then layers that would have been squashed should just be separately composited.
bool needsOwnBackingForDisabledSquashing = !m_layerSquashingEnabled && requiresSquashing(layer->compositingReasons());
return requiresCompositing(layer->compositingReasons()) || needsOwnBackingForDisabledSquashing || (m_compositor->staleInCompositingMode() && layer->isRootLayer());
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositingLayerAssigner.cpp中。
CompositingLayerAssigner类的成员变量m_compositor指向的是一个RenderLayerCompositor对象,CompositingLayerAssigner类的成员函数needsOwnBacking首先调用它的成员函数canBeComposited判断参数layer描述的Render Layer是否需要Compositing,如下所示:
bool RenderLayerCompositor::canBeComposited(const RenderLayer* layer) const
{
// FIXME: We disable accelerated compositing for elements in a RenderFlowThread as it doesn't work properly.
// See http://webkit.org/b/84900 to re-enable it.
return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer() && !layer->subtreeIsInvisible() && layer->renderer()->flowThreadState() == RenderObject::NotInsideFlowThread;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。
一个Render Layer可以Compositing,需要同时满足以下4个条件:
1. 浏览器开启硬件加速合成,即RenderLayerCompositor类的成员变量m_hasAcceleratedCompositing的值等于true。
2. Render Layer本身有Compositing绘制的需求,也就是调用它的成员函数isSelfPaintingLayer得到的返回值为true。从前面Chromium网页Render Layer Tree创建过程分析一文可以知道,Render Layer的类型一般为NormalLayer,但是如果将overflow属性设置为“hidden",那么Render Layer的类型被设置为OverflowClipLayer。对于类型为OverflowClipLayer的Render Layer,如果它的内容没有出现overflow,那么就没有必要对它进行Compositing。
3. Render Layer描述的网页内容是可见的,也就是调用它的成员函数subtreeIsInvisible得到的返回值等于false。
4. Render Layer的内容不是渲染在一个RenderFlowThread中,也就是与Render Layer关联的Render Object的Flow Thread State等于RenderObject::NotInsideFlowThread。从注释可以知道,在RenderFlowThread中渲染的元素是禁用硬件加速合成的,因为不能正确地使用。RenderFlowThread是CSS 3定义的一种元素显示方式,更详细的信息可以参考CSS文档:CSS Regions Module Level 1。
回到CompositingLayerAssigner类的成员函数needsOwnBacking中,如果RenderLayerCompositor类的成员函数canBeComposited告诉它参数layer描述的Render Layer不可进行Compositing,那么就不需要为它创建一个Composited Layer Mapping。
另一方面,如果RenderLayerCompositor类的成员函数canBeComposited告诉CompositingLayerAssigner类的成员函数needsOwnBacking,参数layer描述的Render Layer可以进行Compositing,那么CompositingLayerAssigner类的成员函数needsOwnBacking还需要进一步判断该Render Layer是否真的需要进行Compositing。
如果参数layer描述的Render Layer满足以下3个条件之一,那么CompositingLayerAssigner类的成员函数needsOwnBacking就会认为它需要进行Compositing:
1. Render Layer的Compositing Reason表示它需要Compositing,这是通过调用前面提到的函数requiresCompositing判断的。
2. Render Layer的Compositing Reason表示它需要Squashing,但是浏览器禁用了“Layer Squashing”机制。当浏览器禁用“Layer Squashing”机制时,CompositingLayerAssigner类的成员变量m_layerSquashingEnabled会等于false。调用前面提到的函数requiresSquashing可以判断一个Render Layer是否需要Squashing。
3. Render Layer是Render Layer Tree的根节点,并且Render Layer Compositor处于Compositing模式中。除非设置了Render Layer Tree的根节点无条件Compositing,否则的话,当在Render Layer Tree根节点的子树中,没有任何Render Layer需要Compositing时, Render Layer Tree根节点也不需要Compositing,这时候Render Layer Compositor就会被设置为非Compositing模式。判断一个Render Layer是否是Render Layer Tree的根节点,调用它的成员函数isRootLayer即可,而判断一个Render Layer Compositor是否处于Compositing模式,调用它的成员函数staleInCompositingMode即可。
回到CompositingLayerAssigner类的成员函数computeCompositedLayerUpdate中,当它调用结束后,再返回到CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal中,这时候CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal就知道了参数layer描述的Render Layer的Compositing State Transition Type。
知道了参数layer描述的Render Layer的Compositing State Transition Type之后,CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal接下来调用成员变量m_compositor描述的一个RenderLayerCompositor对象的成员函数allocateOrClearCompositedLayerMapping为其创建或者删除Composited Layer Mapping,如下所示:
bool RenderLayerCompositor::allocateOrClearCompositedLayerMapping(RenderLayer* layer, const CompositingStateTransitionType compositedLayerUpdate)
{
bool compositedLayerMappingChanged = false;
......
switch (compositedLayerUpdate) {
case AllocateOwnCompositedLayerMapping:
......
layer->ensureCompositedLayerMapping();
compositedLayerMappingChanged = true;
......
break;
case RemoveOwnCompositedLayerMapping:
// PutInSquashingLayer means you might have to remove the composited layer mapping first.
case PutInSquashingLayer:
if (layer->hasCompositedLayerMapping()) {
......
layer->clearCompositedLayerMapping();
compositedLayerMappingChanged = true;
}
break;
case RemoveFromSquashingLayer:
case NoCompositingStateChange:
// Do nothing.
break;
}
......
return compositedLayerMappingChanged || nonCompositedReasonChanged;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。
RenderLayerCompositor类的成员函数allocateOrClearCompositedLayerMapping主要是根据Render Layer的Compositing State Transition Type决定是要为其创建Composited Layer Mapping,还是删除Composited Layer Mapping。
对于Compositing State Transition Type等于AllocateOwnCompositedLayerMapping的Render Layer,RenderLayerCompositor类的成员函数allocateOrClearCompositedLayerMapping会调用它的成员函数ensureCompositedLayerMapping为其创建一个Composited Layer Mapping
对于Compositing State Transition Type等于RemoveOwnCompositedLayerMapping或者PutInSquashingLayer的Render Layer,RenderLayerCompositor类的成员函数allocateOrClearCompositedLayerMapping会调用它的成员函数clearCompositedLayerMapping删除原来为它创建的Composited Layer Mapping。
对于Compositing State Transition Type其它值的Render Layer,则不需要进行特别的处理。
这一步执行完成之后,回到CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal中,它接下来判断成员变量m_layerSquashingEnabled的值是否等于true。如果等于true,那么就说明浏览器开启了"Layer Squashing"机制。这时候就需要调用成员函数updateSquashingAssignment判断是否需要将参数layer描述的Render Layer绘制在一个Squashing Graphics Layer中。
CompositingLayerAssigner类的成员函数updateSquashingAssignment的实现如下所示:
bool CompositingLayerAssigner::updateSquashingAssignment(RenderLayer* layer, SquashingState& squashingState, const CompositingStateTransitionType compositedLayerUpdate,
Vector& layersNeedingRepaint)
{
......
if (compositedLayerUpdate == PutInSquashingLayer) {
......
bool changedSquashingLayer =
squashingState.mostRecentMapping->updateSquashingLayerAssignment(layer, squashingState.mostRecentMapping->owningLayer(), squashingState.nextSquashedLayerIndex);
......
return true;
}
if (compositedLayerUpdate == RemoveFromSquashingLayer) {
if (layer->groupedMapping()) {
......
layer->setGroupedMapping(0);
}
......
return true;
}
return false;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositingLayerAssigner.cpp中。
CompositingLayerAssigner类的成员函数updateSquashingAssignment也是根据Render Layer的Compositing State Transition Type决定是否要将它绘制在一个Squashing Graphics Layer中,或者将它从一个Squashing Graphics Layer中删除。
对于Compositing State Transition Type等于PutInSquashingLayer的Render Layer,它将会绘制在一个Squashing Graphics Layer中。这个Squashing Graphics Layer保存在一个Composited Layer Mapping中。这个Composited Layer Mapping关联的Render Layer处于要Squashing的Render Layer的下面,并且前者离后者是最近的,记录在参数squashingState描述的一个SquashingState对象的成员变量mostRecentMapping中。通过调用CompositedLayerMapping类的成员函数updateSquashingLayerAssignment可以将一个Render Layer绘制在一个Composited Layer Mapping内部维护的一个quashing Graphics Layer中。
对于Compositing State Transition Type等于RemoveFromSquashingLayer的Render Layer,如果它之前已经被设置绘制在一个Squashing Graphics Layer中,那么就需要将它从这个Squashing Graphics Layer中删除。如果一个Render Layer之前被设置绘制在一个Squashing Graphics Layer中,那么调用它的成员函数groupedMapping就可以获得一个Grouped Mapping。这个Grouped Mapping描述的也是一个Composited Layer Mapping,并且Render Layer所绘制在的Squashing Graphics Layer就是由这个Composited Layer Mapping维护的。因此,要将一个Render Layer从一个Squashing Graphics Layer中删除,只要将它的Grouped Mapping设置为0即可。这是通过调用RenderLayer类的成员函数setGroupedMapping实现的。
再回到CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal中,它接下来判断参数layer描述的Render Layer所关联的Render Object是否是一个Stacking Context。如果是的话,那么就递归调用成员函数assignLayersToBackingsInternal遍历那些z-index为负数的子Render Object对应的Render Layer,确定是否需要为它们创建Composited Layer Mapping。
到目前为止,CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal就处理完成参数layer描述的Render Layer,以及那些z-index为负数的子Render Layer。这时候,参数layer描述的Render Layer可能会作为那些z-index大于等于0的子Render Layer的Grouped Mapping,因此在继续递归处理z-index大于等于0的子Render Layer之前,CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal需要将参数layer描述的Render Layer对应的Composited Layer Mapping记录下来,前提是这个Render Layer拥有Composited Layer Mapping。这是通过调用参数squashingState描述的一个SquashingState对象的成员函数updateSquashingStateForNewMapping实现的,实际上就是记录在该SquashingState对象的成员变量mostRecentMapping中。这样前面分析的CompositingLayerAssigner类的成员函数updateSquashingAssignment就可以知道将其参数layer描述的Render Layer绘制在哪一个Squashing Graphics Layer中。
最后,CompositingLayerAssigner类的成员函数assignLayersToBackingsInternal就递归调用自己处理那些z-index大于等于0的子Render Layer。递归调用完成之后,整个Render Layer Tree就处理完毕了。这时候哪些Render Layer具有Composited Layer Mapping就可以确定了。
前面分析RenderLayerCompositor类的成员函数allocateOrClearCompositedLayerMapping时提到,调用RenderLayer类的成员函数ensureCompositedLayerMapping可以为一个Render Layer创建一个Composited Layer Mapping,接下来我们就继续分析这个函数的实现,以便了解Composited Layer Mapping的创建过程。
RenderLayer类的成员函数ensureCompositedLayerMapping的实现如下所示:
CompositedLayerMappingPtr RenderLayer::ensureCompositedLayerMapping()
{
if (!m_compositedLayerMapping) {
m_compositedLayerMapping = adoptPtr(new CompositedLayerMapping(*this));
......
}
return m_compositedLayerMapping.get();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayer.cpp中。
RenderLayer类的成员变量compositedLayerMapping描述的就是一个Composited Layer Mapping。如果这个Composited Layer Mapping还没有创建,那么当RenderLayer类的成员函数ensureCompositedLayerMapping被调用时,就会进行创建。
Composited Layer Mapping的创建过程,也就是CompositedLayerMapping类的构造函数的实现,如下所示:
CompositedLayerMapping::CompositedLayerMapping(RenderLayer& layer)
: m_owningLayer(layer)
, ......
{
......
createPrimaryGraphicsLayer();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositedLayerMapping.cpp中。
正在创建的Composited Layer Mapping被参数layer描述的Render Layer拥有,因此这个Render Layer将会被保存在在创建的Composited Layer Mapping的成员变量m_owningLayer中。
从图5可以知道,一个Composited Layer Mapping一定存在一个Main Graphics Layer。这个Main Graphics Layer是CompositedLayerMapping类的构造函数通过调用另外一个成员函数createPrimaryGraphicsLayer创建的,如下所示:
void CompositedLayerMapping::createPrimaryGraphicsLayer()
{
m_graphicsLayer = createGraphicsLayer(m_owningLayer.compositingReasons());
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositedLayerMapping.cpp中。
Composited Layer Mapping中的Main Graphics Layer由成员变量m_graphicsLayer描述,并且这个Main Graphics Layer是通过调用成员函数createGraphicsLayer创建的。如果我们分析CompositedLayerMapping类的其它代码,就会发现Composited Layer Mapping中的其它Graphics Layer也是通过调用成员函数createGraphicsLayer创建的。
CompositedLayerMapping类的成员函数createGraphicsLayer的实现如下所示:
PassOwnPtr CompositedLayerMapping::createGraphicsLayer(CompositingReasons reasons)
{
GraphicsLayerFactory* graphicsLayerFactory = 0;
if (Page* page = renderer()->frame()->page())
graphicsLayerFactory = page->chrome().client().graphicsLayerFactory();
OwnPtr graphicsLayer = GraphicsLayer::create(graphicsLayerFactory, this);
graphicsLayer->setCompositingReasons(reasons);
if (Node* owningNode = m_owningLayer.renderer()->generatingNode())
graphicsLayer->setOwnerNodeId(InspectorNodeIds::idForNode(owningNode));
return graphicsLayer.release();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/CompositedLayerMapping.cpp中。
CompositedLayerMapping类的成员函数createGraphicsLayer在创建一个Graphics Layer之前,首先会获得一个GraphicsLayerFactory对象。这个GraphicsLayerFactory对象是由WebKit的使用者提供的。在我们这个情景中,WebKit的使用者就是Chromium,它提供的GraphicsLayerFactory对象的实际类型为GraphicsLayerFactoryChromium。
获得了GraphicsLayerFactory对象之后,CompositedLayerMapping类的成员函数createGraphicsLayer接下来就以它为参数,调用GraphicsLayer类的静态成员函数create创建一个Graphics Layer,如下所示:
PassOwnPtr GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient* client)
{
return factory->createGraphicsLayer(client);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。
GraphicsLayer类的静态成员函数create调用参数factory描述的一个GraphicsLayerFactoryChromium对象的成员函数createGraphicsLayer创建一个Graphics Layer,如下所示:
PassOwnPtr GraphicsLayerFactoryChromium::createGraphicsLayer(GraphicsLayerClient* client)
{
OwnPtr layer = adoptPtr(new GraphicsLayer(client));
......
return layer.release();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/GraphicsLayerFactoryChromium.cpp中。
从这里可以看到,GraphicsLayerFactoryChromium类的成员函数createGraphicsLayer返回的是一个GraphicsLayer对象。
这一步执行完成后,一个Composited Layer Mapping及其内部的Main Graphics Layer就创建完成了。
前面分析CompositingLayerAssigner类的成员函数updateSquashingAssignment时提到,调用CompositedLayerMapping类的成员函数updateSquashingLayerAssignment可以将一个Render Layer绘制在其内部维护的一个Squashing Graphics Layer中。CompositedLayerMapping类的成员函数updateSquashingLayerAssignment的实现如下所示:
bool CompositedLayerMapping::updateSquashingLayerAssignment(RenderLayer* squashedLayer, const RenderLayer& owningLayer, size_t nextSquashedLayerIndex)
{
......
GraphicsLayerPaintInfo paintInfo;
paintInfo.renderLayer = squashedLayer;
......
// Change tracking on squashing layers: at the first sign of something changed, just invalidate the layer.
// FIXME: Perhaps we can find a tighter more clever mechanism later.
bool updatedAssignment = false;
if (nextSquashedLayerIndex < m_squashedLayers.size()) {
if (!paintInfo.isEquivalentForSquashing(m_squashedLayers[nextSquashedLayerIndex])) {
......
updatedAssignment = true;
m_squashedLayers[nextSquashedLayerIndex] = paintInfo;
}
} else {
......
m_squashedLayers.append(paintInfo);
updatedAssignment = true;
}
squashedLayer->setGroupedMapping(this);
return updatedAssignment;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/GraphicsLayerFactoryChromium.cpp中。
CompositedLayerMapping类有一个成员变量m_squashedLayers,它描述的是一个类型为GraphicsLayerPaintInfo的Vector,如下所示:
class CompositedLayerMapping FINAL : public GraphicsLayerClient {
WTF_MAKE_NONCOPYABLE(CompositedLayerMapping); WTF_MAKE_FAST_ALLOCATED;
......
private:
......
OwnPtr m_squashingLayer; // Only used if any squashed layers exist, this is the backing that squashed layers paint into.
Vector m_squashedLayers;
......
};
void GraphicsLayerUpdater::update(Vector& layersNeedingPaintInvalidation, RenderLayer& layer, UpdateType updateType, const UpdateContext& context)
{
if (layer.hasCompositedLayerMapping()) {
CompositedLayerMappingPtr mapping = layer.compositedLayerMapping();
......
if (mapping->updateGraphicsLayerConfiguration(updateType))
m_needsRebuildTree = true;
......
}
UpdateContext childContext(context, layer);
for (RenderLayer* child = layer.firstChild(); child; child = child->nextSibling())
update(layersNeedingPaintInvalidation, *child, updateType, childContext);
}
bool CompositedLayerMapping::updateGraphicsLayerConfiguration(GraphicsLayerUpdater::UpdateType updateType)
{
......
bool layerConfigChanged = false;
......
// The background layer is currently only used for fixed root backgrounds.
if (updateBackgroundLayer(m_backgroundLayerPaintsFixedRootBackground))
layerConfigChanged = true;
if (updateForegroundLayer(compositor->needsContentsCompositingLayer(&m_owningLayer)))
layerConfigChanged = true;
bool needsDescendantsClippingLayer = compositor->clipsCompositingDescendants(&m_owningLayer);
......
bool needsAncestorClip = compositor->clippedByNonAncestorInStackingTree(&m_owningLayer);
......
if (updateClippingLayers(needsAncestorClip, needsDescendantsClippingLayer))
layerConfigChanged = true;
......
if (updateScrollingLayers(m_owningLayer.needsCompositedScrolling())) {
layerConfigChanged = true;
......
}
bool hasPerspective = false;
if (RenderStyle* style = renderer->style())
hasPerspective = style->hasPerspective();
bool needsChildTransformLayer = hasPerspective && (layerForChildrenTransform() == m_childTransformLayer.get()) && renderer->isBox();
if (updateChildTransformLayer(needsChildTransformLayer))
layerConfigChanged = true;
......
if (updateSquashingLayers(!m_squashedLayers.isEmpty()))
layerConfigChanged = true;
if (layerConfigChanged)
updateInternalHierarchy();
.......
}
bool CompositedLayerMapping::updateSquashingLayers(bool needsSquashingLayers)
{
bool layersChanged = false;
if (needsSquashingLayers) {
......
if (!m_squashingLayer) {
m_squashingLayer = createGraphicsLayer(CompositingReasonLayerForSquashingContents);
......
layersChanged = true;
}
if (m_ancestorClippingLayer) {
if (m_squashingContainmentLayer) {
m_squashingContainmentLayer->removeFromParent();
m_squashingContainmentLayer = nullptr;
layersChanged = true;
}
} else {
if (!m_squashingContainmentLayer) {
m_squashingContainmentLayer = createGraphicsLayer(CompositingReasonLayerForSquashingContainer);
layersChanged = true;
}
}
......
} else {
if (m_squashingLayer) {
m_squashingLayer->removeFromParent();
m_squashingLayer = nullptr;
layersChanged = true;
}
if (m_squashingContainmentLayer) {
m_squashingContainmentLayer->removeFromParent();
m_squashingContainmentLayer = nullptr;
layersChanged = true;
}
......
}
return layersChanged;
}
void CompositedLayerMapping::updateInternalHierarchy()
{
// m_foregroundLayer has to be inserted in the correct order with child layers,
// so it's not inserted here.
if (m_ancestorClippingLayer)
m_ancestorClippingLayer->removeAllChildren();
m_graphicsLayer->removeFromParent();
if (m_ancestorClippingLayer)
m_ancestorClippingLayer->addChild(m_graphicsLayer.get());
if (m_childContainmentLayer)
m_graphicsLayer->addChild(m_childContainmentLayer.get());
else if (m_childTransformLayer)
m_graphicsLayer->addChild(m_childTransformLayer.get());
if (m_scrollingLayer) {
GraphicsLayer* superLayer = m_graphicsLayer.get();
if (m_childContainmentLayer)
superLayer = m_childContainmentLayer.get();
if (m_childTransformLayer)
superLayer = m_childTransformLayer.get();
superLayer->addChild(m_scrollingLayer.get());
}
// The clip for child layers does not include space for overflow controls, so they exist as
// siblings of the clipping layer if we have one. Normal children of this layer are set as
// children of the clipping layer.
if (m_layerForHorizontalScrollbar)
m_graphicsLayer->addChild(m_layerForHorizontalScrollbar.get());
if (m_layerForVerticalScrollbar)
m_graphicsLayer->addChild(m_layerForVerticalScrollbar.get());
if (m_layerForScrollCorner)
m_graphicsLayer->addChild(m_layerForScrollCorner.get());
// The squashing containment layer, if it exists, becomes a no-op parent.
if (m_squashingLayer) {
ASSERT(compositor()->layerSquashingEnabled());
ASSERT((m_ancestorClippingLayer && !m_squashingContainmentLayer) || (!m_ancestorClippingLayer && m_squashingContainmentLayer));
if (m_squashingContainmentLayer) {
m_squashingContainmentLayer->removeAllChildren();
m_squashingContainmentLayer->addChild(m_graphicsLayer.get());
m_squashingContainmentLayer->addChild(m_squashingLayer.get());
} else {
// The ancestor clipping layer is already set up and has m_graphicsLayer under it.
m_ancestorClippingLayer->addChild(m_squashingLayer.get());
}
}
}
GraphicsLayer* CompositedLayerMapping::childForSuperlayers() const
{
if (m_squashingContainmentLayer)
return m_squashingContainmentLayer.get();
return localRootForOwningLayer();
}
GraphicsLayer* CompositedLayerMapping::localRootForOwningLayer() const
{
if (m_ancestorClippingLayer)
return m_ancestorClippingLayer.get();
return m_graphicsLayer.get();
}
GraphicsLayer* CompositedLayerMapping::parentForSublayers() const
{
if (m_scrollingBlockSelectionLayer)
return m_scrollingBlockSelectionLayer.get();
if (m_scrollingContentsLayer)
return m_scrollingContentsLayer.get();
if (m_childContainmentLayer)
return m_childContainmentLayer.get();
if (m_childTransformLayer)
return m_childTransformLayer.get();
return m_graphicsLayer.get();
}
void GraphicsLayerTreeBuilder::rebuild(RenderLayer& layer, GraphicsLayerVector& childLayersOfEnclosingLayer)
{
......
const bool hasCompositedLayerMapping = layer.hasCompositedLayerMapping();
CompositedLayerMappingPtr currentCompositedLayerMapping = layer.compositedLayerMapping();
// If this layer has a compositedLayerMapping, then that is where we place subsequent children GraphicsLayers.
// Otherwise children continue to append to the child list of the enclosing layer.
GraphicsLayerVector layerChildren;
GraphicsLayerVector& childList = hasCompositedLayerMapping ? layerChildren : childLayersOfEnclosingLayer;
......
if (layer.stackingNode()->isStackingContext()) {
RenderLayerStackingNodeIterator iterator(*layer.stackingNode(), NegativeZOrderChildren);
while (RenderLayerStackingNode* curNode = iterator.next())
rebuild(*curNode->layer(), childList);
// If a negative z-order child is compositing, we get a foreground layer which needs to get parented.
if (hasCompositedLayerMapping && currentCompositedLayerMapping->foregroundLayer())
childList.append(currentCompositedLayerMapping->foregroundLayer());
}
RenderLayerStackingNodeIterator iterator(*layer.stackingNode(), NormalFlowChildren | PositiveZOrderChildren);
while (RenderLayerStackingNode* curNode = iterator.next())
rebuild(*curNode->layer(), childList);
if (hasCompositedLayerMapping) {
bool parented = false;
if (layer.renderer()->isRenderPart())
parented = RenderLayerCompositor::parentFrameContentLayers(toRenderPart(layer.renderer()));
if (!parented)
currentCompositedLayerMapping->parentForSublayers()->setChildren(layerChildren);
......
if (shouldAppendLayer(layer))
childLayersOfEnclosingLayer.append(currentCompositedLayerMapping->childForSuperlayers());
}
}
bool parented = false;
if (layer.renderer()->isRenderPart())
parented = RenderLayerCompositor::parentFrameContentLayers(toRenderPart(layer.renderer()));
if (!parented)
currentCompositedLayerMapping->parentForSublayers()->setChildren(layerChildren);
if (shouldAppendLayer(layer))
childLayersOfEnclosingLayer.append(currentCompositedLayerMapping->childForSuperlayers());
static bool shouldAppendLayer(const RenderLayer& layer)
{
if (!RuntimeEnabledFeatures::overlayFullscreenVideoEnabled())
return true;
Node* node = layer.renderer()->node();
if (node && isHTMLMediaElement(*node) && toHTMLMediaElement(node)->isFullscreen())
return false;
return true;
}
+Overflow Controls Host Layer
+Container Layer
+Background Layer
+Scroll Layer
+Root Content Layer
void RenderLayerCompositor::ensureRootLayer()
{
......
if (!m_rootContentLayer) {
m_rootContentLayer = GraphicsLayer::create(graphicsLayerFactory(), this);
......
}
if (!m_overflowControlsHostLayer) {
......
// Create a layer to host the clipping layer and the overflow controls layers.
m_overflowControlsHostLayer = GraphicsLayer::create(graphicsLayerFactory(), this);
// Create a clipping layer if this is an iframe or settings require to clip.
m_containerLayer = GraphicsLayer::create(graphicsLayerFactory(), this);
......
m_scrollLayer = GraphicsLayer::create(graphicsLayerFactory(), this);
......
// Hook them up
m_overflowControlsHostLayer->addChild(m_containerLayer.get());
m_containerLayer->addChild(m_scrollLayer.get());
m_scrollLayer->addChild(m_rootContentLayer.get());
......
}
......
}
void RenderLayerCompositor::rootFixedBackgroundsChanged()
{
......
if (GraphicsLayer* backgroundLayer = fixedRootBackgroundLayer())
m_containerLayer->addChildBelow(backgroundLayer, m_scrollLayer.get());
}