在Chromium中。WebKit会创建一个Graphics Layer Tree描写叙述网页。Graphics Layer Tree是和网页渲染相关的一个Tree。

网页渲染终于由Chromium的CC模块完毕,因此CC模块又会依据Graphics Layer Tree创建一个Layer Tree。以后就会依据这个Layer Tree对网页进行渲染。本文接下来就分析网页Layer Tree的创建过程。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注。

《Android系统源代码情景分析》一书正在进击的程序猿网(http://0xcc0xcd.com)中连载,点击进入!

从前面Chromium网页Graphics 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所看到的:

图1 Graphics Layer Tree与CC Layer Tree的关系

也就是说,每个Graphics Layer都相应有一个CC Layer。只是,Graphics Layer与CC Layer不是直接的一一相应的,它们是透过另外两个Layer才相应起来的。如图2所看到的:

图2 Graphics Layer与CC Layer的相应关系

中间的两个Layer各自是WebContentLayerImpl和WebLayerImpl,它们是属于Content层的对象。关于Chromium的层次划分。能够參考前面Chromium网页载入过程简要介绍和学习计划一文的介绍。Graphics Layer与CC Layer的相应关系,是在Graphics Layer的创建过程中建立起来的,接下来我们就通过源代码分析这样的相应关系的建立过程。

从前面Chromium网页Graphics Layer Tree创建过程分析一文能够知道。Graphics Layer是通过调用GraphicsLayerFactoryChromium类的成员函数createGraphicsLayer创建的,例如以下所看到的:

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类的构造函数的实现,例如以下所看到的:

GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
: m_client(client)
, ......
{
...... m_opaqueRectTrackingContentLayerDelegate = adoptPtr(new OpaqueRectTrackingContentLayerDelegate(this));
m_layer = adoptPtr(Platform::current()->compositorSupport()->createContentLayer(m_opaqueRectTrackingContentLayerDelegate.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的实现例如以下所看到的:

WebContentLayer* WebCompositorSupportImpl::createContentLayer(
WebContentLayerClient* client) {
return new WebContentLayerImpl(client);
}

这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_compositor_support_impl.cc中。

从这里能够看到,WebCompositorSupportImpl类的成员函数createContentLayer创建了一个WebContentLayerImpl对象返回给调用者。

WebContentLayerImpl对象的创建过程,即WebContentLayerImpl类的构造函数的实现。例如以下所看到的:

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_impl.cc中。

从前面的调用过程能够知道,參数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的实现,例如以下所看到的:

scoped_refptr<PictureLayer> PictureLayer::Create(ContentLayerClient* client) {
return make_scoped_refptr(new PictureLayer(client));
}

这个函数定义在文件external/chromium_org/cc/layers/picture_layer.cc中。

从这里能够看到。PictureLayer类的静态成员函数Create创建了一个PictureLayer对象返回给调用者。

PictureLayer对象的创建过程,也就是PictureLayer类的构造函数的实现,例如以下所看到的:

PictureLayer::PictureLayer(ContentLayerClient* client)
: client_(client),
pile_(make_scoped_refptr(new PicturePile())),
...... {
}

这个函数定义在文件external/chromium_org/cc/layers/picture_layer.cc中。

从前面的调用过程能够知道,參数client指向的实际上是一个WebContentLayerImpl对象,PictureLayer类的构造函数将它保存在成员变量client_中。

PictureLayer类的构造函数还做了另外一件重要的事情,就是创建了一个PicturePile对象,而且保存在成员变量pile_中。

这个PicturePile对象是用来将Graphics Layer的绘制命令记录在Pictrue Layer中的。后面我们分析网页内容的绘制过程时就会看到这一点。

回到WebContentLayerImpl类的构造函数中,它创建了一个Pictrue Layer之后,接下来就会以这个Pictrue Layer为參数,创建一个WebLayerImpl对象,例如以下所看到的:

WebLayerImpl::WebLayerImpl(scoped_refptr<Layer> layer) : layer_(layer) {
......
}

这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_layer_impl.cc中。

WebLayerImpl类的构造函数主要就是将參数layer描写叙述的一个PictrueLayer对象保存在成员变量layer_中。

从前面Chromium网页Graphics Layer Tree创建过程分析一文还能够知道。Graphics Layer与Graphics Layer是通过GraphicsLayer类的成员函数addChild形成父子关系的(从而形成Graphics Layer Tree)。例如以下所看到的:

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,例如以下所看到的:

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的实现例如以下所看到的:

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对象,例如以下所看到的:

blink::WebLayer* WebContentLayerImpl::layer() {
return layer_.get();
}

这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_content_layer_impl.cc中。

从前面的分析能够知道。WebContentLayerImpl类的成员变量layer_指向的是一个WebLayerImpl对象,因此WebContentLayerImpl类的成员函数layer返回的是一个WebLayerImpl对象。

回到GraphicsLayer类的成员函数updateChildList中,它接下来调用GraphicsLayer类的成员函数platformLayer获得当前正在处理的Graphics Layer的全部子Graphics Layer相应的WebLayerImpl对象,例如以下所看到的:

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在它们之间也建立父子关系,例如以下所看到的:

void WebLayerImpl::addChild(WebLayer* child) {
layer_->AddChild(static_cast<WebLayerImpl*>(child)->layer());
}

这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_layer_impl.cc中。

从前面的分析能够知道,WebLayerImpl类的成员变量layer_指向的是一个PictrueLayer对象。因此WebLayerImpl类的成员函数addChild所做的事情就是在两个PictrueLayer对象之间建立父子关系,这是通过调用PictrueLayer类的成员函数AddChild实现的。

PictrueLayer类的成员函数AddChild是父类Layer继承下来的。它的实现例如以下所看到的:

void Layer::AddChild(scoped_refptr<Layer> child) {
InsertChild(child, children_.size());
}

这个函数定义在文件external/chromium_org/cc/layers/layer.cc中。

Layer类的成员函数AddChild将參数child描写叙述的Pictrue Layer设置为当前正在处理的Picture Layer的子Picture Layer,这是通过调用Layer类的成员函数InsertChild实现的。例如以下所看到的:

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.cc中。

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的实现例如以下所看到的:

void Layer::SetNeedsFullTreeSync() {
if (!layer_tree_host_)
return; layer_tree_host_->SetNeedsFullTreeSync();
}

这个函数定义在文件external/chromium_org/cc/layers/layer.cc中。

Layer类的成员变量layer_tree_host_指向的是一个LayerTreeHost对象,这个LayerTreeHost是用来管理CC Layer Tree的,后面我们再分析它的创建过程。

Layer类的成员函数SetNeedsFullTreeSync所做的事情就是调用这个LayerTreeHost对象的成员函数SetNeedsFullTreeSync通知它CC Layer Tree结构发生了变化,须要将这个变化同步到CC Pending Layer Tree中去。

LayerTreeHost类的成员函数SetNeedsFullTreeSync的实现例如以下所看到的:

void LayerTreeHost::SetNeedsFullTreeSync() {
needs_full_tree_sync_ = true;
SetNeedsCommit();
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

LayerTreeHost类的成员函数SetNeedsFullTreeSync将成员变量needs_full_tree_sync_设置为true。以标记要在CC Layer Tree和CC Pending Layer Tree之间做一次结构同步,然后再调用另外一个成员函数SetNeedsCommit请求在前面Chromium网页渲染机制简要介绍和学习计划一文中提到的调度器将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的根节点是什么时候创建的呢?从前面Chromium网页载入过程简要介绍和学习计划这个系列的文章能够知道,Graphics Layer Tree的根节点相应于Render Layer Tree的根节点。Render Layer Tree的根节点又相应于Render Object Tree的根节点。因此我们就从Render Object Tree的根节点的创建过程開始。分析Graphics Layer Tree的根节点的创建过程。

从前面Chromium网页DOM Tree创建过程分析一文能够知道,Render Object Tree的根节点是在Document类的成员函数attach中创建的。例如以下所看到的:

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属性。

从前面Chromium网页Render Layer Tree创建过程分析一文能够知道,在给Render Object Tree的节点设置CSS属性的过程中,会创建相应的Render Layer。这一步发生在RenderLayerModelObject类的成员函数styleDidChange中,例如以下所看到的:

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的具体分析能够參考Chromium网页Render Layer Tree创建过程分析一文。

当中,Render Layer的创建是通过调用RenderLayerModelObject类的成员函数createLayer实现的。而且创建出来的Render Layer的成员函数styleChanged会被调用。用来设置它的CSS属性。

在设置Render Layer Tree的根节点的CSS属性的过程中,会触发Graphics Layer Tree的根节点的创建。因此接下来我们继续分析RenderLayer类的成员函数styleChanged的实现,例如以下所看到的:

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,能够參考前面Chromium网页Graphics Layer Tree创建过程分析一文。RenderLayer类的成员函数styleChanged调用上述RenderLayerStackingNode对象的成员函数updateStackingNodesAfterStyleChange通知它所关联的Render Layer的CSS属性发生了变化,这样它可能就须要更新自己的子元素。

RenderLayerStackingNode类的成员函数updateStackingNodesAfterStyleChange的实现例如以下所看到的:

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的实现例如以下所看到的:

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的实现例如以下所看到的:

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/RenderLayerCompositor.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的实现例如以下所看到的:

void RenderLayerCompositor::enableCompositingModeIfNeeded()
{
...... if (rootShouldAlwaysComposite()) {
......
setCompositingModeEnabled(true);
}
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。

RenderLayerCompositor类的成员函数enableCompositingModeIfNeeded首先调用成员函数rootShouldAlwaysComposite推断是否要为网页Render Layer Tree的根节点创建一个Graphics Layer。假设须要的话,就调用另外一个成员函数setCompositingModeEnabled进行创建。

RenderLayerCompositor类的成员函数rootShouldAlwaysComposite的实现例如以下所看到的:

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/RenderLayerCompositor.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的实现例如以下所看到的:

void RenderLayerCompositor::setCompositingModeEnabled(bool enable)
{
...... m_compositing = enable; ...... if (m_compositing)
ensureRootLayer();
else
destroyRootLayer(); ......
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。

从前面的调用过程能够知道,參数enable的值等于true,这时候RenderLayerCompositor类的成员函数setCompositingModeEnabled会调用另外一个成员函数ensureRootLayer创建Graphics Layer Tree的根节点。

RenderLayerCompositor类的成员函数ensureRootLayer的实现例如以下所看到的:

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/RenderLayerCompositor.cpp中。

RenderLayerCompositor类的成员函数ensureRootLayer的具体分析能够參考前面Chromium网页Graphics Layer Tree创建过程分析一文,如今我们关注的重点是它最后调用另外一个成员函数attachRootLayer将Graphics Layer Tree的根节点设置给WebKit的使用者。即Chromium的Content层。

RenderLayerCompositor类的成员函数attachRootLayer的实现例如以下所看到的:

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/RenderLayerCompositor.cpp中。

从前面的调用过程能够知道,假设当前网页是在Main Frame中载入的。那么參数attachment的值就等于RootLayerAttachedViaChromeClient,这时候RenderLayerCompositor类的成员函数attachRootLayer与当前载入网页关联的一个ChromeClientImpl对象。而且调用这个ChromeClientImpl对象的成员函数attachRootGraphicsLayer将Graphics Layer Tree的根节点传递给它处理。

Graphics Layer Tree的根节点能够通过调用RenderLayerCompositor类的成员函数rootGraphicsLayer获得。

ChromeClientImpl类的成员函数attachRootGraphicsLayer的实现例如以下所看到的:

void ChromeClientImpl::attachRootGraphicsLayer(GraphicsLayer* rootLayer)
{
m_webView->setRootGraphicsLayer(rootLayer);
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/ChromeClientImpl.cpp中。

ChromeClientImpl类的成员变量m_webView指向的是一个WebViewImpl对象。这个WebViewImpl对象的创建过程能够參考前面Chromium网页Frame Tree创建过程分析一文。ChromeClientImpl类的成员函数attachRootGraphicsLayer所做的事情就是调用这个WebViewImpl对象的成员函数setRootGraphicsLayer,以便将Graphics Layer Tree的根节点传递给它处理。

WebViewImpl类的成员函数setRootGraphicsLayer的实现例如以下所看到的:

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特性的很多其他信息。能够參考官方文档:Layer-based Solution for Pinch Zoom / Fixed Position

为简单起见,我们假设没有设置"enable-pinch-virtual-viewport"启动选项。这时候WebViewImpl类的成员函数setRootGraphicsLayer会将參数layer指向的一个Graphics Layer,也就是Graphics Layer Tree的根节点,保存在成员变量m_rootGraphicsLayer中,而且调用它的成员函数platformLayer获得与它关联的一个WebLayerImpl对象,保存在另外一个成员变量m_rootLayer中。

再接下来。WebViewImpl类的成员函数setRootGraphicsLayer调用另外一个成员函数setIsAcceleratedCompositingActive激活网页的硬件加速渲染,它的实现例如以下所看到的:

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网页Frame Tree创建过程分析一文。它指向的是一个在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的实现例如以下所看到的:

void RenderViewImpl::initializeLayerTreeView() {
RenderWidget::initializeLayerTreeView();
......
}

这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。

RenderViewImpl类的成员函数initializeLayerTreeView主要是调用父类RenderWidget的成员函数initializeLayerTreeView初始化一个CC Layer Tree,例如以下所看到的:

void RenderWidget::initializeLayerTreeView() {
compositor_ = RenderWidgetCompositor::Create(
this, is_threaded_compositing_enabled_);
......
if (init_complete_)
StartCompositor();
}

这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

RenderWidget类的成员函数initializeLayerTreeView首先是调用RenderWidgetCompositor类的静态成员函数Create创建一个RenderWidgetCompositor对象,而且保存在成员变量compositor_中。在创建这个RenderWidgetCompositor对象期间,也会伴随着创建一个CC Layer Tree。

RenderWidget类有一个成员变量init_complete_,当它的值等于true的时候,表示Browser进程已经为当前正在载入的网页初始化好Render View,这时候RenderWidget类的成员函数initializeLayerTreeView就会调用另外一个成员函数StartCompositor激活前面Chromium网页载入过程简要介绍和学习计划一文中提到的调度器,表示它能够開始进行调度工作了。

接下来我们先分析RenderWidget类的成员变量init_complete_被设置为true的过程。从前面Chromium网页Frame Tree创建过程分析一文能够知道,当Browser进程为在Render进程中载入的网页创建了一个Render View之后,会向Render进程发送一个类型为ViewMsg_New的消息。这个IPC消息被RenderThreadImpl类的成员函数OnCreateNewView处理。

在处理期间,会创建一个RenderViewImpl对象,而且调用它的成员函数Initialize对其进行初始化,例如以下所看到的:

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_impl.cc中。

RenderViewImpl类的成员函数Initialize的具体分析能够參考前面Chromium网页Frame Tree创建过程分析一文。这里我们看到,当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继承下来的,它的实现例如以下所看到的:

void RenderWidget::CompleteInit() {
...... init_complete_ = true; if (compositor_)
StartCompositor(); ......
}

这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

从这里就能够看到,RenderWidget类的成员变量init_complete_将会被设置为true,而且在成员变量compositor_的值不等于NULL的情况下。会调用前面提到的成员函数StartCompositor激活前面Chromium网页载入过程简要介绍和学习计划一文中提到的调度器。

回到前面分析的RenderWidget类的成员函数initializeLayerTreeView中。我们假设正在载入的网页不是在一个Popup Window显示,因此当RenderWidget类的成员函数initializeLayerTreeView被调用时,Browser进程已经为正在载入的网页初始化好了Render View,这意味着此时RenderWidget类的成员变量init_complete_已经被设置为true,于是RenderWidget类的成员函数initializeLayerTreeView就会先调用RenderWidgetCompositor类的静态成员函数Create创建一个RenderWidgetCompositor对象,然后再调用另外一个成员函数StartCompositor激活前面Chromium网页载入过程简要介绍和学习计划一文中提到的调度器。接下来我们就先分析RenderWidgetCompositor类的静态成员函数Create的实现,在接下来一篇文章中再分析RenderWidget类的成员函数StartCompositor激活调度器的过程。

RenderWidgetCompositor类的静态成员函数Create的实现例如以下所看到的:

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);
settings.initial_debug_state.show_screen_space_rects =
cmd->HasSwitch(cc::switches::kShowScreenSpaceRects);
settings.initial_debug_state.show_replica_screen_space_rects =
cmd->HasSwitch(cc::switches::kShowReplicaScreenSpaceRects);
settings.initial_debug_state.show_occluding_rects =
cmd->HasSwitch(cc::switches::kShowOccludingRects);
settings.initial_debug_state.show_non_occluding_rects =
cmd->HasSwitch(cc::switches::kShowNonOccludingRects);
...... compositor->Initialize(settings); return compositor.Pass();
}

这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

RenderWidgetCompositor类的静态成员函数Create首先创建一个RenderWidgetCompositor对象。接着依据Render进程的启动选项初始化一个LayerTreeSettings对象,最后以这个LayerTreeSettings对象为參数,对前面创建的RenderWidgetCompositor对象进行初始化。这是通过调用RenderWidgetCompositor类的成员函数Initialize实现的。

接下来我们先分析RenderWidgetCompositor对象的创建过程,也就是RenderWidgetCompositor类的构造函数的实现。接下来再分析RenderWidgetCompositor对象的初始化过程,也就是RenderWidgetCompositor类的成员函数Initialize的实现。

RenderWidgetCompositor类的构造函数的实现例如以下所看到的:

RenderWidgetCompositor::RenderWidgetCompositor(RenderWidget* widget,
bool threaded)
: threaded_(threaded),
......,
widget_(widget) {
}

这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

RenderWidgetCompositor类的构造函数主要是将參数widget指向的RenderViewImpl对象保存在成员变量widget_中,而且将參数threaded的值保存在成员变量threaded_中。參数threaded用来描写叙述Render进程是否要採用线程化渲染,也就是是否须要创建一个Compositor线程来专门运行渲染相关的工作。

从前面的调用过程能够知道。參数threaded是从RenderWidget类的成员函数initializeLayerTreeView中传递过来的。它的值等于RenderWidget类的成员变量is_threaded_compositing_enabled_的值。RenderWidget类的成员变量is_threaded_compositing_enabled_是在构造函数初始化的。例如以下所看到的:

RenderWidget::RenderWidget(blink::WebPopupType popup_type,
const blink::WebScreenInfo& screen_info,
bool swapped_out,
bool hidden,
bool never_visible)
: ...... {
......
is_threaded_compositing_enabled_ =
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableThreadedCompositing);
}

这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

这意味着当Render进程设置了“enable-threaded-compositing”启动选项时,Render进程就会採用线程化渲染机制。我们接下来以及以后的文章仅仅考虑线程化渲染机制这样的情况。

回到RenderWidgetCompositor类的构造函数中,这意味着它的成员变量threaded_会被设置为true。

接下来我们继续分析RenderWidgetCompositor对象的初始化过程,也就是RenderWidgetCompositor类的成员函数Initialize的实现,例如以下所看到的:

void RenderWidgetCompositor::Initialize(cc::LayerTreeSettings settings) {
scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy;
RenderThreadImpl* render_thread = RenderThreadImpl::current();
......
// render_thread may be NULL in tests.
if (render_thread) {
compositor_message_loop_proxy =
render_thread->compositor_message_loop_proxy();
......
}
if (compositor_message_loop_proxy.get()) {
layer_tree_host_ = cc::LayerTreeHost::CreateThreaded(
this, shared_bitmap_manager, settings, compositor_message_loop_proxy);
} else {
layer_tree_host_ = cc::LayerTreeHost::CreateSingleThreaded(
this, this, shared_bitmap_manager, settings);
}
......
}

这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

RenderWidgetCompositor类的成员函数Initialize首先调用RenderThreadImpl类的静态成员函数current获得一个RenderThreadImpl对象。这个RenderThreadImpl对象描写叙述的实际上是Render进程的Render线程,也就是Main线程。

这个Main线程是在Render进程启动的时候创建的。是一定会存在的,具体能够參考Chromium的Render进程启动过程分析一文。

RenderWidgetCompositor类的成员函数Initialize接下来调用前面获得的RenderThreadImpl对象的成员函数compositor_message_loop_proxy获得Render进程的Compositor线程的消息循环代理对象。

当这个消息循环代理对象存在时,就会调用cc::LayerTreeHost类的静态成员函数CreateThreaded创建一个支持线程化渲染的LayerTreeHost对象,而且保存在成员变量layer_tree_host_中。还有一方面,假设Render进程中不存在Compositor线程。那么RenderWidgetCompositor类的成员函数Initialize就会调用cc::LayerTreeHost类的静态成员函数CreateSingleThreaded创建一个不支持线程化渲染的LayerTreeHost对象。在后一种情况下,全部的渲染操作都将在Render进程的Main线程中运行。

接下来我们首先分析Render进程的Compositor线程的创建过程。前面提到。当Browser进程为Render进程中载入的网页创建了一个Render View时,就会发送一个类型为ViewMsg_New的IPC消息给Render进程。

Render进程通过RenderThreadImpl类的成员函数OnCreateNewView接收和处理该消息。例如以下所看到的:

void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) {
EnsureWebKitInitialized();
// When bringing in render_view, also bring in webkit's glue and jsbindings.
RenderViewImpl::Create(params.opener_route_id,
params.window_was_created_with_opener,
params.renderer_preferences,
params.web_preferences,
params.view_id,
params.main_frame_routing_id,
params.surface_id,
params.session_storage_namespace_id,
params.frame_name,
false,
params.swapped_out,
params.proxy_routing_id,
params.hidden,
params.never_visible,
params.next_page_id,
params.screen_info,
params.accessibility_mode);
}

这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

除了调用RenderViewImpl类的静态成员函数Create创建一个RenderViewImpl对象,RenderThreadImpl类的成员函数OnCreateNewView还会调用另外一个成员函数EnsureWebKitInitialized确保WebKit已经初始化好。

RenderThreadImpl类的成员函数EnsureWebKitInitialized的实现例如以下所看到的:

void RenderThreadImpl::EnsureWebKitInitialized() {
if (webkit_platform_support_)
return; webkit_platform_support_.reset(new RendererWebKitPlatformSupportImpl);
blink::initialize(webkit_platform_support_.get());
...... const CommandLine& command_line = *CommandLine::ForCurrentProcess(); bool enable = command_line.HasSwitch(switches::kEnableThreadedCompositing);
if (enable) {
...... if (!compositor_message_loop_proxy_.get()) {
compositor_thread_.reset(new base::Thread("Compositor"));
compositor_thread_->Start();
......
compositor_message_loop_proxy_ =
compositor_thread_->message_loop_proxy();
......
} ......
} ......
}

这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

除了运行初始化WebKit的工作,RenderThreadImpl类的成员函数EnsureWebKitInitialized还做的另外一件重要事情是检查Render进程是否设置了“enable-threaded-compositing”启动选项。

假设设置了,那么就会创建和启动一个Compositor线程,而且将这个Compositor线程的消息循环代理对象保存在成员变量compositor_message_loop_proxy_中。这样当RenderThreadImpl类的成员函数compositor_message_loop_proxy被调用时。Compositor线程的消息循环代理对象就会被返回给调用者。例如以下所看到的:

class CONTENT_EXPORT RenderThreadImpl : public RenderThread,
public ChildThread,
public GpuChannelHostFactory {
public:
...... scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy() const {
return compositor_message_loop_proxy_;
} ......
};

这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.h中。

有了这个消息循环代理对象之后,就能够向Compositor线程发送消息请求其运行相应的操作了。

回到RenderWidgetCompositor类的成员函数Initialize,它获得了Compositor线程的消息循环代理对象之后。接下来就调用cc::LayerTreeHost类的静态成员函数CreateThreaded创建一个支持线程化渲染的LayerTreeHost对象,例如以下所看到的:

scoped_ptr<LayerTreeHost> LayerTreeHost::CreateThreaded(
LayerTreeHostClient* client,
SharedBitmapManager* manager,
const LayerTreeSettings& settings,
scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) {
DCHECK(impl_task_runner);
scoped_ptr<LayerTreeHost> layer_tree_host(
new LayerTreeHost(client, manager, settings));
layer_tree_host->InitializeThreaded(impl_task_runner);
return layer_tree_host.Pass();
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

LayerTreeHost类的静态成员函数CreateThreaded首先创建了一个LayerTreeHost对象。例如以下所看到的:

LayerTreeHost::LayerTreeHost(LayerTreeHostClient* client,
SharedBitmapManager* manager,
const LayerTreeSettings& settings)
: ......,
client_(client),
...... {
......
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

从前面的调用过程能够知道,參数client指向的是一个RenderWidgetCompositor对象,这个RenderWidgetCompositor对象将会被保存LayerTreeHost类的成员变量client_中。

回到LayerTreeHost类的静态成员函数CreateThreaded中。它创建了一个LayerTreeHost对象之后,接下来会调用它的成员函数InitializeThreaded对其进行初始化,例如以下所看到的:

void LayerTreeHost::InitializeThreaded(
scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) {
InitializeProxy(ThreadProxy::Create(this, impl_task_runner));
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

从前面的调用过程能够知道,參数impl_task_runner描写叙述的是Compositor线程的消息循环,LayerTreeHost类的成员函数InitializeThreaded首先调用ThreadProxy类的静态成员函数Create创建一个ThreadProxy对象。接着用这个ThreadProxy对象初始化当前正在处理的LayerTreeHost对象,这是通过调用LayerTreeHost类的成员函数IntializeProxy实现的。

接下来我们先分析ThreadProxy类的静态成员函数Create创建ThreadProxy对象的过程,接着再分析LayerTreeHost类的成员函数IntializeProxy初始化LayerTreeHost对象的过程。

ThreadProxy类的静态成员函数Create的实现例如以下所看到的:

scoped_ptr<Proxy> ThreadProxy::Create(
LayerTreeHost* layer_tree_host,
scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) {
return make_scoped_ptr(new ThreadProxy(layer_tree_host, impl_task_runner))
.PassAs<Proxy>();
}

这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

从这里能够看到,ThreadProxy类的静态成员函数Create创建了一个ThreadProxy对象返回给调用者。

ThreadProxy对象的创建过程,即ThreadProxy类的构造函数的实现,例如以下所看到的:

ThreadProxy::ThreadProxy(
LayerTreeHost* layer_tree_host,
scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner)
: Proxy(impl_task_runner),
...... {
......
}

这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

ThreadProxy类的构造函数主要是调用了父类Proxy的构造函数运行初始化工作,例如以下所看到的:

Proxy::Proxy(scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner)
: main_task_runner_(base::MessageLoopProxy::current()),
......
impl_task_runner_(impl_task_runner),
...... {
......
}

这个函数定义在文件external/chromium_org/cc/trees/proxy.cc中。

从前面的分析能够知道,參数impl_task_runner描写叙述的是Compositor线程的消息循环,它将被保存在Proxy类的成员变量impl_task_runner_中。此外,Proxy类的构造函数还会通过调用MessageLoopProxy类的静态成员函数current获得当前线程的消息循环,而且保存在成员变量main_task_runner_。当前线程即为Render进程的Main线程,因此Proxy类的成员变量main_task_runner_描写叙述的Main线程的消息循环。

初始化好Proxy类的成员变量main_task_runner_和impl_task_runner_之后。以后就能够通过调用Proxy类的成员函数MainThreadTaskRunner和ImplThreadTaskRunner获得它们描写叙述的线程的消息循环,例如以下所看到的:

base::SingleThreadTaskRunner* Proxy::MainThreadTaskRunner() const {
return main_task_runner_.get();
} ...... base::SingleThreadTaskRunner* Proxy::ImplThreadTaskRunner() const {
return impl_task_runner_.get();
}

这两个函数定义在文件external/chromium_org/cc/trees/proxy.cc中。      

回到LayerTreeHost类的成员函数InitializeThreaded中,它创建了一个ThreadProxy对象之后。接下来就会调用另外一个成员函数InitializeProxy启动前面创建的ThreadProxy对象,例如以下所看到的:

void LayerTreeHost::InitializeProxy(scoped_ptr<Proxy> proxy) {
...... proxy_ = proxy.Pass();
proxy_->Start();
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

LayerTreeHost类的成员函数InitializeProxy首先将參数proxy描写叙述的一个ThreadProxy对象保存在成员变量proxy_中,接着再调用上述ThreadProxy对象的成员函数Start对它进行启动,例如以下所看到的:

void ThreadProxy::Start() {
...... CompletionEvent completion;
Proxy::ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&ThreadProxy::InitializeImplOnImplThread,
base::Unretained(this),
&completion));
completion.Wait(); main_thread_weak_ptr_ = main().weak_factory.GetWeakPtr(); main().started = true;
}

这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

ThreadProxy类的成员函数Start主要是向Compositor线程的消息队列发送了一个Task,而且等待这个Task完毕。这个Task绑定了ThreadProxy类的成员函数InitializeImplOnImplThread,因此接下来ThreadProxy类的成员函数InitializeImplOnImplThread就会在Compositor线程中运行,例如以下所看到的:

void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) {
......
impl().layer_tree_host_impl =
layer_tree_host()->CreateLayerTreeHostImpl(this); SchedulerSettings scheduler_settings(layer_tree_host()->settings());
impl().scheduler = Scheduler::Create(this,
scheduler_settings,
impl().layer_tree_host_id,
ImplThreadTaskRunner());
...... impl_thread_weak_ptr_ = impl().weak_factory.GetWeakPtr();
completion->Signal();
}

这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

ThreadProxy类的成员函数InitializeImplOnImplThread主要是做了三件事情。

第一件事情是调用前面创建的LayerTreeHost对象的成员函数CreateLayerTreeHostImpl函数创建了一个LayerTreeHostImpl对象,而且保存在内部的一个CompositorThreadOnly对象的成员变量layer_tree_host_impl中。

前面创建的LayerTreeHost对象能够通过调用成员函数layer_tree_host获得。

内部的CompositorThreadOnly对象能够通过调用成员函数impl获得。创建出来的LayerTreeHostImpl对象以后负责管理CC Pending Layer Tree和CC Active Layer Tree。

第二件事情是调用Scheduler类的静态成员函数Create创建了一个Scheduler对象。这个Scheduler对象以后就负责在Main线程与Compositor线程之间调度渲染工作。

第三件事情是将參数completion描写叙述的Completion Event设置为有信号,这样正在等待的Main线程就能够唤醒继续运行其他工作了。

接下来我们继续分析LayerTreeHost类的成员函数CreateLayerTreeHostImpl创建LayerTreeHostImpl对象的过程。例如以下所看到的:

scoped_ptr<LayerTreeHostImpl> LayerTreeHost::CreateLayerTreeHostImpl(
LayerTreeHostImplClient* client) {
......
scoped_ptr<LayerTreeHostImpl> host_impl =
LayerTreeHostImpl::Create(settings_,
client,
proxy_.get(),
rendering_stats_instrumentation_.get(),
shared_bitmap_manager_,
id_);
host_impl->SetUseGpuRasterization(UseGpuRasterization());
......
return host_impl.Pass();
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

LayerTreeHost类的成员函数CreateLayerTreeHostImpl首先是调用LayerTreeHostImpl类的静态成员函数Create创建了一个LayerTreeHostImpl对象,接着再调用这个LayerTreeHostImpl对象的成员函数SetUseGpuRasterization设置它是否使用GPU光栅化。

LayerTreeHostImpl对象的创建过程。即LayerTreeHostImpl类的构造函数的实现,例如以下所看到的:

LayerTreeHostImpl::LayerTreeHostImpl(
const LayerTreeSettings& settings,
LayerTreeHostImplClient* client,
Proxy* proxy,
RenderingStatsInstrumentation* rendering_stats_instrumentation,
SharedBitmapManager* manager,
int id)
: client_(client),
proxy_(proxy),
...... { ....... // LTHI always has an active tree.
active_tree_ = LayerTreeImpl::create(this); ......
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host_impl.cc中。

从前面的调用过程能够知道,參数client和proxy指向的是同一个ThreadProxy对象。它们分别保存在LayerTreeHostImpl类的成员变量client_和proxy_中。LayerTreeHostImpl类的构造函数接下来还调用LayerTreeImpl类的静态成员函数create创建了一个Active Layer Tree。注意,这时候Pending Layer Tree还没有创建。等到要将Layer Tree内容同步到Pending Layer Tree的时候才会创建。

回到LayerTreeHost类的成员函数CreateLayerTreeHostImpl,它创建了一个LayerTreeHostImpl对象之后,接下来调用另外一个成员函数UseGpuRasterization获取Render进程是否要採用GPU光栅化的信息。例如以下所看到的:

bool LayerTreeHost::UseGpuRasterization() const {
if (settings_.gpu_rasterization_forced) {
return true;
} else if (settings_.gpu_rasterization_enabled) {
return has_gpu_rasterization_trigger_ &&
content_is_suitable_for_gpu_rasterization_;
} else {
return false;
}
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

当Render进程设置了"force-gpu-rasterization"和"enable-impl-side-painting"启动选项时,LayerTreeHost类的成员变量settings_指向的LayerTreeSettings对象的成员变量gpu_rasterization_forced的值就会等于true,这时候就会强制使用GPU光栅化。

当Render进程设置了"enable-gpu-rasterization"启动选项时,LayerTreeHost类的成员变量settings_指向的LayerTreeSettings对象的成员变量gpu_rasterization_enabled的值就会等于true,这时候是否使用GPU光栅化取决于LayerTreeHost类另外两个成员变量has_gpu_rasterization_trigger_和content_is_suitable_for_gpu_rasterization_。

当这两个成员变量的值均等于true的时候,就会使作GPU光栅化。

LayerTreeHost类的成员变量has_gpu_rasterization_trigger_的值由WebKit间接调用LayerTreeHost类的成员函数SetHasGpuRasterizationTrigger进行设置,这一点能够參考WebViewImpl类的成员函数updatePageDefinedViewportConstraints,主要是与网页的Viewport大小以及当前的缩放因子有关。

LayerTreeHost类的成员变量content_is_suitable_for_gpu_rasterization_的值由Skia决定,后者依据当前要绘制的内容决定的,比如。假设要绘制的内容包括太多的凹多边形。而且这些凹多边形使用了反锯齿效果。那么就会禁用GPU光栅化。

这一点能够參考SkPicturePlayback类的成员函数suitableForGpuRasterization。

在其他情况下,都是禁止使用GPU光栅化的。在眼下的版本号中。Render进程设置了"force-gpu-rasterization"和"enable-impl-side-painting"启动选项,因此会强制使用GPU光栅化。

在以后的文章中,我们都是假设Chromium是使用GPU光栅化的。

再回到LayerTreeHost类的成员函数CreateLayerTreeHostImpl,它获得Render进程是否要採用GPU光栅化的信息之后,就会设置给前面创建的LayerTreeHostImpl对象,这是通过调用LayerTreeHostImpl类的成员函数SetUseGpuRasterization例如以下所看到的:

void LayerTreeHostImpl::SetUseGpuRasterization(bool use_gpu) {
if (use_gpu == use_gpu_rasterization_)
return; use_gpu_rasterization_ = use_gpu;
ReleaseTreeResources(); // Replace existing tile manager with another one that uses appropriate
// rasterizer.
if (tile_manager_) {
DestroyTileManager();
CreateAndSetTileManager();
} // We have released tilings for both active and pending tree.
// We would not have any content to draw until the pending tree is activated.
// Prevent the active tree from drawing until activation.
active_tree_->SetRequiresHighResToDraw();
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host_impl.cc中。

LayerTreeHostImpl类的成员函数SetUseGpuRasterization会将參数use_gpu的值保存在成员变量use_gpu_rasterization_。

假设这时候已经创建了分块管理器。即LayerTreeHostImpl类的成员变量tile_manager_的值不等于NULL,那么还会先销毁它,然后再又一次创建。由于分块管理器在创建的时候就决定了光栅化的方式。最后,LayerTreeHostImpl类的成员函数SetUseGpuRasterization还会要求又一次绘制高分辨率的Active Layer Tree。

这一步运行完毕之后。CC Layer Tree就初始化完毕了。回到前面分析的WebViewImpl类的成员函数setIsAcceleratedCompositingActive中,它接下来会调用成员变量m_client指向的RenderViewImpl对象的成员函数layerTreeView获得之前创建的一个RenderWidgetCompositor对象。

RenderViewImpl类的成员函数layerTreeView是从父类RenderWidget继承下来的。它的实现例如以下所看到的:

blink::WebLayerTreeView* RenderWidget::layerTreeView() {
return compositor_.get();
}

这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

从这里能够看到,RenderWidget类的成员函数layerTreeView返回的是成员变量compositor_描写叙述的一个RenderWidgetCompositor对象。

再回到前面分析的WebViewImpl类的成员函数setIsAcceleratedCompositingActive,它获得了一个RenderWidgetCompositor对象,就会调用这个RenderWidgetCompositor对象的成员函数setRootLayer,以便将CC Layer Tree的根节点传递给它处理,例如以下所看到的:

void RenderWidgetCompositor::setRootLayer(const blink::WebLayer& layer) {
layer_tree_host_->SetRootLayer(
static_cast<const WebLayerImpl*>(&layer)->layer());
}

这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

从前面的调用过程能够知道,參数layer描写叙述的实际上是一个WebLayerImpl对象,调用它的成员函数layer能够获得与它关联的一个PictureLayer对象。这个PictureLayer对象将作为CC Layer Tree的根节点,设置给RenderWidgetCompositor类的成员变量layer_tree_host_描写叙述的一个LayerTreeHost对象。

这是通过调用LayerTreeHost类的成员函数SetRootLayer实现的。例如以下所看到的:

void LayerTreeHost::SetRootLayer(scoped_refptr<Layer> root_layer) {
...... root_layer_ = root_layer;
...... SetNeedsFullTreeSync();
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

从这里能够看到,LayerTreeHost类的成员函数SetRootLayer将CC Layer Tree的根节点保存在成员变量root_layer_中,以后通过这个成员变量就能够遍历整个CC Layer Tree。

由于设置了新的CC Layer Tree,LayerTreeHost类的成员函数SetRootLayer还会调用另外一个成员函数SetNeedsFullTreeSync请求将CC Layer Tree同步到CC Pending Layer Tree去。

前面我们已经分析过LayerTreeHost类的成员函数SetNeedsFullTreeSync的实现了,它调用了另外一个成员函数SetNeedsCommit请求将CC Layer Tree同步到CC Pending Layer Tree去,也就运行一次ACTION_COMMIT操作,后者的实现例如以下所看到的:

void LayerTreeHost::SetNeedsCommit() {
......
proxy_->SetNeedsCommit();
......
}

这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

LayerTreeHost类的成员函数SetNeedsCommit调用成员变量proxy_指向的一个ThreadProxy对象的成员函数SetNeedsCommit请求运行一次ACTION_COMMIT操作。例如以下所看到的:

void ThreadProxy::SetNeedsCommit() {
...... if (main().commit_requested)
return;
......
main().commit_requested = true; SendCommitRequestToImplThreadIfNeeded();
}

这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

ThreadProxy对象的成员函数SetNeedsCommit首先推断之前请求的ACTION_COMMIT操作是否已经被运行。

假设还没有被运行,那么就会忽略当前请求。实际上是将多个ACTION_COMMIT请求合成一个运行。其实。每当WebKit改动了Render Object Tree的内容时。都会请求CC模块运行一次ACTION_COMMIT操作。只是,假设这些ACTION_COMMIT操作请求得太过频繁,就会合成一个一起运行。

假设之前没有请求过ACTION_COMMIT操作,或者之前请求的ACTION_COMMIT操作已经被运行,那么ThreadProxy类的成员函数SetNeedsCommit就会调用另外一个成员函数SendCommitRequestToImplThreadIfNeeded请求Compositor线程运行一次ACTION_COMMIT请求,例如以下所看到的:

void ThreadProxy::SendCommitRequestToImplThreadIfNeeded() {
DCHECK(IsMainThread());
if (main().commit_request_sent_to_impl_thread)
return; main().commit_request_sent_to_impl_thread = true;
Proxy::ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&ThreadProxy::SetNeedsCommitOnImplThread,
impl_thread_weak_ptr_));
}

这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

ThreadProxy类的成员函数SendCommitRequestToImplThreadIfNeeded必须要确保在Main线程中运行。这也是Chromium的多线程编程哲学。在设计上规定了某些对象仅仅能在特定的线程进行訪问。

ThreadProxy类的成员函数SendCommitRequestToImplThreadIfNeeded首先推断Main线程之前是否已经向Compositor线程请求过运行ACTION_COMMIT操作。而且该请求已经被运行。假设是的话,那么就会忽略当前请求。否则的话,就会向Compositor线程的消息队列发送一个Task,这个Task绑定了ThreadProxy类的成员函数SetNeedsCommitOnImplThread。

这意味着接下来ThreadProxy类的成员函数SetNeedsCommitOnImplThread接下来会在Compositor线程中运行。例如以下所看到的:

void ThreadProxy::SetNeedsCommitOnImplThread() {
......
DCHECK(IsImplThread());
impl().scheduler->SetNeedsCommit();
}

这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

Compositor线程并非立即就运行请求的ACTION_COMMIT操作,它仅仅是调用了内部的调度器的成员函数SetNeedsCommit,即Scheduler类的成员函数SetNeedsCommit,例如以下所看到的:

void Scheduler::SetNeedsCommit() {
state_machine_.SetNeedsCommit();
ProcessScheduledActions();
}

这个函数定义在文件external/chromium_org/cc/scheduler/scheduler.cc中。

Scheduler类的成员函数SetNeedsCommit首先通过调用成员变量state_machine_描写叙述的一个SchedulerStateMachine对象的成员函数SetNeedsCommit将内部的状态机设置为须要COMMIT,例如以下所看到的:

void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; }

这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。

SchedulerStateMachine类的成员函数SetNeedsCommit仅仅是将成员变量needs_commit_的值设置为true,表示须要运行一次ACTION_COMMIT操作。只是,这个ACTION_COMMIT有可能不能立即运行。由于有可能CC模块还没有为网页创建Output Surface,或者CC模块如今须要对网页上一帧运行光栅化操作等等。调度器当前究竟须要运行什么样的操作。由Scheduler类的成员函数SetNeedsCommit调用另外一个成员函数ProcessScheduledActions决定。

至此。我们就分析完毕Chromium为网页创建CC Layer Tree的过程了。有了网页的CC Layer Tree之后,Chromium还要为网页创建渲染上下文,也就是Output Surface,这样才干够将网页的内容渲染出来。网页Output Surface的创建以及网页其他渲染相关的操作。都是通过一个调度器依据一定的策略触发运行的。因此在接下来的一篇文章中,我们就继续分析Chromium网页渲染调度器的运行过程。敬请关注!很多其他的信息也能够关注老罗的新浪微博:http://weibo.com/shengyangluo

Chromium网页Layer Tree创建过程分析的更多相关文章

  1. Chromium网页Frame Tree创建过程分析

         Chromium在加载一个网页之前,需要在Browser进程创建一个Frame Tree.Browser进程为网页创建了Frame Tree之后,再请求Render进程加载其内容.Frame ...

  2. Chromium网页Graphics Layer Tree创建过程分析

    在前面一文中.我们分析了网页Render Layer Tree的创建过程.在创建Render Layer的同一时候,WebKit还会为其创建Graphics Layer.这些Graphics Laye ...

  3. Chromium网页URL载入过程分析

    Chromium在Browser进程中为网页创建了一个Frame Tree之后,会将网页的URL发送给Render进程进行载入.Render进程接收到网页URL载入请求之后,会做一些必要的初始化工作, ...

  4. Chromium网页输入事件捕捉和手势检測过程分析

    连续的输入事件可能会产生一定的手势操作.比如滑动手势和捏合手势. 在Chromium中,网页的输入事件是在Browser进程中捕捉的.Browser进程捕获输入事件之后,会进行手势操作检測.检測出来的 ...

  5. Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8303098 在前文中,我们分析了应用程序窗口连 ...

  6. Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

    从前文可知道,每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口.每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图.应用程序窗口视图是真正用来 ...

  7. Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析(转)

    在前文中,我们分析了Android应用程序窗口的运行上下文环境的创建过程.由此可知,每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一 ...

  8. Dalvik虚拟机进程和线程的创建过程分析

    从前面Dalvik虚拟机的运行过程分析一文可以知道,Dalvik虚拟机除了可以执行Java代码之外,还可以执行Native代码,也就是C/C++函数. 这些C/C++函数在执行的过程中,又可以通过本地 ...

  9. Android应用程序资源管理器(Asset Manager)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8791064 在前面一篇文章中,我们分析了And ...

随机推荐

  1. STM32 USB FS Core and USB OTG Core

    STM32 USB-FS-Device development kit Compatible with the STM32F102xx and STM32F103xx series, STM32 L1 ...

  2. GO语言基础之method

    方法 method 1. Go 中虽没有 class,但依旧有 method 2. 通过显示说明 receiver 来实现与某个类型的组合 3. 只能为同一个包中的类型定义方法 4. Receiver ...

  3. TQ2440开发板存储器

    TX2440A与TQ2440A开发板使用核心板完全相同 有过51单片基础的同学应该都会看懂下图,先看下图,对实验板存储器分布有一个整体印象: s3c2440存储器概述: 1.S3C2440A的存储器管 ...

  4. java容器类---概述

    1.容器类关系图 虚线框表示接口. 实线框表示实体类. 粗线框表示最经常使用的实体类. 点线的箭头表示实现了这个接口. 实线箭头表示类能够制造箭头所指的那个类的对象. Java集合工具包位于Java. ...

  5. Revit Family API 添加对齐

    没测试成功,留待以后研究. [TransactionAttribute(Autodesk.Revit.Attributes.TransactionMode.Manual)] ; ; i < nV ...

  6. Win10系统下如何禁止同步主机session?windows 10禁止同步主机session的方法

    近来,有些刚刚升级Win10正式版的用户反映自己的电脑开机时有个同步主机session启动项占用了将近半分钟,而选择用360禁止后,下次会出现同步主机session3,再禁止下次又会出现同步主机ses ...

  7. How AOT compares to a traditional JIT compiler

    Ahead-of-Time (AOT) compilation is in contrast to Just-in-Time compilation (JIT). In a nutshell, .NE ...

  8. 使用cat读取和echo写内核文件节点的一些问题

    span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror ...

  9. Task Parallel Library01,基本用法

    我们知道,每个应用程序就是一个进程,一个进程有多个线程.Task Parallel Library为我们的异步编程.多线程编程提供了强有力的支持,它允许一个主线程运行的同时,另外的一些线程或Task也 ...

  10. C#编程(五十五)----------HashSet和SortedSet

    集 饱含不重复元素的集合称为”集(set)”. .NET4包含两个集(HashSet<T>和SortedSet<T>),他们都实现ISet<T>接口.HashSet ...