Commit 73fb2b6d authored by chrishtr@chromium.org's avatar chrishtr@chromium.org

Allow layers that need squashing to draw into the squashing layer in some cases.

In particular, allow a layer L to squash if their clipping
ancestor differs from the squashing owner, but the clipping
ancestor for L is a descendant of a layer that does squash
into the squashing owner.

BUG=368410

Review URL: https://codereview.chromium.org/264013002

git-svn-id: svn://svn.chromium.org/blink/trunk@173690 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent ace011c2
layer at (0,0) size 800x600
RenderView at (0,0) size 800x600
layer at (0,0) size 800x242
RenderBlock {HTML} at (0,0) size 800x242
RenderBody {BODY} at (8,8) size 784x226
RenderText {#text} at (0,0) size 0x0
layer at (20,20) size 100x100
RenderBlock (positioned) {DIV} at (20,20) size 100x100
layer at (18,18) size 202x202 clip at (19,19) size 200x200 scrollY 500 scrollHeight 1000
RenderBlock (relative positioned) {DIV} at (10,10) size 202x202 [border: (1px solid #000000)]
layer at (19,-481) size 200x1000 backgroundClip at (19,19) size 200x200 clip at (19,19) size 200x200 outlineClip at (19,19) size 200x200
RenderBlock (relative positioned) {DIV} at (1,1) size 200x1000 [bgcolor=#0000FF]
RenderBlock {DIV} at (0,0) size 200x500 [bgcolor=#FF0000]
RenderBlock {DIV} at (0,500) size 200x500 [bgcolor=#008000]
<!DOCTYPE html>
<html lang="en">
<head>
<style type="text/css" media="screen">
.container {
display: inline-block;
width: 200px;
height: 200px;
overflow: hidden;
margin: 10px;
border: 1px solid black;
}
.inner {
width: 100%;
height: 1000px;
background-color: blue;
}
.compositing {
position: absolute;
top: 20px;
left: 20px;
width: 100px;
height: 100px;
-webkit-transform: translateZ(0);
}
.top {
height: 50%;
width: 100%;
background-color: red;
}
.bottom {
height: 50%;
width: 100%;
background-color: green;
}
</style>
<script type="text/javascript" charset="utf-8">
if (window.testRunner)
testRunner.waitUntilDone();
function doTest()
{
window.setTimeout(function() {
var scrollables = document.querySelectorAll('.container');
for (var i = 0; i < scrollables.length; ++i)
scrollables[i].scrollTop = 500;
if (window.testRunner)
testRunner.notifyDone();
}, 0);
}
window.addEventListener('load', doTest, false);
</script>
</head>
<body>
<!-- Go into compositing. -->
<div class="compositing"></div>
<!-- Test repainting when the graphicsLayer offsetFromRenderer changes -->
<!-- You should see one green square, and no red -->
<div class="container" style="position: relative;">
<div class="inner" style="position: relative;">
<div class="top"></div>
<div class="bottom"></div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<style type="text/css" media="screen">
.container {
display: inline-block;
width: 200px;
height: 200px;
overflow: hidden;
margin: 10px;
border: 1px solid black;
}
.inner {
width: 100%;
height: 1000px;
background-color: blue;
}
</style>
<!-- Test repainting when the graphicsLayer offsetFromRenderer changes -->
<!-- You should see one green square, and no red -->
<div class="container" style="position: relative;">
<div class="inner" style="position: relative;">
</div>
</div>
<!DOCTYPE html>
<style type="text/css" media="screen">
.container {
display: inline-block;
width: 200px;
height: 200px;
overflow: hidden;
margin: 10px;
border: 1px solid black;
}
.inner {
width: 100%;
height: 1000px;
background-color: blue;
}
.compositing {
position: absolute;
top: 20px;
left: 20px;
width: 100px;
height: 100px;
-webkit-transform: translateZ(0);
}
</style>
<script type="text/javascript" charset="utf-8">
if (window.internals)
window.internals.settings.setLayerSquashingEnabled(true);
</script>
<!-- Go into compositing. -->
<div class="compositing"></div>
<!-- Test repainting when the graphicsLayer offsetFromRenderer changes -->
<!-- You should see one green square, and no red -->
<div class="container" style="position: relative;">
<div class="inner" style="position: relative;">
</div>
</div>
<!DOCTYPE html>
<style type="text/css" media="screen">
.container {
display: inline-block;
width: 200px;
height: 200px;
overflow: hidden;
margin: 10px;
border: 1px solid black;
}
.inner {
width: 100%;
height: 1000px;
background-color: blue;
}
.nextto {
position: absolute;
top: 20px;
left: 20px;
width: 100px;
height: 100px;
}
.bottom {
height: 50%;
width: 100%;
background-color: green;
}
</style>
<div class="container" style="position: relative;">
<div class="inner" style="position: relative;">
<div class="bottom"></div>
</div>
</div>
<!DOCTYPE html>
<style type="text/css" media="screen">
.container {
display: inline-block;
width: 200px;
height: 200px;
overflow: hidden;
margin: 10px;
border: 1px solid black;
}
.inner {
width: 100%;
height: 1000px;
background-color: blue;
}
.compositing {
position: absolute;
top: 20px;
left: 20px;
width: 100px;
height: 100px;
-webkit-transform: translateZ(0);
}
.top {
height: 50%;
width: 100%;
background-color: red;
}
.bottom {
height: 50%;
width: 100%;
background-color: green;
}
</style>
<script type="text/javascript" charset="utf-8">
if (window.testRunner)
testRunner.waitUntilDone();
function doTest()
{
window.setTimeout(function() {
// alert('hi');
var scrollables = document.querySelectorAll('.container');
for (var i = 0; i < scrollables.length; ++i) {
scrollables[i].scrollTop = 500;
}
if (window.testRunner)
testRunner.notifyDone();
}, 0);
}
window.addEventListener('load', doTest, false);
</script>
<!-- Go into compositing. -->
<div class="compositing"></div>
<!-- Test repainting when the graphicsLayer offsetFromRenderer changes -->
<!-- You should see one green square, and no red -->
<div class="container" style="position: relative;">
<div class="inner" style="position: relative;">
<div class="top"></div>
<div class="bottom"></div>
</div>
</div>
\ No newline at end of file
......@@ -595,7 +595,11 @@ void CompositedLayerMapping::updateSquashingLayerGeometry(const IntPoint& delta)
// FIXME: find a better design to avoid this redundant value - most likely it will make
// sense to move the paint task info into RenderLayer's m_compositingProperties.
m_squashedLayers[i].renderLayer->setOffsetFromSquashingLayerOrigin(m_squashedLayers[i].offsetFromRenderer);
}
for (size_t i = 0; i < m_squashedLayers.size(); ++i)
m_squashedLayers[i].localClipRectForSquashedLayer = localClipRectForSquashedLayer(m_squashedLayers[i]);
}
void CompositedLayerMapping::updateGraphicsLayerGeometry(GraphicsLayerUpdater::UpdateType updateType, const RenderLayer* compositingContainer)
......@@ -1825,6 +1829,40 @@ void CompositedLayerMapping::setContentsNeedDisplayInRect(const IntRect& r)
ApplyToGraphicsLayers(this, functor, ApplyToContentLayers);
}
const GraphicsLayerPaintInfo* CompositedLayerMapping::containingSquashedLayer(const RenderObject* renderObject) const
{
for (size_t i = 0; i < m_squashedLayers.size(); ++i) {
if (renderObject->isDescendantOf(m_squashedLayers[i].renderLayer->renderer())) {
return &m_squashedLayers[i];
break;
}
}
return 0;
}
IntRect CompositedLayerMapping::localClipRectForSquashedLayer(const GraphicsLayerPaintInfo& paintInfo) const
{
const RenderObject* clippingContainer = paintInfo.renderLayer->renderer()->clippingContainer();
if (clippingContainer == m_owningLayer.renderer()->clippingContainer())
return PaintInfo::infiniteRect();
ASSERT(clippingContainer);
const GraphicsLayerPaintInfo* ancestorPaintInfo = containingSquashedLayer(clippingContainer);
// Must be there, otherwise CompositingLayerAssigner::canSquashIntoCurrentSquashingOwner would have disallowed squashing.
ASSERT(ancestorPaintInfo);
// FIXME: this is a potential performance issue. We shoudl consider caching these clip rects or otherwise optimizing.
ClipRectsContext clipRectsContext(ancestorPaintInfo->renderLayer, TemporaryClipRects);
IntRect parentClipRect = pixelSnappedIntRect(paintInfo.renderLayer->clipper().backgroundClipRect(clipRectsContext).rect());
ASSERT(parentClipRect != PaintInfo::infiniteRect());
// Convert from ancestor to local coordinates.
IntSize ancestorToLocalOffset = paintInfo.offsetFromRenderer - ancestorPaintInfo->offsetFromRenderer;
parentClipRect.move(ancestorToLocalOffset);
return parentClipRect;
}
void CompositedLayerMapping::doPaintTask(GraphicsLayerPaintInfo& paintInfo, GraphicsContext* context,
const IntRect& clip) // In the coords of rootLayer.
{
......@@ -1893,10 +1931,10 @@ void CompositedLayerMapping::doPaintTask(GraphicsLayerPaintInfo& paintInfo, Grap
LayerPaintingInfo paintingInfo(paintInfo.renderLayer, dirtyRect, PaintBehaviorNormal, paintInfo.renderLayer->subpixelAccumulation());
// RenderLayer::paintLayer assumes that the caller clips to the passed rect. Squashed layers need to do this clipping in software,
// since there is no graphics layer to clip them precisely.
// FIXME: in some cases this clip is not necessary. For example if the dirty rect is not the same as the bounds of the layer,
// RenderLayer will clip it (see RenderLayer::clipToRect). Put in more work if this becomes a performance issue.
// since there is no graphics layer to clip them precisely. Furthermore, in some cases we squash layers that need clipping in software
// from clipping ancestors (see CompositedLayerMapping::localClipRectForSquashedLayer()).
context->save();
dirtyRect.intersect(paintInfo.localClipRectForSquashedLayer);
context->clip(dirtyRect);
paintInfo.renderLayer->paintLayer(context, paintingInfo, paintFlags);
context->restore();
......
......@@ -51,6 +51,9 @@ struct GraphicsLayerPaintInfo {
// known, then we can trivially convert this offset to m_squashingLayer's space.
LayoutSize offsetFromSquashingCLM;
// The clip rect to apply, in the local coordinate space of the squashed layer, when painting it.
IntRect localClipRectForSquashedLayer;
// Offset describing where this squashed RenderLayer paints into the shared GraphicsLayer backing.
IntSize offsetFromRenderer;
LayoutSize subpixelAccumulation;
......@@ -205,6 +208,9 @@ public:
return m_squashingLayerOffsetFromTransformedAncestor;
}
// If there is a squashed layer painting into this CLM that is an ancestor of the given RenderObject, return it. Otherwise return 0.
const GraphicsLayerPaintInfo* containingSquashedLayer(const RenderObject*) const;
private:
void createPrimaryGraphicsLayer();
void destroyGraphicsLayers();
......@@ -275,6 +281,13 @@ private:
void doPaintTask(GraphicsLayerPaintInfo&, GraphicsContext*, const IntRect& clip);
// Computes the background clip rect for the given squashed layer, up to any containing layer that is squashed into the
// same squashing layer and contains this squashed layer's clipping ancestor.
// The clip rect is returned in the coordinate space of the given squashed layer.
// If there is no such containing layer, returns the infinite rect.
// FIXME: unify this code with the code that sets up m_ancestorClippingLayer. They are doing very similar things.
IntRect localClipRectForSquashedLayer(const GraphicsLayerPaintInfo&) const;
RenderLayer& m_owningLayer;
// The hierarchy of layers that is maintained by the CompositedLayerMapping looks like this:
......
......@@ -130,8 +130,10 @@ bool CompositingLayerAssigner::canSquashIntoCurrentSquashingOwner(const RenderLa
ASSERT(squashingState.hasMostRecentMapping);
const RenderLayer& squashingLayer = squashingState.mostRecentMapping->owningLayer();
if (layer->renderer()->clippingContainer() != squashingLayer.renderer()->clippingContainer())
return false;
if (layer->renderer()->clippingContainer() != squashingLayer.renderer()->clippingContainer()) {
if (!squashingLayer.compositedLayerMapping()->containingSquashedLayer(layer->renderer()->clippingContainer()))
return false;
}
// Composited descendants need to be clipped by a child contianment graphics layer, which would not be available if the layer is
if (m_compositor->clipsCompositingDescendants(layer))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment