不是所有的RenderLayer都需要创建它的Backing Store,只有网页的RenderObject树之RenderLayer满足如下条件:
1 Transform:几何变换
2 Video:页面有<video>
3 Canvas: 页面有<canvas>
4 Plugin
5 Frame
6 3DTransforms
7 Animation
8 Filters:CSS过滤器
9 Position:CSS之定位
10 clipsCompositingDescendants:裁剪区合成到子孙
// Note: this specifies whether the RL needs a compositing layer for intrinsic reasons.
// Use needsToBeComposited() to determine if a RL actually needs a compositing layer.
// static
bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) const
{
RenderObject* renderer = layer->renderer();
// The compositing state of a reflection should match that of its reflected layer.
if (layer->isReflection()) {
renderer = renderer->parent(); // The RenderReplica's parent is the object being reflected.
layer = toRenderBoxModelObject(renderer)->layer();
}
// The root layer always has a compositing layer, but it may not have backing.
return requiresCompositingForTransform(renderer)
|| requiresCompositingForVideo(renderer)
|| requiresCompositingForCanvas(renderer)
|| requiresCompositingForPlugin(renderer)
|| requiresCompositingForFrame(renderer)
|| (canRender3DTransforms() && renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden)
|| clipsCompositingDescendants(layer)
|| requiresCompositingForAnimation(renderer)
|| requiresCompositingForFilters(renderer)
|| requiresCompositingForPosition(renderer, layer);
}
每次创建DOM节点的时候,发现存在样式( Style)或者样式被改变,webkit就创建相应RenderObject节点并设置其新样式。同时,要求更新本RenderObject节点的父亲--整颗RenderLayer树的样式,RenderLayer就会更新合成器的状态。
创建RenderObject并且设置新样式:
#8 WebCore::RenderObject::setStyle (this=0xc16f18, style=...)RenderObject.cpp:1744
#9 WebCore::RenderObject::setAnimatableStyle (this=0xc16f18, style=...) RenderObject.cpp:1645
#10 WebCore::NodeRendererFactory::createRenderer (this)WebCore/dom/NodeRenderingContext.cpp:291
#11 WebCore::NodeRendererFactory::createRendererIfNeeded (this=0x7fffffffcb50)NodeRenderingContext.cpp:324
#12 WebCore::Node::createRendererIfNeeded (this=0xc0aa80) WebCore/dom/Node.cpp:1378
#13 WebCore::Element::attach (this=0xc0aa80) at WebKit/Source/WebCore/dom/Element.cpp:941
#14 WebCore::executeTask (task=...) WebCore/html/parser/HTMLConstructionSite.cpp:102
#15 WebCore::HTMLConstructionSite::executeQueuedTasks (this=) WebCore/html/parser/HTMLConstructionSite.cpp:142
#16 WebCore::HTMLTreeBuilder::constructTreeFromAtomicToken (this=0xc89840, token=) HTMLTreeBuilder.cpp:475
#17 WebCore::HTMLTreeBuilder::constructTreeFromToken (this=0xc89840, rawToken=...)
更新盒子模型:
#5 RenderBoxModelObject::styleDidChange (this, diff=WebCore::StyleDifferenceEqual, oldStyle) RenderBoxModelObject.cpp:445
#6 RenderBox::styleDidChange (this, diff=WebCore::StyleDifferenceEqual, oldStyle=0x0)RenderBox.cpp:233
#7 WebCore::RenderBlock::styleDidChange (this=0xc16f18, diff=WebCore::StyleDifferenceEqual, oldStyle=0x0)RenderBlock.cpp:315
要求更新整颗RenderLayer树的样式:
#4 WebCore::RenderLayer::styleChanged (this=0xc3d838, oldStyle=0x0) WebCore/rendering/RenderLayer.cpp:4886
RenderLayer就会更新合成器的状态: #3 RenderLayerCompositor::updateLayerCompositingState (layer, shouldRepaint=CompositingChangeRepaintNow)RenderLayerCompositor.cpp:558
并首先更新后端存储区:
#2 RenderLayerCompositor::updateBacking (layer, shouldRepaint=CompositingChangeRepaintNow) RenderLayerCompositor.cpp:495
先检查是否需要后端存储区:
bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeRepaint shouldRepaint)
{
bool layerChanged = false;
if (<span style="background-color: rgb(51, 255, 51);">needsToBeComposited</span>(layer)) {
enableCompositingMode();
if (!layer->backing()) {
// If we need to repaint, do so before making backing
if (shouldRepaint == CompositingChangeRepaintNow)
repaintOnCompositingChange(layer);
layer-><span style="background-color: rgb(51, 255, 51);">ensureBacking</span>();
// The RenderLayer's needs to update repaint rects here, because the target
// repaintContainer may have changed after becoming a composited layer.
// https://bugs.webkit.org/show_bug.cgi?id=80641
if (layer->parent())
layer->computeRepaintRects();
layerChanged = true;
}
创建后端存储区的管理类:
RenderLayerBacking* RenderLayer::<span style="background-color: rgb(51, 255, 51);">ensureBacking</span>()
{
if (!m_backing) {
m_backing = adoptPtr(new RenderLayerBacking(this));
compositor()->layerBecameComposited(this);
#if ENABLE(CSS_FILTERS)
updateOrRemoveFilterEffect();
#endif
}
return m_backing.get();
}
创建Backing Store:
RenderLayerBacking::RenderLayerBacking(RenderLayer* layer)
{
<span style="background-color: rgb(51, 255, 51);">createPrimaryGraphicsLayer</span>();
}
<pre name="code" class="cpp">void RenderLayerBacking::createPrimaryGraphicsLayer()
{
String layerName;
#ifndef NDEBUG
layerName = nameForLayer();
#endif
// The call to createGraphicsLayer ends calling back into here as
// a GraphicsLayerClient to ask if it shouldUseTileCache(). We only want
// the tile cache on our main layer. This is pretty ugly, but saves us from
// exposing the API to all clients.
m_creatingPrimaryGraphicsLayer = true;
m_graphicsLayer = <span style="background-color: rgb(51, 255, 51);">createGraphicsLayer</span>(layerName);
}
PassOwnPtr<GraphicsLayer> RenderLayerBacking::createGraphicsLayer(const String& name)
{
OwnPtr<GraphicsLayer> graphicsLayer = <span style="background-color: rgb(51, 255, 51);">GraphicsLayer::create(</span>this);
graphicsLayer->setMaintainsPixelAlignment(compositor()->keepLayersPixelAligned());
return graphicsLayer.release();
}
我们看看chrome的实现:
PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client)
{
return adoptPtr(new GraphicsLayerChromium(client));
}
GraphicsLayerChromium就是Backing Store的抽象类GraphicsLayer之具体实现!
以下是我的测试网页:
<html>
<head>
<style type="text/css">
div, video, canvas
{
-webkit-transform: rotateY(10deg) rotateX(-15deg);
}
</style>
</head>
<body>
<video src=http://media.w3.org/2010/05/sintel/trailer.mp4></video>
<div>
<canvas id="a12d"></canvas><br>
<canvas id="a13d"></canvas>
</div>
<script type="text/javascript">
var size=100;
//2d drawing
var a12dCtx = document.getElementById('a12d').getContext.('2d');
a12dCtx.canvas.width = size;
a12dCtx.canvas.height = size;
a12dCtx.canvas.fillStyle="rgba(0,92,92,80)";
a12dCtx.canvas.fillRect(0,0,100,100);
</script>
</body>
</html>