Bug 1321412 - Add support for partial prerendering to ShouldPrerenderTransformedContent(). r=mattwoodrow
☠☠ backed out by f8a867670915 ☠ ☠
authorBotond Ballo <botond@mozilla.com>
Mon, 28 Nov 2016 15:30:52 -0500
changeset 372790 b2508bbc5ef9db3c4a30a90dea686509e735f9c3
parent 372789 9587048cd2ee1f0f0a424995b789ddb05ccbcd32
child 372791 a8e8ba250d6218fbc9a5aa6cb63b263bb0ab5d32
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1321412
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1321412 - Add support for partial prerendering to ShouldPrerenderTransformedContent(). r=mattwoodrow This is behind a pref and not enabled by default yet. MozReview-Commit-ID: HKbP02PkdI9
gfx/thebes/gfxPrefs.h
layout/generic/nsFrame.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
modules/libpref/init/all.js
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -523,16 +523,17 @@ private:
   DECL_GFX_PREF(Once, "layers.tiles.edge-padding",             TileEdgePaddingEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.enabled",          LayerTileFadeInEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.duration-ms",      LayerTileFadeInDuration, uint32_t, 250);
   DECL_GFX_PREF(Live, "layers.transaction.warning-ms",         LayerTransactionWarning, uint32_t, 200);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, true);
   DECL_GFX_PREF(Live, "layers.draw-mask-debug",                DrawMaskLayer, bool, false);
 
+  DECL_GFX_PREF(Live, "layout.animation.prerender.partial", PartiallyPrerenderAnimatedContent, bool, false);
   DECL_GFX_PREF(Live, "layout.animation.prerender.viewport-ratio-limit-x", AnimationPrerenderViewportRatioLimitX, float, 1.125f);
   DECL_GFX_PREF(Live, "layout.animation.prerender.viewport-ratio-limit-y", AnimationPrerenderViewportRatioLimitY, float, 1.125f);
   DECL_GFX_PREF(Live, "layout.animation.prerender.absolute-limit-x", AnimationPrerenderAbsoluteLimitX, uint32_t, 4096);
   DECL_GFX_PREF(Live, "layout.animation.prerender.absolute-limit-y", AnimationPrerenderAbsoluteLimitY, uint32_t, 4096);
 
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled",    ScrollBehaviorEnabled, bool, true);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2209,16 +2209,20 @@ nsIFrame::BuildDisplayListForStackingCon
   if (isTransformed) {
     const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
     nsDisplayTransform::PrerenderDecision decision =
         nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this, &dirtyRect);
     switch (decision) {
     case nsDisplayTransform::FullPrerender:
       allowAsyncAnimation = true;
       break;
+    case nsDisplayTransform::PartialPrerender:
+      allowAsyncAnimation = true;
+      MOZ_FALLTHROUGH;
+      // fall through to the NoPrerender case
     case nsDisplayTransform::NoPrerender:
       if (overflow.IsEmpty() && !extend3DContext) {
         return;
       }
 
       // If we're in preserve-3d then grab the dirty rect that was given to the root
       // and transform using the combined transform.
       if (Combines3DTransformWithAncestors()) {
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -6221,16 +6221,31 @@ nsDisplayOpacity::CanUseAsyncAnimations(
 }
 
 bool
 nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
 {
   return mAllowAsyncAnimation;
 }
 
+static nsRect ComputePartialPrerenderArea(const nsRect& aDirtyRect,
+                                          const nsRect& aOverflow,
+                                          const nsSize& aPrerenderSize)
+{
+  // Simple calculation for now: center the pre-render area on the dirty rect,
+  // and clamp to the overflow area. Later we can do more advanced things like
+  // redistributing from one axis to another, or from one side to another.
+  nscoord xExcess = aPrerenderSize.width - aDirtyRect.width;
+  nscoord yExcess = aPrerenderSize.height - aDirtyRect.height;
+  return nsRect(aDirtyRect.x - (xExcess / 2),
+                aDirtyRect.y - (yExcess / 2),
+                aPrerenderSize.width,
+                aPrerenderSize.height).MoveInsideAndClamp(aOverflow);
+}
+
 /* static */ auto
 nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
                                                       nsIFrame* aFrame,
                                                       nsRect* aDirtyRect) -> PrerenderDecision
 {
   // Elements whose transform has been modified recently, or which
   // have a compositor-animated transform, can be prerendered. An element
   // might have only just had its transform animated in which case
@@ -6261,16 +6276,19 @@ nsDisplayTransform::ShouldPrerenderTrans
   nsSize maxSize = Min(relativeLimit, absoluteLimit);
   gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(aFrame);
   nsRect overflow = aFrame->GetVisualOverflowRectRelativeToSelf();
   nsSize frameSize = nsSize(overflow.Size().width * scale.width,
                             overflow.Size().height * scale.height);
   if (frameSize <= maxSize) {
     *aDirtyRect = overflow;
     return FullPrerender;
+  } else if (gfxPrefs::PartiallyPrerenderAnimatedContent()) {
+    *aDirtyRect = ComputePartialPrerenderArea(*aDirtyRect, overflow, maxSize);
+    return PartialPrerender;
   }
 
   nsRect visual = aFrame->GetVisualOverflowRect();
 
 
   EffectCompositor::SetPerformanceWarning(
     aFrame, eCSSProperty_transform,
     AnimationPerformanceWarning(
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -4060,17 +4060,18 @@ class nsDisplayTransform: public nsDispl
       }
       nsDisplayWrapList::UpdateBounds(aBuilder);
     }
   };
 
 public:
   enum PrerenderDecision {
     NoPrerender,
-    FullPrerender
+    FullPrerender,
+    PartialPrerender
   };
 
   /**
    * Returns a matrix (in pixels) for the current frame. The matrix should be relative to
    * the current frame's coordinate space.
    *
    * @param aFrame The frame to compute the transform for.
    * @param aAppUnitsPerPixel The number of app units per graphics unit.
@@ -4276,18 +4277,21 @@ public:
                                                uint32_t aFlags,
                                                const nsRect* aBoundsOverride = nullptr);
   static Matrix4x4 GetResultingTransformMatrix(const FrameTransformProperties& aProperties,
                                                const nsPoint& aOrigin,
                                                float aAppUnitsPerPixel,
                                                uint32_t aFlags,
                                                const nsRect* aBoundsOverride = nullptr);
   /**
-   * Return FullPrerender when we should try to prerender the entire contents of the
+   * Decide whether we should prerender some or all of the contents of the
    * transformed frame even when it's not completely visible (yet).
+   * Return FullPrerender if the entire contents should be prerendered,
+   * PartialPrerender if some but not all of the contents should be prerendered,
+   * or NoPrerender if only the visible area should be rendered.
    * |aDirtyRect| is updated to the area that should be prerendered.
    */
   static PrerenderDecision ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
                                                              nsIFrame* aFrame,
                                                              nsRect* aDirtyRect);
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
   bool MayBeAnimated(nsDisplayListBuilder* aBuilder);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2733,16 +2733,17 @@ pref("dom.animations-api.core.enabled", 
 // ignored.
 pref("dom.animations-api.element-animate.enabled", true);
 
 // Pref to throttle offsreen animations
 pref("dom.animations.offscreen-throttling", true);
 
 // Prefs to control the maximum area to pre-render when animating a large
 // element on the compositor.
+pref("layout.animation.prerender.partial", false);
 pref("layout.animation.prerender.viewport-ratio-limit-x", "1.125");
 pref("layout.animation.prerender.viewport-ratio-limit-y", "1.125");
 pref("layout.animation.prerender.absolute-limit-x", 4096);
 pref("layout.animation.prerender.absolute-limit-y", 4096);
 
 // pref to permit users to make verified SOAP calls by default
 pref("capability.policy.default.SOAPCall.invokeVerifySourceHeader", "allAccess");