Improve performance of the RenderLayerCompositor::OverlapMap
authorachicu@adobe.com <achicu@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 17 Apr 2014 01:00:26 +0000 (01:00 +0000)
committerachicu@adobe.com <achicu@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 17 Apr 2014 01:00:26 +0000 (01:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=115063

Reviewed by Simon Fraser.

PerformanceTests:
Testing the performance of computing the overlap of 5000 layers.

* Layout/layers_overlap_2d.html: Added. Using non-composited layers, to check
that the performance on the non-composited path is not changing with this patch.
* Layout/layers_overlap_3d.html: Added. Records the time to do the layout of 5000
non-overlapping 3D layers.

Source/WebCore:
No new tests, no new functionality or behavior.

Do not use the OverlapMap in RenderLayerCompositor::computeCompositingRequirements if the layer already
has a 3D transform. This way we can avoid a potential expensive lookups when we know for sure the layer
is already supposed to be composited.

Also, added a bounding box of the overlap map, so that it can catch cases when the new layer is not overlapping
any of the previous layers. This is pretty common when having composited layers laid out in a vertical/horizontal list.

* rendering/RenderLayerCompositor.cpp:
(OverlapMapContainer):
(WebCore::OverlapMapContainer::add):
(WebCore::OverlapMapContainer::overlapsLayers):
(WebCore::OverlapMapContainer::unite):
(WebCore):
(WebCore::RenderLayerCompositor::OverlapMap::add):
(WebCore::RenderLayerCompositor::OverlapMap::overlapsLayers):
(WebCore::RenderLayerCompositor::OverlapMap::pushCompositingContainer):
(WebCore::RenderLayerCompositor::OverlapMap::popCompositingContainer):
(RenderLayerCompositor::OverlapMap):
(WebCore::RenderLayerCompositor::computeCompositingRequirements):

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

PerformanceTests/ChangeLog
PerformanceTests/Layout/layers_overlap_2d.html [new file with mode: 0644]
PerformanceTests/Layout/layers_overlap_3d.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderLayerCompositor.cpp

index 4968c59..a2fcd09 100644 (file)
@@ -1,3 +1,17 @@
+2014-04-16  Alexandru Chiculita  <achicu@adobe.com>
+
+        Improve performance of the RenderLayerCompositor::OverlapMap
+        https://bugs.webkit.org/show_bug.cgi?id=115063
+
+        Reviewed by Simon Fraser.
+
+        Testing the performance of computing the overlap of 5000 layers.
+
+        * Layout/layers_overlap_2d.html: Added. Using non-composited layers, to check
+        that the performance on the non-composited path is not changing with this patch.
+        * Layout/layers_overlap_3d.html: Added. Records the time to do the layout of 5000
+        non-overlapping 3D layers.
+
 2014-04-15  Zoltan Horvath  <zoltan@webkit.org>
 
         [CSS Shapes] Linking stylesheet instead of inline style definition has ruined ShapesRegions test
diff --git a/PerformanceTests/Layout/layers_overlap_2d.html b/PerformanceTests/Layout/layers_overlap_2d.html
new file mode 100644 (file)
index 0000000..a7d90e2
--- /dev/null
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Performance tester for non-overlaping 2D layers</title>
+        <style>
+            .container {
+                position: relative;
+                width: 20px;
+                height: 20px;
+                border: 1px solid #AAA;
+                margin: 0 auto 5px;
+            }
+
+            .box {
+                width: 100%;
+                height: 100%;
+                position: absolute;
+                background: red;
+            }
+
+            .composited {
+                -webkit-transform: translateZ(1px);
+            }
+        </style>
+        <script src="../resources/runner.js"></script>
+    </head>
+    <body>
+        <pre id="log"></pre>
+        <script>
+            function createTestFunction(count) {
+                return function() {
+                    var container = document.createElement("div");
+                    for(i = 0; i < count; ++i) {
+                        var outer = document.createElement('div');
+                        outer.className = 'container';
+                        var inner = document.createElement('div');
+                        inner.className = 'box';
+                        if (i == 0) {
+                            // Use at least one 3D layer to trigger the overlap map checking.
+                            inner.className += " composited";
+                        }
+                        outer.appendChild(inner);
+                        container.appendChild(outer);
+                    }
+                    document.body.appendChild(container);
+                    // Force a layout update.
+                    document.body.clientHeight;
+                    container.remove();
+                }
+            }
+            PerfTestRunner.measureTime({run: createTestFunction(5000)});
+        </script>
+    </body>
+</html>
diff --git a/PerformanceTests/Layout/layers_overlap_3d.html b/PerformanceTests/Layout/layers_overlap_3d.html
new file mode 100644 (file)
index 0000000..5732c3f
--- /dev/null
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Performance tester for non-overlaping 3D layers</title>
+        <style>
+            .container {
+                width: 20px;
+                height: 20px;
+                border: 1px solid #AAA;
+                margin: 0 auto 5px;
+                -webkit-perspective: 400px;
+            }
+
+            .box {
+                width: 100%;
+                height: 100%;
+                position: absolute;
+                background: red;
+                -webkit-transform: translateZ( -200px );
+            }
+        </style>
+        <script src="../resources/runner.js"></script>
+    </head>
+    <body>
+        <pre id="log"></pre>
+        <script>
+            function createTestFunction(count) {
+                return function() {
+                    var container = document.createElement("div");
+                    for(i = 0; i < count; ++i) {
+                        var outer = document.createElement('div');
+                        outer.className = 'container';
+                        var inner = document.createElement('div');
+                        inner.className = 'box';
+                        outer.appendChild(inner);
+                        container.appendChild(outer);
+                    }
+                    document.body.appendChild(container);
+                    // Force a layout update.
+                    document.body.clientHeight;
+                    container.remove();
+                }
+            }
+            PerfTestRunner.measureTime({run: createTestFunction(5000)});
+        </script>
+    </body>
+</html>
index 1ab0768..9f39b96 100644 (file)
@@ -1,3 +1,33 @@
+2014-04-16  Alexandru Chiculita  <achicu@adobe.com>
+
+        Improve performance of the RenderLayerCompositor::OverlapMap
+        https://bugs.webkit.org/show_bug.cgi?id=115063
+
+        Reviewed by Simon Fraser.
+
+        No new tests, no new functionality or behavior.
+
+        Do not use the OverlapMap in RenderLayerCompositor::computeCompositingRequirements if the layer already
+        has a 3D transform. This way we can avoid a potential expensive lookups when we know for sure the layer
+        is already supposed to be composited. 
+
+        Also, added a bounding box of the overlap map, so that it can catch cases when the new layer is not overlapping
+        any of the previous layers. This is pretty common when having composited layers laid out in a vertical/horizontal list.
+
+
+        * rendering/RenderLayerCompositor.cpp:
+        (OverlapMapContainer):
+        (WebCore::OverlapMapContainer::add):
+        (WebCore::OverlapMapContainer::overlapsLayers):
+        (WebCore::OverlapMapContainer::unite):
+        (WebCore):
+        (WebCore::RenderLayerCompositor::OverlapMap::add):
+        (WebCore::RenderLayerCompositor::OverlapMap::overlapsLayers):
+        (WebCore::RenderLayerCompositor::OverlapMap::pushCompositingContainer):
+        (WebCore::RenderLayerCompositor::OverlapMap::popCompositingContainer):
+        (RenderLayerCompositor::OverlapMap):
+        (WebCore::RenderLayerCompositor::computeCompositingRequirements):
+
 2014-04-16  Brian J. Burg  <burg@cs.washington.edu>
 
         Web Replay: memoize fallback time values for document.lastModified
index aa8358c..9e5247c 100644 (file)
@@ -105,6 +105,38 @@ static const double throttledLayerFlushDelay = .5;
 
 using namespace HTMLNames;
 
+class OverlapMapContainer {
+public:
+    void add(const IntRect& bounds)
+    {
+        m_layerRects.append(bounds);
+        m_boundingBox.unite(bounds);
+    }
+
+    bool overlapsLayers(const IntRect& bounds) const
+    {
+        // Checking with the bounding box will quickly reject cases when
+        // layers are created for lists of items going in one direction and
+        // never overlap with each other.
+        if (!bounds.intersects(m_boundingBox))
+            return false;
+        for (unsigned i = 0; i < m_layerRects.size(); i++) {
+            if (m_layerRects[i].intersects(bounds))
+                return true;
+        }
+        return false;
+    }
+
+    void unite(const OverlapMapContainer& otherContainer)
+    {
+        m_layerRects.appendVector(otherContainer.m_layerRects);
+        m_boundingBox.unite(otherContainer.m_boundingBox);
+    }
+private:
+    Vector<IntRect> m_layerRects;
+    IntRect m_boundingBox;
+};
+
 class RenderLayerCompositor::OverlapMap {
     WTF_MAKE_NONCOPYABLE(OverlapMap);
 public:
@@ -123,7 +155,7 @@ public:
         // contribute to overlap as soon as their composited ancestor has been
         // recursively processed and popped off the stack.
         ASSERT(m_overlapStack.size() >= 2);
-        m_overlapStack[m_overlapStack.size() - 2].append(bounds);
+        m_overlapStack[m_overlapStack.size() - 2].add(bounds);
         m_layers.add(layer);
     }
 
@@ -134,7 +166,7 @@ public:
 
     bool overlapsLayers(const IntRect& bounds) const
     {
-        return m_overlapStack.last().intersects(bounds);
+        return m_overlapStack.last().overlapsLayers(bounds);
     }
 
     bool isEmpty()
@@ -144,12 +176,12 @@ public:
 
     void pushCompositingContainer()
     {
-        m_overlapStack.append(RectList());
+        m_overlapStack.append(OverlapMapContainer());
     }
 
     void popCompositingContainer()
     {
-        m_overlapStack[m_overlapStack.size() - 2].append(m_overlapStack.last());
+        m_overlapStack[m_overlapStack.size() - 2].unite(m_overlapStack.last());
         m_overlapStack.removeLast();
     }
 
@@ -185,7 +217,7 @@ private:
         }
     };
 
-    Vector<RectList> m_overlapStack;
+    Vector<OverlapMapContainer> m_overlapStack;
     HashSet<const RenderLayer*> m_layers;
     RenderGeometryMap m_geometryMap;
 };
@@ -1090,12 +1122,19 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
     
     // Clear the flag
     layer.setHasCompositingDescendant(false);
+    layer.setIndirectCompositingReason(RenderLayer::NoIndirectCompositingReason);
 
-    RenderLayer::IndirectCompositingReason compositingReason = compositingState.m_subtreeIsCompositing ? RenderLayer::IndirectCompositingForStacking : RenderLayer::NoIndirectCompositingReason;
+    // Check if the layer needs to be composited for non-indirect reasons (ex. 3D transform).
+    // We use this value to avoid checking the overlap-map, if we know for sure the layer
+    // is already going to be composited for other reasons.
+    bool willBeComposited = needsToBeComposited(layer);
 
+    RenderLayer::IndirectCompositingReason compositingReason = compositingState.m_subtreeIsCompositing ? RenderLayer::IndirectCompositingForStacking : RenderLayer::NoIndirectCompositingReason;
     bool haveComputedBounds = false;
     IntRect absBounds;
-    if (overlapMap && !overlapMap->isEmpty() && compositingState.m_testingOverlap) {
+
+    // If we know for sure the layer is going to be composited, don't bother looking it up in the overlap map
+    if (!willBeComposited && overlapMap && !overlapMap->isEmpty() && compositingState.m_testingOverlap) {
         // If we're testing for overlap, we only need to composite if we overlap something that is already composited.
         absBounds = enclosingIntRect(overlapMap->geometryMap().absoluteRect(layer.overlapBounds()));
 
@@ -1117,6 +1156,11 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
 
     layer.setIndirectCompositingReason(compositingReason);
 
+    // Check if the computed indirect reason will force the layer to become composited.
+    if (!willBeComposited && layer.mustCompositeForIndirectReasons() && canBeComposited(layer))
+        willBeComposited = true;
+    ASSERT(willBeComposited == needsToBeComposited(layer));
+
     // The children of this layer don't need to composite, unless there is
     // a compositing layer among them, so start by inheriting the compositing
     // ancestor with m_subtreeIsCompositing set to false.
@@ -1126,7 +1170,6 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
     childState.m_hasUnisolatedCompositedBlendingDescendants = false;
 #endif
 
-    bool willBeComposited = needsToBeComposited(layer);
     if (willBeComposited) {
         // Tell the parent it has compositing descendants.
         compositingState.m_subtreeIsCompositing = true;