Avoid creating compositing layers for preserve-3d without transformed descendants
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Jun 2012 22:18:31 +0000 (22:18 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Jun 2012 22:18:31 +0000 (22:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=88115

Source/WebCore:

Reviewed by Antti Koivisto.

Avoid creating compositing layers, and therefore using excess backing store,
for elements that have -webkit-transform-style: preserve-3d, but no 3D-transformed
descendants that would be affected by that preserve-3d.

Test: compositing/layer-creation/no-compositing-for-preserve-3d.html

* rendering/RenderLayer.h: Replace the "mustOverlap" flag with a enum
that describes the different reasons for indirect compositing, so that
we can use that information to decide whether to allocate backing store.
(WebCore::RenderLayer::setIndirectCompositingReason):
(WebCore::RenderLayer::indirectCompositingReason):
(WebCore::RenderLayer::mustCompositeForIndirectReasons):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::RenderLayer): Initialize m_indirectCompositingReason

* rendering/RenderLayerCompositor.h: New out param for computeCompositingRequirements()
that is uses to indicate that a 3d-transformed descendant has been encountered.
Rename requiresCompositingWhenDescendantsAreCompositing() to requiresCompositingForIndirectReason(),
and return the reason as an out param.

* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::updateCompositingLayers): Pass in the saw3DTransform
param to computeCompositingRequirements().
(WebCore::RenderLayerCompositor::computeCompositingRequirements): Return a flag
from computeCompositingRequirements() that is set to true if we've seen descendants
that have 3d transforms. This is later used to decide whether to composite for
perspective or preserve-3d.
Change the "mustOverlapCompositedLayers" code to use the new "indirect compositing"
enum flags.
After enumerating children, call requiresCompositingForIndirectReason() and
record the reason in the layer.
(WebCore::RenderLayerCompositor::needsToBeComposited): Use mustCompositeForIndirectReasons() now.
(WebCore::RenderLayerCompositor::requiresOwnBackingStore): Consult the indirect compositing
reason rather than just looking for the overlap flag.
(WebCore::RenderLayerCompositor::reasonForCompositing): Now that we have more information
about indirect compositing reasons, the logging can be more detailed.
(WebCore::RenderLayerCompositor::requiresCompositingForTransform): This now only looks
for 3d transforms. We now treat perspective and perserve-3d as "indirect" reasons, because
whether they composite depends on descendants having non-affine transforms.
(WebCore::RenderLayerCompositor::requiresCompositingForIndirectReason): Includes the logic
previously in requiresCompositingWhenDescendantsAreCompositing(), and now determines
whether to composite for preserve-3d and perspective, based on whether we have transformed descendants.

LayoutTests:

Reviewed by Antti Koivisto.

* compositing/backing/no-backing-for-perspective-expected.txt:
* compositing/backing/no-backing-for-perspective.html: Change the transform to be non-affine so that
we keep making layers for perspective.
* compositing/layer-creation/no-compositing-for-preserve-3d-expected.txt: Added.
* compositing/layer-creation/no-compositing-for-preserve-3d.html: Copied from LayoutTests/compositing/backing/no-backing-for-perspective.html.

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

LayoutTests/ChangeLog
LayoutTests/compositing/backing/no-backing-for-perspective-expected.txt
LayoutTests/compositing/backing/no-backing-for-perspective.html
LayoutTests/compositing/layer-creation/no-compositing-for-preserve-3d-expected.txt [new file with mode: 0644]
LayoutTests/compositing/layer-creation/no-compositing-for-preserve-3d.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderLayer.h
Source/WebCore/rendering/RenderLayerCompositor.cpp
Source/WebCore/rendering/RenderLayerCompositor.h

index af62ad3..b927088 100644 (file)
@@ -1,3 +1,16 @@
+2012-06-05  Simon Fraser  <simon.fraser@apple.com>
+
+        Avoid creating compositing layers for preserve-3d without transformed descendants
+        https://bugs.webkit.org/show_bug.cgi?id=88115
+
+        Reviewed by Antti Koivisto.
+        
+        * compositing/backing/no-backing-for-perspective-expected.txt:
+        * compositing/backing/no-backing-for-perspective.html: Change the transform to be non-affine so that
+        we keep making layers for perspective.
+        * compositing/layer-creation/no-compositing-for-preserve-3d-expected.txt: Added.
+        * compositing/layer-creation/no-compositing-for-preserve-3d.html: Copied from LayoutTests/compositing/backing/no-backing-for-perspective.html.
+
 2012-06-05  Levi Weintraub  <leviw@chromium.org>
 
         Block selection gaps painted not properly pixel snapped
index 24e7306..db434dc 100644 (file)
@@ -20,6 +20,7 @@ This layer should not have backing store.
                   (position 31.00 49.00)
                   (bounds 100.00 100.00)
                   (drawsContent 1)
+                  (transform [1.00 0.00 0.00 0.00] [0.00 1.00 0.00 0.00] [0.00 0.00 1.00 0.00] [0.00 0.00 1.00 1.00])
                 )
               )
             )
index 31e3689..ef10da6 100644 (file)
@@ -21,7 +21,7 @@
     }
     
     .composited {
-      -webkit-transform: translateZ(0);
+      -webkit-transform: translateZ(1px);
     }
   </style>
 
diff --git a/LayoutTests/compositing/layer-creation/no-compositing-for-preserve-3d-expected.txt b/LayoutTests/compositing/layer-creation/no-compositing-for-preserve-3d-expected.txt
new file mode 100644 (file)
index 0000000..d2f2867
--- /dev/null
@@ -0,0 +1,28 @@
+This layer should not be composited.
+This layer should not be composited.
+This layer should be composited.
+(GraphicsLayer
+  (bounds 785.00 611.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 785.00 611.00)
+      (children 1
+        (GraphicsLayer
+          (position 18.00 390.00)
+          (bounds 342.00 180.00)
+          (preserves3D 1)
+          (drawsContent 1)
+          (children 1
+            (GraphicsLayer
+              (position 31.00 49.00)
+              (bounds 100.00 100.00)
+              (drawsContent 1)
+              (transform [0.98 0.00 -0.17 0.00] [0.00 1.00 0.00 0.00] [0.17 0.00 0.98 0.00] [0.00 0.00 0.00 1.00])
+            )
+          )
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/compositing/layer-creation/no-compositing-for-preserve-3d.html b/LayoutTests/compositing/layer-creation/no-compositing-for-preserve-3d.html
new file mode 100644 (file)
index 0000000..64cf496
--- /dev/null
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <style>
+    .box {
+      position: relative;
+      height: 100px;
+      width: 100px;
+      margin: 10px;
+      left: 0;
+      top: 0;
+      background-color: silver;
+    }
+    
+    .preserve3d {
+      width: 300px;
+      border: 1px solid black;
+      padding: 20px;
+      margin: 10px;
+      -webkit-transform-style: preserve-3d;
+    }
+  </style>
+
+  <script>
+  if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+    
+  function dumpLayers()
+  {
+    var layersResult = document.getElementById('layers');
+    if (window.layoutTestController)
+      layersResult.innerText = layoutTestController.layerTreeAsText();
+    
+  }
+  window.addEventListener('load', dumpLayers, false)
+  </script>
+
+</head>
+<body>
+
+  <div class="preserve3d">
+    This layer should not be composited.
+    <div class="box"></div>
+    </div>
+  </div>
+
+  <div class="preserve3d">
+    This layer should not be composited.
+    <div class="box" style="-webkit-transform: rotate(10deg)"></div>
+    </div>
+  </div>
+
+  <div class="preserve3d">
+    This layer should be composited.
+    <div class="box" style="-webkit-transform: rotateY(10deg)"></div>
+  </div>
+<pre id="layers">Layer tree goes here in DRT</pre>
+
+</body>
+</html>
index 241b089..44d845c 100644 (file)
@@ -1,3 +1,53 @@
+2012-06-05  Simon Fraser  <simon.fraser@apple.com>
+
+        Avoid creating compositing layers for preserve-3d without transformed descendants
+        https://bugs.webkit.org/show_bug.cgi?id=88115
+
+        Reviewed by Antti Koivisto.
+        
+        Avoid creating compositing layers, and therefore using excess backing store,
+        for elements that have -webkit-transform-style: preserve-3d, but no 3D-transformed
+        descendants that would be affected by that preserve-3d.
+        
+        Test: compositing/layer-creation/no-compositing-for-preserve-3d.html
+
+        * rendering/RenderLayer.h: Replace the "mustOverlap" flag with a enum
+        that describes the different reasons for indirect compositing, so that
+        we can use that information to decide whether to allocate backing store.
+        (WebCore::RenderLayer::setIndirectCompositingReason):
+        (WebCore::RenderLayer::indirectCompositingReason):
+        (WebCore::RenderLayer::mustCompositeForIndirectReasons):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::RenderLayer): Initialize m_indirectCompositingReason
+
+        * rendering/RenderLayerCompositor.h: New out param for computeCompositingRequirements()
+        that is uses to indicate that a 3d-transformed descendant has been encountered.
+        Rename requiresCompositingWhenDescendantsAreCompositing() to requiresCompositingForIndirectReason(),
+        and return the reason as an out param.
+        
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::RenderLayerCompositor::updateCompositingLayers): Pass in the saw3DTransform
+        param to computeCompositingRequirements().
+        (WebCore::RenderLayerCompositor::computeCompositingRequirements): Return a flag
+        from computeCompositingRequirements() that is set to true if we've seen descendants
+        that have 3d transforms. This is later used to decide whether to composite for
+        perspective or preserve-3d.
+        Change the "mustOverlapCompositedLayers" code to use the new "indirect compositing"
+        enum flags.
+        After enumerating children, call requiresCompositingForIndirectReason() and 
+        record the reason in the layer.
+        (WebCore::RenderLayerCompositor::needsToBeComposited): Use mustCompositeForIndirectReasons() now.
+        (WebCore::RenderLayerCompositor::requiresOwnBackingStore): Consult the indirect compositing
+        reason rather than just looking for the overlap flag.
+        (WebCore::RenderLayerCompositor::reasonForCompositing): Now that we have more information
+        about indirect compositing reasons, the logging can be more detailed.
+        (WebCore::RenderLayerCompositor::requiresCompositingForTransform): This now only looks
+        for 3d transforms. We now treat perspective and perserve-3d as "indirect" reasons, because
+        whether they composite depends on descendants having non-affine transforms.
+        (WebCore::RenderLayerCompositor::requiresCompositingForIndirectReason): Includes the logic
+        previously in requiresCompositingWhenDescendantsAreCompositing(), and now determines
+        whether to composite for preserve-3d and perspective, based on whether we have transformed descendants.
+
 2012-06-05  Levi Weintraub  <leviw@chromium.org>
 
         Block selection gaps painted not properly pixel snapped
index 05d9ba5..9e785f7 100644 (file)
@@ -142,7 +142,7 @@ RenderLayer::RenderLayer(RenderBoxModelObject* renderer)
     , m_has3DTransformedDescendant(false)
 #if USE(ACCELERATED_COMPOSITING)
     , m_hasCompositingDescendant(false)
-    , m_mustOverlapCompositedLayers(false)
+    , m_indirectCompositingReason(NoIndirectCompositingReason)
 #endif
     , m_containsDirtyOverlayScrollbars(false)
 #if !ASSERT_DISABLED
index 4852ff0..4b07c6c 100644 (file)
@@ -816,8 +816,18 @@ private:
     bool hasCompositingDescendant() const { return m_hasCompositingDescendant; }
     void setHasCompositingDescendant(bool b)  { m_hasCompositingDescendant = b; }
     
-    bool mustOverlapCompositedLayers() const { return m_mustOverlapCompositedLayers; }
-    void setMustOverlapCompositedLayers(bool b) { m_mustOverlapCompositedLayers = b; }
+    enum IndirectCompositingReason {
+        NoIndirectCompositingReason,
+        IndirectCompositingForOverlap,
+        IndirectCompositingForBackgroundLayer,
+        IndirectCompositingForGraphicalEffect, // opacity, mask, filter, transform etc.
+        IndirectCompositingForPerspective,
+        IndirectCompositingForPreserve3D
+    };
+    
+    void setIndirectCompositingReason(IndirectCompositingReason reason) { m_indirectCompositingReason = reason; }
+    IndirectCompositingReason indirectCompositingReason() const { return static_cast<IndirectCompositingReason>(m_indirectCompositingReason); }
+    bool mustCompositeForIndirectReasons() const { return m_indirectCompositingReason; }
 #endif
 
     friend class RenderLayerBacking;
@@ -875,7 +885,7 @@ protected:
                                             // in a preserves3D hierarchy. Hint to do 3D-aware hit testing.
 #if USE(ACCELERATED_COMPOSITING)
     bool m_hasCompositingDescendant : 1; // In the z-order tree.
-    bool m_mustOverlapCompositedLayers : 1;
+    unsigned m_indirectCompositingReason : 3;
 #endif
 
     bool m_containsDirtyOverlayScrollbars : 1;
index c20db84..98d05c4 100644 (file)
@@ -401,11 +401,12 @@ void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType update
         // FIXME: we could maybe do this and the hierarchy udpate in one pass, but the parenting logic would be more complex.
         CompositingState compState(updateRoot, m_compositingConsultsOverlap);
         bool layersChanged = false;
+        bool saw3DTransform = false;
         if (m_compositingConsultsOverlap) {
             OverlapMap overlapTestRequestMap;
-            computeCompositingRequirements(0, updateRoot, &overlapTestRequestMap, compState, layersChanged);
+            computeCompositingRequirements(0, updateRoot, &overlapTestRequestMap, compState, layersChanged, saw3DTransform);
         } else
-            computeCompositingRequirements(0, updateRoot, 0, compState, layersChanged);
+            computeCompositingRequirements(0, updateRoot, 0, compState, layersChanged, saw3DTransform);
         
         needHierarchyUpdate |= layersChanged;
     }
@@ -716,7 +717,7 @@ void RenderLayerCompositor::addToOverlapMapRecursive(OverlapMap& overlapMap, Ren
 //      must be compositing so that its contents render over that child.
 //      This implies that its positive z-index children must also be compositing.
 //
-void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestorLayer, RenderLayer* layer, OverlapMap* overlapMap, CompositingState& compositingState, bool& layersChanged)
+void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestorLayer, RenderLayer* layer, OverlapMap* overlapMap, CompositingState& compositingState, bool& layersChanged, bool& descendantHas3DTransform)
 {
     layer->updateLayerListsIfNeeded();
     
@@ -741,7 +742,7 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
         mustOverlapCompositedLayers = overlapMap->overlapsLayers(absBounds);
     }
     
-    layer->setMustOverlapCompositedLayers(mustOverlapCompositedLayers);
+    layer->setIndirectCompositingReason(mustOverlapCompositedLayers ? RenderLayer::IndirectCompositingForOverlap : RenderLayer::NoIndirectCompositingReason);
     
     // The children of this layer don't need to composite, unless there is
     // a compositing layer among them, so start by inheriting the compositing
@@ -777,18 +778,20 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
     LayerListMutationDetector mutationChecker(layer);
 #endif
 
+    bool anyDescendantHas3DTransform = false;
+
     if (layer->isStackingContext()) {
         if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) {
             size_t listSize = negZOrderList->size();
             for (size_t i = 0; i < listSize; ++i) {
                 RenderLayer* curLayer = negZOrderList->at(i);
-                computeCompositingRequirements(layer, curLayer, overlapMap, childState, layersChanged);
+                computeCompositingRequirements(layer, curLayer, overlapMap, childState, layersChanged, anyDescendantHas3DTransform);
 
                 // If we have to make a layer for this child, make one now so we can have a contents layer
                 // (since we need to ensure that the -ve z-order child renders underneath our contents).
                 if (!willBeComposited && childState.m_subtreeIsCompositing) {
                     // make layer compositing
-                    layer->setMustOverlapCompositedLayers(true);
+                    layer->setIndirectCompositingReason(RenderLayer::IndirectCompositingForBackgroundLayer);
                     childState.m_compositingAncestor = layer;
                     if (overlapMap)
                         overlapMap->pushCompositingContainer();
@@ -802,7 +805,7 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
         size_t listSize = normalFlowList->size();
         for (size_t i = 0; i < listSize; ++i) {
             RenderLayer* curLayer = normalFlowList->at(i);
-            computeCompositingRequirements(layer, curLayer, overlapMap, childState, layersChanged);
+            computeCompositingRequirements(layer, curLayer, overlapMap, childState, layersChanged, anyDescendantHas3DTransform);
         }
     }
 
@@ -811,7 +814,7 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
             size_t listSize = posZOrderList->size();
             for (size_t i = 0; i < listSize; ++i) {
                 RenderLayer* curLayer = posZOrderList->at(i);
-                computeCompositingRequirements(layer, curLayer, overlapMap, childState, layersChanged);
+                computeCompositingRequirements(layer, curLayer, overlapMap, childState, layersChanged, anyDescendantHas3DTransform);
             }
         }
     }
@@ -830,11 +833,11 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
     if (overlapMap && childState.m_compositingAncestor && !childState.m_compositingAncestor->isRootLayer())
         addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds);
 
-    // If we have a software transform, and we have layers under us, we need to also
-    // be composited. Also, if we have opacity < 1, then we need to be a layer so that
-    // the child layers are opaque, then rendered with opacity on this layer.
-    if (!willBeComposited && canBeComposited(layer) && childState.m_subtreeIsCompositing && requiresCompositingWhenDescendantsAreCompositing(layer->renderer())) {
-        layer->setMustOverlapCompositedLayers(true);
+    // Now check for reasons to become composited that depend on the state of descendant layers.
+    RenderLayer::IndirectCompositingReason indirectCompositingReason;
+    if (!willBeComposited && canBeComposited(layer)
+        && requiresCompositingForIndirectReason(layer->renderer(), childState.m_subtreeIsCompositing, anyDescendantHas3DTransform, indirectCompositingReason)) {
+        layer->setIndirectCompositingReason(indirectCompositingReason);
         childState.m_compositingAncestor = layer;
         if (overlapMap) {
             overlapMap->pushCompositingContainer();
@@ -842,11 +845,11 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
         }
         willBeComposited = true;
     }
-
+    
     ASSERT(willBeComposited == needsToBeComposited(layer));
     if (layer->reflectionLayer()) {
         // FIXME: Shouldn't we call computeCompositingRequirements to handle a reflection overlapping with another renderer?
-        layer->reflectionLayer()->setMustOverlapCompositedLayers(willBeComposited);
+        layer->reflectionLayer()->setIndirectCompositingReason(willBeComposited ? RenderLayer::IndirectCompositingForOverlap : RenderLayer::NoIndirectCompositingReason);
     }
 
     // Subsequent layers in the parent stacking context also need to composite.
@@ -899,6 +902,8 @@ void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* ancestor
     if (layer->reflectionLayer() && updateLayerCompositingState(layer->reflectionLayer(), CompositingChangeRepaintNow))
         layersChanged = true;
 
+    descendantHas3DTransform |= anyDescendantHas3DTransform || layer->has3DTransform();
+
     if (overlapMap)
         overlapMap->geometryMap().popMappingsToAncestor(ancestorLayer ? ancestorLayer->renderer() : 0);
 }
@@ -1417,7 +1422,7 @@ bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const
     if (!canBeComposited(layer))
         return false;
 
-    return requiresCompositingLayer(layer) || layer->mustOverlapCompositedLayers() || (inCompositingMode() && layer->isRootLayer());
+    return requiresCompositingLayer(layer) || layer->mustCompositeForIndirectReasons() || (inCompositingMode() && layer->isRootLayer());
 }
 
 // Note: this specifies whether the RL needs a compositing layer for intrinsic reasons.
@@ -1460,7 +1465,7 @@ bool RenderLayerCompositor::requiresOwnBackingStore(const RenderLayer* layer, co
             || compositingAncestorLayer->backing()->paintsIntoCompositedAncestor()))
         return true;
 
-    return layer->isRootLayer()
+    if (layer->isRootLayer()
         || layer->transform() // note: excludes perspective and transformStyle3D.
         || requiresCompositingForVideo(renderer)
         || requiresCompositingForCanvas(renderer)
@@ -1473,8 +1478,18 @@ bool RenderLayerCompositor::requiresOwnBackingStore(const RenderLayer* layer, co
         || renderer->isTransparent()
         || renderer->hasMask()
         || renderer->hasReflection()
-        || renderer->hasFilter()
-        || layer->mustOverlapCompositedLayers();
+        || renderer->hasFilter())
+        return true;
+        
+    
+    if (layer->mustCompositeForIndirectReasons()) {
+        RenderLayer::IndirectCompositingReason reason = layer->indirectCompositingReason();
+        return reason == RenderLayer::IndirectCompositingForOverlap
+            || reason == RenderLayer::IndirectCompositingForBackgroundLayer
+            || reason == RenderLayer::IndirectCompositingForGraphicalEffect
+            || reason == RenderLayer::IndirectCompositingForPreserve3D; // preserve-3d has to create backing store to ensure that 3d-transformed elements intersect.
+    }
+    return false;
 }
 
 #if !LOG_DISABLED
@@ -1486,14 +1501,8 @@ const char* RenderLayerCompositor::reasonForCompositing(const RenderLayer* layer
         layer = toRenderBoxModelObject(renderer)->layer();
     }
 
-    if (renderer->hasTransform() && renderer->style()->hasPerspective())
-        return "perspective";
-
-    if (renderer->hasTransform() && (renderer->style()->transformStyle3D() == TransformStyle3DPreserve3D))
-        return "preserve-3d";
-
-    if (renderer->hasTransform())
-        return "transform";
+    if (requiresCompositingForTransform(renderer))
+        return "3D transform";
 
     if (requiresCompositingForVideo(renderer))
         return "video";
@@ -1523,9 +1532,35 @@ const char* RenderLayerCompositor::reasonForCompositing(const RenderLayer* layer
         return "position: fixed";
 
     // This includes layers made composited by requiresCompositingWhenDescendantsAreCompositing().
-    if (layer->mustOverlapCompositedLayers())
+    if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForOverlap)
         return "overlap/stacking";
 
+    if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForBackgroundLayer)
+        return "negative z-index children";
+
+    if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForGraphicalEffect) {
+        if (layer->transform())
+            return "transform with composited descendants";
+
+        if (renderer->isTransparent())
+            return "opacity with composited descendants";
+
+        if (renderer->hasMask())
+            return "mask with composited descendants";
+
+        if (renderer->hasReflection())
+            return "reflection with composited descendants";
+
+        if (renderer->hasFilter())
+            return "filter with composited descendants";
+    }
+
+    if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForPerspective)
+        return "perspective";
+
+    if (layer->indirectCompositingReason() == RenderLayer::IndirectCompositingForPreserve3D)
+        return "preserve-3d";
+
     if (inCompositingMode() && layer->isRootLayer())
         return "root";
 
@@ -1593,7 +1628,7 @@ bool RenderLayerCompositor::requiresCompositingForTransform(RenderObject* render
     RenderStyle* style = renderer->style();
     // Note that we ask the renderer if it has a transform, because the style may have transforms,
     // but the renderer may be an inline that doesn't suppport them.
-    return renderer->hasTransform() && (style->transform().has3DOperation() || style->transformStyle3D() == TransformStyle3DPreserve3D || style->hasPerspective());
+    return renderer->hasTransform() && style->transform().has3DOperation();
 }
 
 bool RenderLayerCompositor::requiresCompositingForVideo(RenderObject* renderer) const
@@ -1701,11 +1736,35 @@ bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* render
     return false;
 }
 
-bool RenderLayerCompositor::requiresCompositingWhenDescendantsAreCompositing(RenderObject* renderer) const
+bool RenderLayerCompositor::requiresCompositingForIndirectReason(RenderObject* renderer, bool hasCompositedDescendants, bool has3DTransformedDescendants, RenderLayer::IndirectCompositingReason& reason) const
 {
-    return renderer->hasTransform() || renderer->isTransparent() || renderer->hasMask() || renderer->hasReflection() || renderer->hasFilter();
-}
+    RenderLayer* layer = toRenderBoxModelObject(renderer)->layer();
+
+    // When a layer has composited descendants, some effects, like 2d transforms, filters, masks etc must be implemented
+    // via compositing so that they also apply to those composited descdendants.
+    if (hasCompositedDescendants && (layer->transform() || renderer->isTransparent() || renderer->hasMask() || renderer->hasReflection() || renderer->hasFilter())) {
+        reason = RenderLayer::IndirectCompositingForGraphicalEffect;
+        return true;
+    }
+
+    // A layer with preserve-3d or perspective only needs to be composited if there are descendant layers that
+    // will be affected by the preserve-3d or perspective.
+    if (has3DTransformedDescendants) {
+        if (renderer->style()->transformStyle3D() == TransformStyle3DPreserve3D) {
+            reason = RenderLayer::IndirectCompositingForPreserve3D;
+            return true;
+        }
     
+        if (renderer->style()->hasPerspective()) {
+            reason = RenderLayer::IndirectCompositingForPerspective;
+            return true;
+        }
+    }
+
+    reason = RenderLayer::NoIndirectCompositingReason;
+    return false;
+}
+
 bool RenderLayerCompositor::requiresCompositingForFilters(RenderObject* renderer) const
 {
 #if ENABLE(CSS_FILTERS)
index 7aa725e..792fb01 100644 (file)
@@ -246,7 +246,7 @@ private:
     void updateCompositingLayersTimerFired(Timer<RenderLayerCompositor>*);
 
     // Returns true if any layer's compositing changed
-    void computeCompositingRequirements(RenderLayer* ancestorLayer, RenderLayer*, OverlapMap*, struct CompositingState&, bool& layersChanged);
+    void computeCompositingRequirements(RenderLayer* ancestorLayer, RenderLayer*, OverlapMap*, struct CompositingState&, bool& layersChanged, bool& descendantHas3DTransform);
     
     // Recurses down the tree, parenting descendant compositing layers and collecting an array of child layers for the current compositing layer.
     void rebuildCompositingLayerTree(RenderLayer*, Vector<GraphicsLayer*>& childGraphicsLayersOfEnclosingLayer, int depth);
@@ -286,10 +286,10 @@ private:
     bool requiresCompositingForCanvas(RenderObject*) const;
     bool requiresCompositingForPlugin(RenderObject*) const;
     bool requiresCompositingForFrame(RenderObject*) const;
-    bool requiresCompositingWhenDescendantsAreCompositing(RenderObject*) const;
     bool requiresCompositingForFilters(RenderObject*) const;
     bool requiresCompositingForScrollableFrame() const;
     bool requiresCompositingForPosition(RenderObject*, const RenderLayer*) const;
+    bool requiresCompositingForIndirectReason(RenderObject*, bool hasCompositedDescendants, bool has3DTransformedDescendants, RenderLayer::IndirectCompositingReason&) const;
 
     bool requiresScrollLayer(RootLayerAttachment) const;
     bool requiresHorizontalScrollbarLayer() const;