Avoid backing store for opacity:0 descendant layers
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Nov 2014 02:21:51 +0000 (02:21 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Nov 2014 02:21:51 +0000 (02:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=138448

Reviewed by Dean Jackson.

Source/WebCore:

If a composited layer has no rendered content but a painting zero-opacity descendant
layer, than we can avoid making backing store.

When the opacity on such a child changes, we need to trigger a tree rebuild
to force the backing store to come back (this could be optimized later).

Tests: compositing/backing/no-backing-for-opacity-0-child.html
       compositing/backing/toggle-opacity-0-child.html

* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::calculateClipRects):
* rendering/RenderLayerCompositor.cpp:
(WebCore::styleChangeRequiresLayerRebuild):
* rendering/RenderObject.cpp:
(WebCore::RenderObject::containerForRepaint):

LayoutTests:

Test for no backing store with opacity:0 child, and that we get backing
store when dynamically changing the opacity to non-0.

* compositing/backing/no-backing-for-opacity-0-child-expected.txt: Added.
* compositing/backing/no-backing-for-opacity-0-child.html: Added.
* compositing/backing/toggle-opacity-0-child-expected.txt: Added.
* compositing/backing/toggle-opacity-0-child.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@175656 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/compositing/backing/no-backing-for-opacity-0-child-expected.txt [new file with mode: 0644]
LayoutTests/compositing/backing/no-backing-for-opacity-0-child.html [new file with mode: 0644]
LayoutTests/compositing/backing/toggle-opacity-0-child-expected.txt [new file with mode: 0644]
LayoutTests/compositing/backing/toggle-opacity-0-child.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderLayerCompositor.cpp
Source/WebCore/rendering/RenderObject.cpp

index a761b11..9ceaf66 100644 (file)
@@ -1,3 +1,18 @@
+2014-11-05  Simon Fraser  <simon.fraser@apple.com>
+
+        Avoid backing store for opacity:0 descendant layers
+        https://bugs.webkit.org/show_bug.cgi?id=138448
+
+        Reviewed by Dean Jackson.
+        
+        Test for no backing store with opacity:0 child, and that we get backing
+        store when dynamically changing the opacity to non-0.
+
+        * compositing/backing/no-backing-for-opacity-0-child-expected.txt: Added.
+        * compositing/backing/no-backing-for-opacity-0-child.html: Added.
+        * compositing/backing/toggle-opacity-0-child-expected.txt: Added.
+        * compositing/backing/toggle-opacity-0-child.html: Added.
+
 2014-11-05  Chris Fleizach  <cfleizach@apple.com>
 
         AX: add "alt" as an overriding synonym of "-webkit-alt" (now in the CSS4 spec)
diff --git a/LayoutTests/compositing/backing/no-backing-for-opacity-0-child-expected.txt b/LayoutTests/compositing/backing/no-backing-for-opacity-0-child-expected.txt
new file mode 100644 (file)
index 0000000..6b3b045
--- /dev/null
@@ -0,0 +1,18 @@
+(GraphicsLayer
+  (anchor 0.00 0.00)
+  (bounds 800.00 600.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 800.00 600.00)
+      (contentsOpaque 1)
+      (children 1
+        (GraphicsLayer
+          (position 18.00 10.00)
+          (anchor 0.40 0.40)
+          (bounds 125.00 125.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/compositing/backing/no-backing-for-opacity-0-child.html b/LayoutTests/compositing/backing/no-backing-for-opacity-0-child.html
new file mode 100644 (file)
index 0000000..b484732
--- /dev/null
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style>
+    .container {
+      position: relative;
+      height: 100px;
+      width: 100px;
+      margin: 10px;
+      left: 0;
+      top: 0;
+    }
+    
+    .clipping {
+      position: absolute;
+      overflow: hidden;
+      left: 20px;
+      top: 20px;
+      height: 100px;
+      width: 300px;
+      z-index: 0;
+      background-color: green;
+    }
+    
+    .box {
+        position: absolute;
+        top: 75px;
+        left: 75px;
+        width: 50px;
+        height: 50px;
+        background-color: black;
+    }
+    
+    .child {
+        opacity: 0;
+    }
+    
+    .composited {
+      -webkit-transform: translateZ(0);
+    }
+  </style>
+
+  <script>
+  if (window.testRunner)
+    testRunner.dumpAsText();
+    
+  function dumpLayers()
+  {
+    var layersResult = document.getElementById('layers');
+    if (window.testRunner)
+      layersResult.innerText = window.internals.layerTreeAsText(document);
+  }
+
+  window.addEventListener('load', dumpLayers, false)
+  </script>
+
+</head>
+<body>
+
+  <div class="composited container">
+      <div class="child box">
+      </div>
+    </div>
+  </div>
+
+<pre id="layers">Layer tree goes here in DRT</pre>
+
+</body>
+</html>
diff --git a/LayoutTests/compositing/backing/toggle-opacity-0-child-expected.txt b/LayoutTests/compositing/backing/toggle-opacity-0-child-expected.txt
new file mode 100644 (file)
index 0000000..6b49beb
--- /dev/null
@@ -0,0 +1,19 @@
+(GraphicsLayer
+  (anchor 0.00 0.00)
+  (bounds 800.00 600.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 800.00 600.00)
+      (contentsOpaque 1)
+      (children 1
+        (GraphicsLayer
+          (position 18.00 10.00)
+          (anchor 0.40 0.40)
+          (bounds 125.00 125.00)
+          (drawsContent 1)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/compositing/backing/toggle-opacity-0-child.html b/LayoutTests/compositing/backing/toggle-opacity-0-child.html
new file mode 100644 (file)
index 0000000..e582b76
--- /dev/null
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style>
+    .container {
+      position: relative;
+      height: 100px;
+      width: 100px;
+      margin: 10px;
+      left: 0;
+      top: 0;
+    }
+    
+    .clipping {
+      position: absolute;
+      overflow: hidden;
+      left: 20px;
+      top: 20px;
+      height: 100px;
+      width: 300px;
+      z-index: 0;
+      background-color: green;
+    }
+    
+    .box {
+        position: absolute;
+        top: 75px;
+        left: 75px;
+        width: 50px;
+        height: 50px;
+        background-color: black;
+    }
+    
+    .child {
+        opacity: 0;
+    }
+    
+    .child.changed {
+        opacity: 0.5;
+    }
+    
+    .composited {
+      -webkit-transform: translateZ(0);
+    }
+  </style>
+
+  <script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    function doTest()
+    {
+        document.getElementById('child').classList.add('changed');
+        dumpLayers();
+        if (window.testRunner) 
+            testRunner.notifyDone();
+    }
+    
+    function dumpLayers()
+    {
+        var layersResult = document.getElementById('layers');
+        if (window.testRunner)
+            layersResult.innerText = window.internals.layerTreeAsText(document);
+    }
+
+    window.addEventListener('load', doTest, false)
+  </script>
+
+</head>
+<body>
+
+  <div class="composited container">
+      <div id="child" class="child box">
+      </div>
+    </div>
+  </div>
+
+<pre id="layers">Layer tree goes here in DRT</pre>
+
+</body>
+</html>
index c0baea2..ecfcdf3 100644 (file)
@@ -1,3 +1,26 @@
+2014-11-05  Simon Fraser  <simon.fraser@apple.com>
+
+        Avoid backing store for opacity:0 descendant layers
+        https://bugs.webkit.org/show_bug.cgi?id=138448
+
+        Reviewed by Dean Jackson.
+        
+        If a composited layer has no rendered content but a painting zero-opacity descendant
+        layer, than we can avoid making backing store.
+        
+        When the opacity on such a child changes, we need to trigger a tree rebuild
+        to force the backing store to come back (this could be optimized later).
+
+        Tests: compositing/backing/no-backing-for-opacity-0-child.html
+               compositing/backing/toggle-opacity-0-child.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::calculateClipRects):
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::styleChangeRequiresLayerRebuild):
+        * rendering/RenderObject.cpp:
+        (WebCore::RenderObject::containerForRepaint):
+
 2014-11-05  Chris Dumez  <cdumez@apple.com>
 
         Assertion hit DOMTimer::updateTimerIntervalIfNecessary()
index a569392..8bc3afd 100644 (file)
@@ -6328,7 +6328,7 @@ bool RenderLayer::isVisuallyNonEmpty() const
 {
     ASSERT(!m_visibleDescendantStatusDirty);
 
-    if (!hasVisibleContent())
+    if (!hasVisibleContent() || !renderer().style().opacity())
         return false;
 
     if (renderer().isRenderReplaced() || hasOverflowControls())
index b6015e1..bda7339 100644 (file)
@@ -881,6 +881,15 @@ static bool styleChangeRequiresLayerRebuild(const RenderLayer& layer, const Rend
     if (oldStyle.clip() != newStyle.clip() || oldStyle.hasClip() != newStyle.hasClip())
         return true;
 
+    // FIXME: need to check everything that we consult to avoid backing store here: webkit.org/b/138383
+    if (!oldStyle.opacity() != !newStyle.opacity()) {
+        RenderLayerModelObject* repaintContainer = layer.renderer().containerForRepaint();
+        if (RenderLayerBacking* ancestorBacking = repaintContainer->layer()->backing()) {
+            if (newStyle.opacity() != ancestorBacking->graphicsLayer()->drawsContent())
+                return true;
+        }
+    }
+
     // When overflow changes, composited layers may need to update their ancestorClipping layers.
     if (!layer.isComposited() && (oldStyle.overflowX() != newStyle.overflowX() || oldStyle.overflowY() != newStyle.overflowY()) && layer.stackingContainer()->hasCompositingDescendant())
         return true;
index 30c53de..4820133 100644 (file)
@@ -1195,7 +1195,7 @@ LayoutRect RenderObject::paintingRootRect(LayoutRect& topLevelRect)
 
 RenderLayerModelObject* RenderObject::containerForRepaint() const
 {
-    RenderLayerModelObject* repaintContainer = 0;
+    RenderLayerModelObject* repaintContainer = nullptr;
 
     if (view().usesCompositing()) {
         if (RenderLayer* parentLayer = enclosingLayer()) {