Bug 1467619 - Track and adjust layerization on repaint triggers r?mattwoodrow draft
authorDoug Thayer <dothayer@mozilla.com>
Thu, 07 Jun 2018 11:44:03 -0700
changeset 805558 dd845ecd31bb81422a4a43e3bd98a82f6bfc7468
parent 805204 199a085199815cc99daa658956a7c9436e1d436b
push id112693
push userbmo:dothayer@mozilla.com
push dateThu, 07 Jun 2018 23:29:37 +0000
reviewersmattwoodrow
bugs1467619
milestone62.0a1
Bug 1467619 - Track and adjust layerization on repaint triggers r?mattwoodrow To avoid creating a bunch of layers when we don't need to, this tracks when a frame needs to be repainted, and invalidates the IsStyleAnimated logic around mRestyleCounts if the frame is also being invalidated. MozReview-Commit-ID: 5Q96Cx6f3V0
layout/base/RestyleManager.cpp
layout/painting/ActiveLayerTracker.cpp
layout/painting/ActiveLayerTracker.h
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1020,16 +1020,18 @@ DoApplyRenderingChangeToTree(nsIFrame* a
       needInvalidatingPaint = true;
       aFrame->InvalidateFrameSubtree();
       if ((aChange & nsChangeHint_UpdateEffects) &&
           aFrame->IsFrameOfType(nsIFrame::eSVG) &&
           !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
         // Need to update our overflow rects:
         nsSVGUtils::ScheduleReflowSVG(aFrame);
       }
+
+      ActiveLayerTracker::NotifyNeedsRepaint(aFrame);
     }
     if (aChange & nsChangeHint_UpdateTextPath) {
       if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
         // Invalidate and reflow the entire SVGTextFrame:
         NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
                      "expected frame for a <textPath> element");
         nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
           aFrame, LayoutFrameType::SVGText);
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -45,16 +45,17 @@ public:
     ACTIVITY_TRANSFORM,
     ACTIVITY_LEFT,
     ACTIVITY_TOP,
     ACTIVITY_RIGHT,
     ACTIVITY_BOTTOM,
     ACTIVITY_BACKGROUND_POSITION,
 
     ACTIVITY_SCALE,
+    ACTIVITY_TRIGGERED_REPAINT,
 
     // keep as last item
     ACTIVITY_COUNT
   };
 
   explicit LayerActivity(nsIFrame* aFrame)
     : mFrame(aFrame)
     , mContent(nullptr)
@@ -365,16 +366,29 @@ ActiveLayerTracker::NotifyInlineStyleRul
   }
   if (gLayerActivityTracker &&
       gLayerActivityTracker->mCurrentScrollHandlerFrame.IsAlive()) {
     NotifyAnimatedFromScrollHandler(aFrame, aProperty,
       gLayerActivityTracker->mCurrentScrollHandlerFrame.GetFrame());
   }
 }
 
+/* static */ void
+ActiveLayerTracker::NotifyNeedsRepaint(nsIFrame* aFrame)
+{
+  LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
+  if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
+    // This is mirroring NotifyInlineStyleRuleModified's NotifyAnimated logic. Just max out
+    // the restyle count if we're in an animation callback.
+    layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] = 0xFF;
+  } else {
+    IncrementMutationCount(&layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT]);
+  }
+}
+
 /* static */ bool
 ActiveLayerTracker::IsStyleMaybeAnimated(nsIFrame* aFrame, nsCSSPropertyID aProperty)
 {
   return IsStyleAnimated(nullptr, aFrame, aProperty);
 }
 
 /* static */ bool
 ActiveLayerTracker::IsBackgroundPositionAnimated(nsDisplayListBuilder* aBuilder,
@@ -421,17 +435,18 @@ ActiveLayerTracker::IsStyleAnimated(nsDi
       aProperty == eCSSProperty_opacity &&
       (!aBuilder || aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
 
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   if (layerActivity) {
     LayerActivity::ActivityIndex activityIndex = LayerActivity::GetActivityIndexForProperty(aProperty);
-    if (layerActivity->mRestyleCounts[activityIndex] >= 2) {
+    if (layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] < 2 &&
+        layerActivity->mRestyleCounts[activityIndex] >= 2) {
       return true;
     }
     if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) {
       return true;
     }
   }
   if (aProperty == eCSSProperty_transform && aFrame->Combines3DTransformWithAncestors()) {
     return IsStyleAnimated(aBuilder, aFrame->GetParent(), aProperty);
--- a/layout/painting/ActiveLayerTracker.h
+++ b/layout/painting/ActiveLayerTracker.h
@@ -69,16 +69,23 @@ public:
    * style will trigger this.
    * aNewValue and aDOMCSSDecl are used to determine whether the property's
    * value has changed.
    */
   static void NotifyInlineStyleRuleModified(nsIFrame* aFrame, nsCSSPropertyID aProperty,
                                             const nsAString& aNewValue,
                                             nsDOMCSSDeclaration* aDOMCSSDecl);
   /**
+   * Notify that a frame needs to be repainted. This is important for layering
+   * decisions where, say, aFrame's transform is updated from JS, but we need
+   * to repaint aFrame anyway, so we get no benefit from giving it its own
+   * layer.
+   */
+  static void NotifyNeedsRepaint(nsIFrame* aFrame);
+  /**
    * Return true if aFrame's aProperty style should be considered as being animated
    * for pre-rendering.
    */
   static bool IsStyleMaybeAnimated(nsIFrame* aFrame, nsCSSPropertyID aProperty);
   /**
    * Return true if aFrame's aProperty style should be considered as being animated
    * for constructing active layers.
    */