Chromium网页Layer Tree创建过程分析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Chromium网页Layer Tree创建过程分
析
在Chromium中,WebKit会创建一个Graphics Layer Tree描述网页。
Graphics Layer Tree是和网页渲染相关的一个Tree。
网页渲染最终由Chromium的CC模块完成,因此CC模块又会根据Graphics Layer Tree创建一个Layer Tree,以后就会根据这个Layer Tree对网页进行渲染。
本文接下来就分析网页Layer Tree的创建过程。
从前面一文可以知道,网页的Graphics Layer Tree是根据Render Layer Tree创建的,Render Layer Tree又是根据Render Object Tree创建的。
Graphics Layer Tree与Render Layer Tree、Render Layer Tree与Render Object Tree的节点是均是一对多的关系,然而Graphics Layer Tree 与CC模块创建的Layer Tree的节点是一一对应的关系,如图1所示:
也就是说,每一个Graphics Layer都对应有一个CC Layer。
不过,Graphics Layer与CC Layer 不是直接的一一对应的,它们是透过另外两个Layer才对应起来的,如图2所示:
中间的两个Layer分别是WebContentLayerImpl和WebLayerImpl,它们是属于Content层的对象。
关于Chromium的层次划分,可以参考前面一文的介绍。
Graphics Layer与CC Layer 的对应关系,是在Graphics Layer的创建过程中建立起来的,接下来我们就通过源码分析这种对应关系的建立过程。
从前面一文可以知道,Graphics Layer是通过调用GraphicsLayerFactoryChromium类
的成员函数createGraphicsLayer创建的,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
PassOwnPtr<GraphicsLayer>
GraphicsLayerFactoryChromium::createGraphicsLayer(GraphicsLayerClient* client)
{
OwnPtr<GraphicsLayer> layer = adoptPtr(new GraphicsLayer(client));
......
return layer.release();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/GraphicsLayerFactoryChromium.cpp中。
参数client指向的实际上是一个CompositedLayerMapping对象,这个CompositedLayerMapping对象会用来构造一个Graphics Layer。
Graphics Layer的构造过程,也就是GraphicsLayer类的构造函数的实现,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
: m_client(client)
, ......
{
......
m_opaqueRectTrackingContentLayerDelegate = adoptPtr(new OpaqueRectTrackingContentLayerDelegate(this));
m_layer = adoptPtr(Platform::current()->compositorSupport()->createContentLayer(m_opaqueRectTracking ContentLayerDelegate.get()));
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。
GraphicsLayer类的构造函数首先是将参数client指向的CompositedLayerMapping对象保存在成员变量m_client中,接着又创建了一个OpaqueRectTrackingContentLayerDelegate 对象保存在成员变量opaqueRectTrackingContentLayerDelegate中。
再接下来GraphicsLayer类的构造函数通过Platform类的静态成员函数current获得一个RendererWebKitPlatformSupportImpl对象。
这个RendererWebKitPlatformSupportImpl对象定义在Content模块中,它实现了由WebKit定义的Platform接口,用来向WebKit层提供平台相关的实现。
通过调用RendererWebKitPlatformSupportImpl类的成员函数compositorSupport可以获得一个WebCompositorSupportImpl对象。
有了这个WebCompositorSupportImpl对象之后,
就可以调用它的成员函数createContentLayer创建一个WebContentLayerImpl对象,并且保存在GraphicsLayer类的成员变量m_layer中。
WebCompositorSupportImpl类的成员函数createContentLayer的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
WebContentLayer* WebCompositorSupportImpl::createContentLayer(
WebContentLayerClient* client) {
return new WebContentLayerImpl(client);
}
这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_compositor_support_ 中。
从这里可以看到,WebCompositorSupportImpl类的成员函数createContentLayer创建了一个WebContentLayerImpl对象返回给调用者。
WebContentLayerImpl对象的创建过程,即WebContentLayerImpl类的构造函数的实现,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片WebContentLayerImpl::WebContentLayerImpl(blink::WebContentLayerClient* client) : client_(client), ...... {
if (WebLayerImpl::UsingPictureLayer())
layer_ = make_scoped_ptr(new WebLayerImpl(PictureLayer::Create(this)));
else
layer_ = make_scoped_ptr(new WebLayerImpl(ContentLayer::Create(this)));
......
}
这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_content_layer_中。
从前面的调用过程可以知道,参数client指向的实际上是一个OpaqueRectTrackingContentLayerDelegate对象,WebContentLayerImpl类的构造函数首先将它保存在成员变量client_中。
WebContentLayerImpl类的构造函数接下来调用WebLayerImpl类的静态成员函数UsingPictureLayer判断Render进程是否启用Impl Side Painting特性。
如果启用的话,就会调用PictureLayer类的静态成员函数Create创建一个Picture Layer;否则的话,就会调用ContentLayer类的静态成员函数Create创建一个Content Layer。
有了Picture Layer或者Content Layer之后,再创建一个WebLayerImpl对象,保存在WebContentLayerImpl类的成员变量layer_中。
当Render进程设置了enable-impl-side-painting启动选项时,就会启用Impl Side Painting特性,也就是会在Render进程中创建一个Compositor线程,与Render进程中的Main线程一起协作完成网页的渲染。
在这种情况下,Graphics Layer在绘制网页内容的时候,
实际上只是记录了绘制命令。
这些绘制命令就记录在对应的Picture Layer中。
另一方面,如果Render进程没有设置enable-impl-side-painting启动选项,那么Graphics Layer在绘制网页内容的时候,就会通过Content Layer提供的一个Canvas真正地把网页内容对应的UI绘制在一个内存缓冲区中。
无论是Picture Layer还是Content Layer,它们都是在cc::Layer类继承下来的,也就是说,它们对应于图2所示的CC Layer。
不过,我们只考虑Picture Layer的情况,因此接下来我们继续分析Picture Layer的创建过程,也就是PictureLayer类的静态成员函数Create 的实现,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
scoped_refptr<PictureLayer> PictureLayer::Create(ContentLayerClient* client) {
return make_scoped_refptr(new PictureLayer(client));
}
这个函数定义在文件external/chromium_org/cc/layers/picture_中。
从这里可以看到,PictureLayer类的静态成员函数Create创建了一个PictureLayer对象返回给调用者。
PictureLayer对象的创建过程,也就是PictureLayer类的构造函数的实现,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
PictureLayer::PictureLayer(ContentLayerClient* client)
: client_(client),
pile_(make_scoped_refptr(new PicturePile())),
...... {
}
这个函数定义在文件external/chromium_org/cc/layers/picture_中。
从前面的调用过程可以知道,参数client指向的实际上是一个WebContentLayerImpl 对象,PictureLayer类的构造函数将它保存在成员变量client_中。
PictureLayer类的构造函数还做了另外一件重要的事情,就是创建了一个PicturePile 对象,并且保存在成员变量pile_中。
这个PicturePile对象是用来将Graphics Layer的绘制命令记录在Pictrue Layer中的,后面我们分析网页内容的绘制过程时就会看到这一点。
回到WebContentLayerImpl类的构造函数中,它创建了一个Pictrue Layer之后,接下来就会以这个Pictrue Layer为参数,创建一个WebLayerImpl对象,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
WebLayerImpl::WebLayerImpl(scoped_refptr<Layer> layer) : layer_(layer) {
......
}
这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_layer_中。
WebLayerImpl类的构造函数主要就是将参数layer描述的一个PictrueLayer对象保存在成员变量layer_中。
从前面一文还可以知道,Graphics Layer与Graphics Layer是通过GraphicsLayer类的成员函数addChild形成父子关系的(从而形成Graphics Layer Tree),如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void GraphicsLayer::addChild(GraphicsLayer* childLayer)
{
addChildInternal(childLayer);
updateChildList();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。
GraphicsLayer类的成员函数addChild首先调用成员函数addChildInternal将参数childLayer描述的一个Graphics Layer作为当前正在处理的Graphics Layer的子Graphics Layer,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void GraphicsLayer::addChildInternal(GraphicsLayer* childLayer)
{
......
childLayer->setParent(this);
m_children.append(childLayer);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。
这一步执行完成后,Graphics Layer之间就建立了父子关系。
回到GraphicsLayer类的成员函数addChild中,它接下来还会调用另外一个成员函数updateChildList,用来在CC Layer之间建立父子关系,从而形CC Layer Tree。
GraphicsLayer类的成员函数updateChildList的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void GraphicsLayer::updateChildList()
{
WebLayer* childHost = m_layer->layer();
......
for (size_t i = 0; i < m_children.size(); ++i)
childHost->addChild(m_children[i]->platformLayer());
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。
从前面的分析可以知道,GraphicsLayer类的成员变量m_layer指向的是一个WebContentLayerImpl对象,调用这个WebContentLayerImpl对象的成员函数layer获得的是一个WebLayerImpl对象,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
blink::WebLayer* WebContentLayerImpl::layer() {
return layer_.get();
}
这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_content_layer_中。
从前面的分析可以知道,WebContentLayerImpl类的成员变量layer_指向的是一个WebLayerImpl对象,因此WebContentLayerImpl类的成员函数layer返回的是一个WebLayerImpl对象。
回到GraphicsLayer类的成员函数updateChildList中,它接下来调用GraphicsLayer类的成员函数platformLayer获得当前正在处理的Graphics Layer的所有子Graphics Layer对应的WebLayerImpl对象,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
WebLayer* GraphicsLayer::platformLayer() const
{
return m_layer->layer();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。
这些子Graphics Layer对应的WebLayerImpl对象也就是通过调用它们的成员变量m_layer指向的WebContentLayerImpl对象的成员函数layer获得的。
再回到GraphicsLayer类的成员函数updateChildList中,获得当前正在处理的Graphics Layer对应的WebLayerImpl对象,以及其所有的子Graphics Layer对应的WebLayerImpl对象之后,就可以通过调用WebLayerImpl类的成员函数addChild在它们之间也建立父子关系,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void WebLayerImpl::addChild(WebLayer* child) {
layer_->AddChild(static_cast<WebLayerImpl*>(child)->layer());
}
这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_layer_中。
从前面的分析可以知道,WebLayerImpl类的成员变量layer_指向的是一个PictrueLayer对象,因此WebLayerImpl类的成员函数addChild所做的事情就是在两个PictrueLayer对象之间建立父子关系,这是通过调用PictrueLayer类的成员函数AddChild实现的。
PictrueLayer类的成员函数AddChild是父类Layer继承下来的,它的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void Layer::AddChild(scoped_refptr<Layer> child) {
InsertChild(child, children_.size());
}
这个函数定义在文件external/chromium_org/cc/layers/中。
Layer类的成员函数AddChild将参数child描述的Pictrue Layer设置为当前正在处理的Picture Layer的子Picture Layer,这是通过调用Layer类的成员函数InsertChild实现的,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void Layer::InsertChild(scoped_refptr<Layer> child, size_t index) {
DCHECK(IsPropertyChangeAllowed());
child->RemoveFromParent();
child->SetParent(this);
child->stacking_order_changed_ = true;
index = std::min(index, children_.size());
children_.insert(children_.begin() + index, child);
SetNeedsFullTreeSync();
}
这个函数定义在文件external/chromium_org/cc/layers/中。
Layer类的成员函数InsertChild所做的第一件事情是将当前正在处理的Picture Layer 设置为参数child描述的Pictrue Layer的父Picture Layer,并且将参数child描述的Pictrue Layer保存在当前正在处理的Picture Layer的子Picture Layer列表中。
Layer类的成员函数InsertChild所做的第二件事情是调用另外一个成员函数SetNeedsFullTreeSync发出一个通知,要在CC Layer Tree与CC Pending Layer Tree之间做一个Tree结构同步。
Layer类的成员函数SetNeedsFullTreeSync的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void Layer::SetNeedsFullTreeSync() {
if (!layer_tree_host_)
return;
layer_tree_host_->SetNeedsFullTreeSync();
这个函数定义在文件external/chromium_org/cc/layers/中。
Layer类的成员变量layer_tree_host_指向的是一个LayerTreeHost对象,这个LayerTreeHost是用来管理CC Layer Tree的,后面我们再分析它的创建过程。
Layer类的成员函数SetNeedsFullTreeSync所做的事情就是调用这个LayerTreeHost对象的成员函数SetNeedsFullTreeSync通知它CC Layer Tree结构发生了变化,需要将这个变化同步到CC Pending Layer Tree中去。
LayerTreeHost类的成员函数SetNeedsFullTreeSync的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void LayerTreeHost::SetNeedsFullTreeSync() {
needs_full_tree_sync_ = true;
SetNeedsCommit();
}
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_中。
LayerTreeHost类的成员函数SetNeedsFullTreeSync将成员变量needs_full_tree_sync_设置为true,以标记要在CC Layer Tree和CC Pending Layer Tree之间做一次结构同步,然后再调用另外一个成员函数SetNeedsCommit请求在前面一文中提到的调度器将CC Layer Tree同步到CC Pending Tree去。
至于这个同步操作什么时候会执行,就是由调度器根据其内部的状态机决定了。
这一点我们在后面的文章再分析。
这一步执行完成之后,就可以在CC模块中得到一个Layer Tree,这个Layer Tree与WebKit中的Graphics Layer Tree在结构上是完全同步的,并且这个同步过程是由WebKit控制的。
这个同步过程之所以要由WebKit控制,是因为CC Layer Tree是根据Graphics Layer Tree创建的,而Graphics Layer Tree又是由WebKit管理的。
WebKit现在还需要做的另外一件重要的事情是告诉CC模块,哪一个Picture Layer 是CC Layer Tree的根节点,这样CC模块才可以对整个CC Layer Tree进行管理。
很显然,Graphics Layer Tree的根节点对应的Picture Layer,就是CC Layer Tree的根节点。
因此,WebKit会在创建Graphics Layer Tree的根节点的时候,将该根节点对应的Picture Layer设置到CC模块中去,以便后者将其作为CC Layer Tree的根节点。
Graphics Layer Tree的根节点是什么时候创建的呢?从前面这个系列的文章可以知道,Graphics Layer Tree的根节点对应于Render Layer Tree的根节点,Render Layer Tree的根节点又对应于Render Object Tree的根节点,因此我们就从Render Object Tree的根节点的创建过程开始,分析Graphics Layer Tree的根节点的创建过程。
从前面一文可以知道,Render Object Tree的根节点是在Document类的成员函数attach中创建的,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void Document::attach(const AttachContext& context)
......
m_renderView = new RenderView(this);
......
m_renderView->setStyle(StyleResolver::styleForDocument(*this));
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/dom/Document.cpp中。
Document类的成员函数attach首先创建了一个RenderView对象,保存在成员变量m_renderView中。
这个RenderView对象就是Render Object Tree的根节点。
Document类的成员函数attach接下来还会调用RenderView类的成员函数setStyle给前面创建的RenderView 对象设置CSS属性。
从前面一文可以知道,在给Render Object Tree的节点设置CSS属性的过程中,会创建相应的Render Layer。
这一步发生在RenderLayerModelObject类的成员函数styleDidChange中,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
......
LayerType type = layerTypeRequired();
if (type != NoLayer) {
if (!layer() && layerCreationAllowedForSubtree()) {
......
createLayer(type);
......
}
} else if (layer() && layer()->parent()) {
......
layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
......
}
if (layer()) {
......
layer()->styleChanged(diff, oldStyle);
}
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayerModelObject.cpp 中。
RenderLayerModelObject类的成员函数styleDidChange的详细分析可以参考一文。
其中,Render Layer的创建是通过调用RenderLayerModelObject类的成员函数createLayer实现的,并且创建出来的Render Layer的成员函数styleChanged会被调用,用来设置它的CSS 属性。
在设置Render Layer Tree的根节点的CSS属性的过程中,会触发Graphics Layer Tree 的根节点的创建,因此接下来我们继续分析RenderLayer类的成员函数styleChanged的实现,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle)
{
......
m_stackingNode->updateStackingNodesAfterStyleChange(oldStyle);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayer.cpp中。
RenderLayer类的成员变量m_stackingNode指向的是一个RenderLayerStackingNode 对象。
这个RenderLayerStackingNode对象描述的是一个Stacking Context。
关于Stacking Context,可以参考前面一文。
RenderLayer类的成员函数styleChanged调用上述RenderLayerStackingNode对象的成员函数updateStackingNodesAfterStyleChange通知它所关联的Render Layer的CSS属性发生了变化,这样它可能就需要更新自己的子元素。
RenderLayerStackingNode类的成员函数updateStackingNodesAfterStyleChange的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerStackingNode::updateStackingNodesAfterStyleChange(const RenderStyle* oldStyle)
{
bool wasStackingContext = oldStyle ? !oldStyle->hasAutoZIndex() : false;
int oldZIndex = oldStyle ? oldStyle->zIndex() : 0;
bool isStackingContext = this->isStackingContext();
if (isStackingContext == wasStackingContext && oldZIndex == zIndex())
return;
dirtyStackingContextZOrderLists();
if (isStackingContext)
dirtyZOrderLists();
else
clearZOrderLists();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayerStackingNode.cpp 中。
RenderLayerStackingNode类的成员函数updateStackingNodesAfterStyleChange判断当前正在处理的RenderLayerStackingNode对象关联的Render Layer的CSS属性变化,是否导致它从一个Stacking Context变为一个非Stacking Context,或者从一个非Stacking Context 变为一个Stacking Context。
在从非Stacking Context变为Stacking Context的情况下,RenderLayerStackingNode 类的成员函数updateStackingNodesAfterStyleChange就会调用另外一个成员函数dirtyZOrderLists将Stacking Context标记为Dirty状态,这样以后在需要的时候就会根据该Stacking Context的子元素的z-index重新构建Graphics Layer Tree。
RenderLayerStackingNode类的成员函数dirtyZOrderLists的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerStackingNode::dirtyZOrderLists()
{
......
if (m_posZOrderList)
m_posZOrderList->clear();
if (m_negZOrderList)
m_negZOrderList->clear();
m_zOrderListsDirty = true;
if (!renderer()->documentBeingDestroyed())
compositor()->setNeedsCompositingUpdate(CompositingUpdateRebuildTree);
}
这个函数定义在文件
external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayerStackingNode.cpp 中。
RenderLayerStackingNode类的成员函数dirtyZOrderLists首先是将用来保存子元素的两个列表清空。
其中一个列表用来保存z-index为正数的子元素,另一个列表用来保存z-index 为负数的子元素。
这些子元素在各自的列表中均是按照从小到大的顺序排列的。
有了这个顺序之后,Graphics Layer Tree就可以方便地按照z-index顺序创建出来。
RenderLayerStackingNode类的成员函数dirtyZOrderLists接下来将成员变量m_zOrderListsDirty的值设置为true,就将自己的状态标记为Dirty,以后就会重新根据子元素的z-index值,将它们分别保存在对应的列表中。
RenderLayerStackingNode类的成员函数dirtyZOrderLists最后判断当前加载的网页有没有被销毁。
如果没有被销毁,就会调用另外一个成员函数compositor,获得一个RenderLayerCompositor对象。
这个RenderLayerCompositor对象是用来管理当前加载的网页的Graphics Layer Tree的。
有了这个RenderLayerCompositor对象之后,就可以调用它的成员函数setNeedsCompositingUpdate,用来通知它需要重建Graphics Layer Tree。
RenderLayerCompositor类的成员函数setNeedsCompositingUpdate的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerCompositor::setNeedsCompositingUpdate(CompositingUpdateType updateType)
{
......
if (!m_renderView.needsLayout())
enableCompositingModeIfNeeded();
m_pendingUpdateType = std::max(m_pendingUpdateType, updateType);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCom positor.cpp中。
RenderLayerCompositor类的成员变量m_renderView描述的是一个RenderView对象。
这个RenderView对象就是在前面分析的Document类的成员函数attach中创建的RenderView 对象。
RenderLayerCompositor类的成员函数setNeedsCompositingUpdate判断它是否需要重新Layout。
如果需要的话,就会调用另外一个成员函数enableCompositingModeIfNeeded将网页的Render Layer Tree的根节点设置为一个Compositing Layer,也就是要为它创建一个Graphics Layer。
在我们这个情景中,RenderLayerCompositor类的成员变量m_renderView描述的RenderView对象是刚刚创建的,这意味它需要执行一个Layout操作,因此接下来RenderLayerCompositor类的成员函数setNeedsCompositingUpdate会调用成员函数enableCompositingModeIfNeeded为Render Layer Tree的根节点创建一个Graphics Layer,作
为Graphics Layer Tree的根节点。
RenderLayerCompositor类的成员函数enableCompositingModeIfNeeded的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerCompositor::enableCompositingModeIfNeeded()
{
......
if (rootShouldAlwaysComposite()) {
......
setCompositingModeEnabled(true);
}
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCom positor.cpp中。
RenderLayerCompositor类的成员函数enableCompositingModeIfNeeded首先调用成员函数rootShouldAlwaysComposite判断是否要为网页Render Layer Tree的根节点创建一个Graphics Layer。
如果需要的话,就调用另外一个成员函数setCompositingModeEnabled进行创建。
RenderLayerCompositor类的成员函数rootShouldAlwaysComposite的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
bool RenderLayerCompositor::rootShouldAlwaysComposite() const
{
if (!m_hasAcceleratedCompositing)
return false;
return m_renderView.frame()->isMainFrame() || m_compositingReasonFinder.requiresCompositingForScrollableFrame();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCom positor.cpp中。
只有在采用硬件加速渲染网页的情况下,才需要创建Graphics Layer。
当RenderLayerCompositor类的成员变量m_hasAcceleratedCompositing的值等于true的时候,就表示描述网页采用硬件加速渲染。
因此,当RenderLayerCompositor类的成员变量m_hasAcceleratedCompositing的值等于false的时候,RenderLayerCompositor类的成员函数就返回一个false值给调用者,表示不需要为网页Render Layer Tree的根节点创建Graphics Layer。
在采用硬件加速渲染网页的情况下,在两种情况下,需要为Render Layer Tree的根
节点创建Graphics Layer。
第一种情况是当前网页加载在Main Frame中。
第二种情况是当前网页不是加载在Main Frame,例如是通过iframe嵌入在Main Frame中,但是它是可滚动的。
我们假设当前网页是加载在Main Frame中的,因此RenderLayerCompositor类的成员函数rootShouldAlwaysComposite的返回值为true,这时候RenderLayerCompositor类的成员函数enableCompositingModeIfNeeded就会调用另外一个成员函数setCompositingModeEnabled为网页Render Layer Tree的根节点创建Graphics Layer。
RenderLayerCompositor类的成员函数setCompositingModeEnabled的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerCompositor::setCompositingModeEnabled(bool enable)
{
......
m_compositing = enable;
......
if (m_compositing)
ensureRootLayer();
else
destroyRootLayer();
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCom positor.cpp中。
从前面的调用过程可以知道,参数enable的值等于true,这时候RenderLayerCompositor类的成员函数setCompositingModeEnabled会调用另外一个成员函数ensureRootLayer创建Graphics Layer Tree的根节点。
RenderLayerCompositor类的成员函数ensureRootLayer的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerCompositor::ensureRootLayer()
{
RootLayerAttachment expectedAttachment = m_renderView.frame()->isMainFrame() ? RootLayerAttachedViaChromeClient : RootLayerAttachedViaEnclosingFrame;
......
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());
......
}
......
attachRootLayer(expectedAttachment);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCom positor.cpp中。
RenderLayerCompositor类的成员函数ensureRootLayer的详细分析可以参考前面一文,现在我们关注的重点是它最后调用另外一个成员函数attachRootLayer将Graphics Layer Tree的根节点设置给WebKit的使用者,即Chromium的Content层。
RenderLayerCompositor类的成员函数attachRootLayer的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderLayerCompositor::attachRootLayer(RootLayerAttachment attachment)
{
......
switch (attachment) {
......
case RootLayerAttachedViaChromeClient: {
LocalFrame& frame = m_renderView.frameView()->frame();
Page* page = frame.page();
if (!page)
return;
page->chrome().client().attachRootGraphicsLayer(rootGraphicsLayer());
break;
}
......
}
m_rootLayerAttachment = attachment;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCom positor.cpp中。
从前面的调用过程可以知道,如果当前网页是在Main Frame中加载的,那么参数attachment的值就等于RootLayerAttachedViaChromeClient,这时候RenderLayerCompositor 类的成员函数attachRootLayer与当前加载网页关联的一个ChromeClientImpl对象,并且调用这个ChromeClientImpl对象的成员函数attachRootGraphicsLayer将Graphics Layer Tree的根节点传递给它处理。
Graphics Layer Tree的根节点可以通过调用RenderLayerCompositor 类的成员函数rootGraphicsLayer获得。
ChromeClientImpl类的成员函数attachRootGraphicsLayer的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void ChromeClientImpl::attachRootGraphicsLayer(GraphicsLayer* rootLayer)
{
m_webView->setRootGraphicsLayer(rootLayer);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/ChromeClientImpl.cpp中。
ChromeClientImpl类的成员变量m_webView指向的是一个WebViewImpl对象。
这个WebViewImpl对象的创建过程可以参考前面一文。
ChromeClientImpl类的成员函数attachRootGraphicsLayer所做的事情就是调用这个WebViewImpl对象的成员函数setRootGraphicsLayer,以便将Graphics Layer Tree的根节点传递给它处理。
WebViewImpl类的成员函数setRootGraphicsLayer的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void WebViewImpl::setRootGraphicsLayer(GraphicsLayer* layer)
{
if (pinchVirtualViewportEnabled()) {
PinchViewport& pinchViewport = page()->frameHost().pinchViewport();
pinchViewport.attachToLayerTree(layer, graphicsLayerFactory());
if (layer) {
m_rootGraphicsLayer = pinchViewport.rootGraphicsLayer();
m_rootLayer = pinchViewport.rootGraphicsLayer()->platformLayer();
......
}
......
} else {
m_rootGraphicsLayer = layer;
m_rootLayer = layer ? layer->platformLayer() : 0;
......
}
setIsAcceleratedCompositingActive(layer);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。
如果浏览器设置了"enable-pinch-virtual-viewport"启动选项,调用WebViewImpl类的成员函数pinchVirtualViewportEnabled得到的返回值就会为true。
这时候网页有两个Viewport,一个称为Inner Viewport,另一个称为Outer Viewport。
Outer Viewport有两个特性。
第一个特性是它的大小不跟随页面进行缩放,第二个特性是fixed positioned元素的位置是根据它来计算的。
这种体验的特点是fixed positioned元素的位置不会随页面的缩放发生变化。
为实现这种体验,需要在Graphics Layer Tree中增加一些Graphics Layer。
这些Graphics Layer 通过一个PinchViewport管理。
这时候Graphics Layer Tree的根节点就不再是参数layer描述的Graphics Layer,而是PinchViewport额外创建的一个Graphics Layer。
关于Pinch Virtual Viewport特性的更多信息,可以参考官方文档:。
为简单起见,我们假设没有设置"enable-pinch-virtual-viewport"启动选项,这时候WebViewImpl类的成员函数setRootGraphicsLayer会将参数layer指向的一个Graphics Layer,也就是Graphics Layer Tree的根节点,保存在成员变量m_rootGraphicsLayer中,并且调用它的成员函数platformLayer获得与它关联的一个WebLayerImpl对象,保存在另外一个成员变量m_rootLayer中。
再接下来,WebViewImpl类的成员函数setRootGraphicsLayer调用另外一个成员函数setIsAcceleratedCompositingActive激活网页的硬件加速渲染,它的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void WebViewImpl::setIsAcceleratedCompositingActive(bool active)
{
......
if (!active) {
m_isAcceleratedCompositingActive = false;
......
} else if (m_layerTreeView) {
m_isAcceleratedCompositingActive = true;
......
} else {
......
m_client->initializeLayerTreeView();
m_layerTreeView = m_client->layerTreeView();
if (m_layerTreeView) {
m_layerTreeView->setRootLayer(*m_rootLayer);
......
}
......
m_isAcceleratedCompositingActive = true;
......
}
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。
从前面的调用过程可以知道,参数active的值是等于true的,WebViewImpl类的成员变量m_isAcceleratedCompositingActive的值将被设置为参数active的值,用来表示网页是否已经激活网页硬件加速渲染。
WebViewImpl类还有两个重要的成员变量m_client和m_layerTreeView。
其中,成员变量m_client的初始化过程可以参考前面一文,它指向的是一个在Chromium的Content层创建的RenderViewImpl对象。
另外一个成员变量m_layerTreeView是一个类型为WebLayerTreeView指针,它的值开始的时候是等于NULL的。
WebViewImpl类的成员函数setIsAcceleratedCompositingActive 被调用的时候,如果参数active的值是等于true,并且成员变量m_layerTreeView的值也等于NULL,那么WebKit就会先请求使用者,也就是Chromium的Content层,初始化CC Layer Tree。
这是通过调用成员变量m_client指向的一个RenderViewImpl对象的成员函数initializeLayerTreeView实现的。
Layer Tree View初始化完成之后,WebViewImpl类再将成员变量m_rootLayer描述的WebLayerImpl对象关联的Picture Layer设置为CC Layer Tree的根节点。
接下来我们先分析RenderViewImpl类的成员函数initializeLayerTreeView初始化CC Layer Tree的过程,然后再分析设置CC Layer Tree根节点的过程。
RenderViewImpl类的成员函数initializeLayerTreeView的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderViewImpl::initializeLayerTreeView() {
RenderWidget::initializeLayerTreeView();
......
}
这个函数定义在文件external/chromium_org/content/renderer/render_view_中。
RenderViewImpl类的成员函数initializeLayerTreeView主要是调用父类RenderWidget 的成员函数initializeLayerTreeView初始化一个CC Layer Tree,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderWidget::initializeLayerTreeView() {
compositor_ = RenderWidgetCompositor::Create(
this, is_threaded_compositing_enabled_);
......
if (init_complete_)
StartCompositor();
}
这个函数定义在文件external/chromium_org/content/renderer/render_中。
RenderWidget类的成员函数initializeLayerTreeView首先是调用RenderWidgetCompositor类的静态成员函数Create创建一个RenderWidgetCompositor对象,并且保存在成员变量compositor_中。
在创建这个RenderWidgetCompositor对象期间,也会伴随着创建一个CC Layer Tree。
RenderWidget类有一个成员变量init_complete_,当它的值等于true的时候,表示Browser进程已经为当前正在加载的网页初始化好Render View,这时候RenderWidget类的成员函数initializeLayerTreeView就会调用另外一个成员函数StartCompositor激活前面一文中提到的调度器,表示它可以开始进行调度工作了。
接下来我们先分析RenderWidget类的成员变量init_complete_被设置为true的过程。
从前面一文可以知道,当Browser进程为在Render进程中加载的网页创建了一个Render View之后,会向Render进程发送一个类型为ViewMsg_New的消息。
这个IPC消息被RenderThreadImpl类的成员函数OnCreateNewView处理。
在处理期间,会创建一个RenderViewImpl对象,并且调用它的成员函数Initialize对其进行初始化,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderViewImpl::Initialize(RenderViewImplParams* params) {
......
main_render_frame_.reset(RenderFrameImpl::Create(
this, params->main_frame_routing_id));
......
WebLocalFrame* web_frame = WebLocalFrame::create(main_render_frame_.get());
main_render_frame_->SetWebFrame(web_frame);
......
webwidget_ = WebView::create(this);
......
// If this is a popup, we must wait for the CreatingNew_ACK message before
// completing initialization. Otherwise, we can finish it now.
if (opener_id_ == MSG_ROUTING_NONE) {
......
CompleteInit();
}
webview()->setMainFrame(main_render_frame_->GetWebFrame());
......
}
这个函数定义在文件external/chromium_org/content/renderer/render_view_中。
RenderViewImpl类的成员函数Initialize的详细分析可以参考前面一文。
这里我们看到,当RenderViewImpl类的成员变量opener_id_的值等于MSG_ROUTING_NONE的时候,另外一个成员函数CompleteInit就会被调用。
RenderViewImpl类的成员变量opener_id_什么时候会等于MSG_ROUTING_NONE呢?如果正在加载的网页不是在一个Popup Window显示时,它的值就会等于MSG_ROUTING_NONE,否则它的值等于将它Popup出来的网页的Routing ID。
从代码注释我们还可以看到,如果当前加载的网页是在一个Popup Window显示时,RenderViewImpl类的成员函数CompleteInit将会延迟到Render进程接收到Broswer 发送另外一个类型为ViewMsg_CreatingNew_ACK的IPC消息时才会被调用。
我们假设正在加载的网页不是一个Popup Window显示,这时候RenderViewImpl类的成员函数CompleteInit就会被调用。
RenderViewImpl类的成员函数CompleteInit是从父类RenderWidget继承下来的,它的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderWidget::CompleteInit() {
......
init_complete_ = true;
if (compositor_)
StartCompositor();
......
}
这个函数定义在文件external/chromium_org/content/renderer/render_中。
从这里就可以看到,RenderWidget类的成员变量init_complete_将会被设置为true,并且在成员变量compositor_的值不等于NULL的情况下,会调用前面提到的成员函数StartCompositor激活前面一文中提到的调度器。
回到前面分析的RenderWidget类的成员函数initializeLayerTreeView中,我们假设正在加载的网页不是在一个Popup Window显示,因此当RenderWidget类的成员函数initializeLayerTreeView被调用时,Browser进程已经为正在加载的网页初始化好了Render View,这意味着此时RenderWidget类的成员变量init_complete_已经被设置为true,于是RenderWidget类的成员函数initializeLayerTreeView就会先调用RenderWidgetCompositor类的静态成员函数Create创建一个RenderWidgetCompositor对象,然后再调用另外一个成员函数StartCompositor激活前面一文中提到的调度器。
接下来我们就先分析RenderWidgetCompositor类的静态成员函数Create的实现,在接下来一篇文章中再分析RenderWidget类的成员函数StartCompositor激活调度器的过程。
RenderWidgetCompositor类的静态成员函数Create的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
scoped_ptr<RenderWidgetCompositor> RenderWidgetCompositor::Create(
RenderWidget* widget,
bool threaded) {
scoped_ptr<RenderWidgetCompositor> compositor(
new RenderWidgetCompositor(widget, threaded));
CommandLine* cmd = CommandLine::ForCurrentProcess();
cc::LayerTreeSettings settings;
......
settings.initial_debug_state.show_debug_borders =
cmd->HasSwitch(cc::switches::kShowCompositedLayerBorders);
settings.initial_debug_state.show_fps_counter =
cmd->HasSwitch(cc::switches::kShowFPSCounter);
settings.initial_debug_state.show_layer_animation_bounds_rects =
cmd->HasSwitch(cc::switches::kShowLayerAnimationBounds);
settings.initial_debug_state.show_paint_rects =
cmd->HasSwitch(switches::kShowPaintRects);
settings.initial_debug_state.show_property_changed_rects =
cmd->HasSwitch(cc::switches::kShowPropertyChangedRects);
settings.initial_debug_state.show_surface_damage_rects =
cmd->HasSwitch(cc::switches::kShowSurfaceDamageRects);。