2009-02-11 Simon Fraser <simon.fraser@apple.com>
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Feb 2009 20:48:19 +0000 (20:48 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Feb 2009 20:48:19 +0000 (20:48 +0000)
        Reviewed by Dave Hyatt

        https://bugs.webkit.org/show_bug.cgi?id=23548

        When opacity or transform change on an object which has a compositing layer,
        avoid repainting the layer.

        Added a new StyleDifference value, StyleDifferenceRecompositeLayer, which indicates
        that the only thing styleChanged() has to do is to update composited properties of
        the layer. RenderStyle::diff() now has an out param for a bitmask of "context sensitive"
        properties, currently for opacity and transform. When one of these changes, we need
        to see if we have a compositing layer before we decide whether to layout/repaint,
        or just update the composited layer, via adjustStyleDifference().

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

WebCore/ChangeLog
WebCore/rendering/RenderObject.cpp
WebCore/rendering/RenderObject.h
WebCore/rendering/style/RenderStyle.cpp
WebCore/rendering/style/RenderStyle.h
WebCore/rendering/style/RenderStyleConstants.h

index 75c1d92..e736c8e 100644 (file)
@@ -1,3 +1,30 @@
+2009-02-11  Simon Fraser  <simon.fraser@apple.com>
+
+        Reviewed by Dave Hyatt
+
+        https://bugs.webkit.org/show_bug.cgi?id=23548
+
+        When opacity or transform change on an object which has a compositing layer,
+        avoid repainting the layer.
+        
+        Added a new StyleDifference value, StyleDifferenceRecompositeLayer, which indicates
+        that the only thing styleChanged() has to do is to update composited properties of
+        the layer. RenderStyle::diff() now has an out param for a bitmask of "context sensitive"
+        properties, currently for opacity and transform. When one of these changes, we need
+        to see if we have a compositing layer before we decide whether to layout/repaint,
+        or just update the composited layer, via adjustStyleDifference().
+        
+        * rendering/RenderObject.cpp:
+        (WebCore::RenderObject::adjustStyleDifference):
+        (WebCore::RenderObject::setStyle):
+        (WebCore::RenderObject::styleDidChange):
+        * rendering/RenderObject.h:
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::diff):
+        * rendering/style/RenderStyle.h:
+        * rendering/style/RenderStyleConstants.h:
+        (WebCore::):
+
 2009-02-11  Alexey Proskuryakov  <ap@webkit.org>
 
         Reviewed by Darin Adler.
index c258970..16a112f 100644 (file)
 #include <stdio.h>
 #include <wtf/RefCountedLeakCounter.h>
 
+#if USE(ACCELERATED_COMPOSITING)
+#include "RenderLayerCompositor.h"
+#endif
+
 #if ENABLE(WML)
 #include "WMLNames.h"
 #endif
@@ -1965,18 +1969,48 @@ void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style)
         setStyle(style);
 }
 
+StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff, unsigned contextSensitiveProperties) const
+{
+#if USE(ACCELERATED_COMPOSITING)
+    // If transform changed, and we are not composited, need to do a layout.
+    if (contextSensitiveProperties & ContextSensitivePropertyTransform)
+        // Text nodes share style with their parents but transforms don't apply to them,
+        // hence the !isText() check.
+        // FIXME: when transforms are taken into account for overflow, we will need to do a layout.
+        if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited()))
+            diff = StyleDifferenceLayout;
+        else if (diff < StyleDifferenceRecompositeLayer)
+            diff = StyleDifferenceRecompositeLayer;
+
+    // If opacity changed, and we are not composited, need to repaint (also
+    // ignoring text nodes)
+    if (contextSensitiveProperties & ContextSensitivePropertyOpacity)
+        if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited()))
+            diff = StyleDifferenceRepaintLayer;
+        else if (diff < StyleDifferenceRecompositeLayer)
+            diff = StyleDifferenceRecompositeLayer;
+#else
+    UNUSED_PARAM(contextSensitiveProperties);
+#endif
+
+    // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint.
+    if (diff == StyleDifferenceRepaintLayer && !hasLayer())
+        diff = StyleDifferenceRepaint;
+
+    return diff;
+}
+
 void RenderObject::setStyle(PassRefPtr<RenderStyle> style)
 {
     if (m_style == style)
         return;
 
     StyleDifference diff = StyleDifferenceEqual;
+    unsigned contextSensitiveProperties = ContextSensitivePropertyNone;
     if (m_style)
-        diff = m_style->diff(style.get());
+        diff = m_style->diff(style.get(), contextSensitiveProperties);
 
-    // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint.
-    if (diff == StyleDifferenceRepaintLayer && !hasLayer())
-        diff = StyleDifferenceRepaint;
+    diff = adjustStyleDifference(diff, contextSensitiveProperties);
 
     styleWillChange(diff, style.get());
     
@@ -1995,6 +2029,26 @@ void RenderObject::setStyle(PassRefPtr<RenderStyle> style)
         toRenderView(document()->renderer())->setMaximalOutlineSize(m_style->outlineSize());
 
     styleDidChange(diff, oldStyle.get());
+
+    if (!m_parent || isText())
+        return;
+
+    // Now that the layer (if any) has been updated, we need to adjust the diff again,
+    // check whether we should layout now, and decide if we need to repaint.
+    StyleDifference updatedDiff = adjustStyleDifference(diff, contextSensitiveProperties);
+    
+    if (diff <= StyleDifferenceLayoutPositionedMovementOnly) {
+        if (updatedDiff == StyleDifferenceLayout)
+            setNeedsLayoutAndPrefWidthsRecalc();
+        else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly)
+            setNeedsPositionedMovementLayout();
+    }
+    
+    if (updatedDiff == StyleDifferenceRepaintLayer || updatedDiff == StyleDifferenceRepaint) {
+        // Do a repaint with the new style now, e.g., for example if we go from
+        // not having an outline to having an outline.
+        repaint();
+    }
 }
 
 void RenderObject::setStyleInternal(PassRefPtr<RenderStyle> style)
@@ -2083,10 +2137,9 @@ void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*)
         setNeedsLayoutAndPrefWidthsRecalc();
     else if (diff == StyleDifferenceLayoutPositionedMovementOnly)
         setNeedsPositionedMovementLayout();
-    else if (diff == StyleDifferenceRepaintLayer || diff == StyleDifferenceRepaint)
-        // Do a repaint with the new style now, e.g., for example if we go from
-        // not having an outline to having an outline.
-        repaint();
+
+    // Don't check for repaint here; we need to wait until the layer has been
+    // updated by subclasses before we know if we have to repaint (in setStyle()).
 }
 
 void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers)
index 4da5e84..b51991f 100644 (file)
@@ -791,7 +791,8 @@ protected:
     
 private:
     RenderStyle* firstLineStyleSlowCase() const;
-
+    StyleDifference adjustStyleDifference(StyleDifference, unsigned contextSensitiveProperties) const;
+    
     RefPtr<RenderStyle> m_style;
 
     Node* m_node;
index 4ad4a67..bdca736 100644 (file)
@@ -271,8 +271,10 @@ static bool positionedObjectMoved(const LengthBox& a, const LengthBox& b)
   optimisations are unimplemented, and currently result in the
   worst case result causing a relayout of the containing block.
 */
-StyleDifference RenderStyle::diff(const RenderStyle* other) const
+StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const
 {
+    changedContextSensitiveProperties = ContextSensitivePropertyNone;
+
 #if ENABLE(SVG)
     // This is horribly inefficient.  Eventually we'll have to integrate
     // this more directly by calling: Diff svgDiff = svgStyle->diff(other)
@@ -324,8 +326,14 @@ StyleDifference RenderStyle::diff(const RenderStyle* other) const
             return StyleDifferenceLayout;
 
         if (rareNonInheritedData->m_transform.get() != other->rareNonInheritedData->m_transform.get() &&
-            *rareNonInheritedData->m_transform.get() != *other->rareNonInheritedData->m_transform.get())
+            *rareNonInheritedData->m_transform.get() != *other->rareNonInheritedData->m_transform.get()) {
+#if USE(ACCELERATED_COMPOSITING)
+            changedContextSensitiveProperties |= ContextSensitivePropertyTransform;
+            // Don't return; keep looking for another change
+#else
             return StyleDifferenceLayout;
+#endif
+        }
 
 #if ENABLE(DASHBOARD_SUPPORT)
         // If regions change, trigger a relayout to re-calc regions.
@@ -445,8 +453,16 @@ StyleDifference RenderStyle::diff(const RenderStyle* other) const
             return StyleDifferenceRepaintLayer;
     }
 
-    if (rareNonInheritedData->opacity != other->rareNonInheritedData->opacity ||
-        rareNonInheritedData->m_mask != other->rareNonInheritedData->m_mask ||
+    if (rareNonInheritedData->opacity != other->rareNonInheritedData->opacity) {
+#if USE(ACCELERATED_COMPOSITING)
+        changedContextSensitiveProperties |= ContextSensitivePropertyOpacity;
+        // Don't return; keep looking for another change.
+#else
+        return StyleDifferenceRepaintLayer;
+#endif
+    }
+
+    if (rareNonInheritedData->m_mask != other->rareNonInheritedData->m_mask ||
         rareNonInheritedData->m_maskBoxImage != other->rareNonInheritedData->m_maskBoxImage)
         return StyleDifferenceRepaintLayer;
 
index 43a17f6..297f455 100644 (file)
@@ -982,7 +982,7 @@ public:
 
     bool inheritedNotEqual(RenderStyle*) const;
 
-    StyleDifference diff(const RenderStyle*) const;
+    StyleDifference diff(const RenderStyle*, unsigned& changedContextSensitiveProperties) const;
 
     bool isDisplayReplacedType() const
     {
index 0ac8432..5aa18de 100644 (file)
@@ -38,17 +38,31 @@ namespace WebCore {
 
 // The difference between two styles.  The following values are used:
 // (1) StyleDifferenceEqual - The two styles are identical
-// (2) StyleDifferenceRepaint - The object just needs to be repainted.
-// (3) StyleDifferenceRepaintLayer - The layer and its descendant layers needs to be repainted.
-// (4) StyleDifferenceLayout - A layout is required.
+// (2) StyleDifferenceRecompositeLayer - The layer needs its position and transform updated, but no repaint
+// (3) StyleDifferenceRepaint - The object just needs to be repainted.
+// (4) StyleDifferenceRepaintLayer - The layer and its descendant layers needs to be repainted.
+// (5) StyleDifferenceLayout - A layout is required.
 enum StyleDifference {
     StyleDifferenceEqual,
+#if USE(ACCELERATED_COMPOSITING)
+    StyleDifferenceRecompositeLayer,
+#endif
     StyleDifferenceRepaint,
     StyleDifferenceRepaintLayer,
     StyleDifferenceLayoutPositionedMovementOnly,
     StyleDifferenceLayout
 };
 
+// When some style properties change, different amounts of work have to be done depending on
+// context (e.g. whether the property is changing on an element which has a compositing layer).
+// A simple StyleDifference does not provide enough information so we return a bit mask of
+// StyleDifferenceContextSensitiveProperties from RenderStyle::diff() too.
+enum StyleDifferenceContextSensitiveProperty {
+    ContextSensitivePropertyNone = 0,
+    ContextSensitivePropertyTransform = (1 << 0),
+    ContextSensitivePropertyOpacity = (1 << 1)
+};
+
 // Static pseudo styles. Dynamic ones are produced on the fly.
 enum PseudoId {
     NOPSEUDO, FIRST_LINE, FIRST_LETTER, BEFORE, AFTER, SELECTION, FIRST_LINE_INHERITED, SCROLLBAR, FILE_UPLOAD_BUTTON, INPUT_PLACEHOLDER,