Bug 1153539 patch 2 - Compute scale for rasterizing off-main-thread transform animations based on ratio to display size in addition to maximum and minimum scale. r=roc
authorL. David Baron <dbaron@dbaron.org>
Wed, 06 May 2015 08:52:20 +0200
changeset 273951 8fccf4962f1adbf2496f824f6eaa954160cfae42
parent 273950 868b584b3eacd59a167483839adea45b8a86f4b1
child 273952 aaa5e4221fd123fc9b96c4f977ef097ece052125
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1153539, 945082, 972310, 0, 586
milestone40.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 1153539 patch 2 - Compute scale for rasterizing off-main-thread transform animations based on ratio to display size in addition to maximum and minimum scale. r=roc This fixes bug 1153539 with the Firefox download arrow being pixellated (verified by testing locally). I also confirmed what happens on a flame device with the transform unlocking the Firefox OS homescreen (bug 945082 / bug 972310). For that transform (which had a maximum scale of 2), I see four calls to GetSuitableScale: GetSuitableScale: aMaxScale=2.000000, displayVisibleRatio=1.000000 GetSuitableScale: aMaxScale=2.000000, displayVisibleRatio=1.000586 GetSuitableScale: aMaxScale=2.000000, displayVisibleRatio=1.000000 GetSuitableScale: aMaxScale=2.000000, displayVisibleRatio=1.000586 (Presumably the first and third are for width, and the second and fourth are for height.) I believe this shows that bug 972310 will remain fixed with this patch. I chose to use the pres context's visible area rather than the screen size because it seemed more appropriate, and also because (if memory serves correctly) it's much cheaper to get.
layout/base/FrameLayerBuilder.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4480,17 +4480,19 @@ ChooseScaleAndSetTransform(FrameLayerBui
   gfxSize scale;
   // XXX Should we do something for 3D transforms?
   if (canDraw2D) {
     // If the container's transform is animated off main thread, fix a suitable scale size
     // for animation
     if (aContainerFrame->GetContent() &&
         nsLayoutUtils::HasAnimationsForCompositor(
           aContainerFrame->GetContent(), eCSSProperty_transform)) {
-      scale = nsLayoutUtils::ComputeSuitableScaleForAnimation(aContainerFrame->GetContent());
+      scale = nsLayoutUtils::ComputeSuitableScaleForAnimation(
+                aContainerFrame->GetContent(), aVisibleRect.Size(),
+                aContainerFrame->PresContext()->GetVisibleArea().Size());
     } else {
       // Scale factors are normalized to a power of 2 to reduce the number of resolution changes
       scale = RoundToFloatPrecision(ThebesMatrix(transform2d).ScaleFactors(true));
       // For frames with a changing transform that's not just a translation,
       // round scale factors up to nearest power-of-2 boundary so that we don't
       // keep having to redraw the content as it scales up and down. Rounding up to nearest
       // power-of-2 boundary ensures we never scale up, only down --- avoiding
       // jaggies. It also ensures we never scale down by more than a factor of 2,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -476,28 +476,26 @@ GetScaleForValue(const StyleAnimationVal
   if (!canDraw2D) {
     return gfxSize();
   }
 
   return transform2d.ScaleFactors(true);
 }
 
 static float
-GetSuitableScale(float aMaxScale, float aMinScale)
-{
-  // If the minimum scale >= 1.0f, use it; if the maximum <= 1.0f, use it;
-  // otherwise use 1.0f.
-  if (aMinScale >= 1.0f) {
-    return aMinScale;
-  }
-  else if (aMaxScale <= 1.0f) {
-    return aMaxScale;
-  }
-
-  return 1.0f;
+GetSuitableScale(float aMaxScale, float aMinScale,
+                 nscoord aVisibleDimension, nscoord aDisplayDimension)
+{
+  float displayVisibleRatio = float(aDisplayDimension) /
+                              float(aVisibleDimension);
+  // We want to rasterize based on the largest scale used during the
+  // transform animation, unless that would make us rasterize something
+  // larger than the screen.  But we never want to go smaller than the
+  // minimum scale over the animation.
+  return std::max(std::min(aMaxScale, displayVisibleRatio), aMinScale);
 }
 
 static void
 GetMinAndMaxScaleForAnimationProperty(nsIContent* aContent,
                                       AnimationCollection* aAnimations,
                                       gfxSize& aMaxScale,
                                       gfxSize& aMinScale)
 {
@@ -526,17 +524,19 @@ GetMinAndMaxScaleForAnimationProperty(ns
           aMinScale.height = std::min<float>(aMinScale.height, to.height);
         }
       }
     }
   }
 }
 
 gfxSize
-nsLayoutUtils::ComputeSuitableScaleForAnimation(nsIContent* aContent)
+nsLayoutUtils::ComputeSuitableScaleForAnimation(nsIContent* aContent,
+                                                const nsSize& aVisibleSize,
+                                                const nsSize& aDisplaySize)
 {
   gfxSize maxScale(std::numeric_limits<gfxFloat>::min(),
                    std::numeric_limits<gfxFloat>::min());
   gfxSize minScale(std::numeric_limits<gfxFloat>::max(),
                    std::numeric_limits<gfxFloat>::max());
 
   AnimationCollection* animations =
     nsAnimationManager::GetAnimationsForCompositor(aContent,
@@ -551,21 +551,23 @@ nsLayoutUtils::ComputeSuitableScaleForAn
                                                     eCSSProperty_transform);
   if (animations) {
     GetMinAndMaxScaleForAnimationProperty(aContent, animations,
                                           maxScale, minScale);
   }
 
   if (maxScale.width == std::numeric_limits<gfxFloat>::min()) {
     // We didn't encounter a transform
-    maxScale = minScale = gfxSize(1.0, 1.0);
-  }
-
-  return gfxSize(GetSuitableScale(maxScale.width, minScale.width),
-                 GetSuitableScale(maxScale.height, minScale.height));
+    return gfxSize(1.0, 1.0);
+  }
+
+  return gfxSize(GetSuitableScale(maxScale.width, minScale.width,
+                                  aVisibleSize.width, aDisplaySize.width),
+                 GetSuitableScale(maxScale.height, minScale.height,
+                                  aVisibleSize.height, aDisplaySize.height));
 }
 
 bool
 nsLayoutUtils::AreAsyncAnimationsEnabled()
 {
   static bool sAreAsyncAnimationsEnabled;
   static bool sAsyncPrefCached = false;
 
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2148,18 +2148,22 @@ public:
   /**
    * Find a suitable scale for an element (aContent) over the course of any
    * animations and transitions of the CSS transform property on the
    * element that run on the compositor thread.
    * It will check the maximum and minimum scale during the animations and
    * transitions and return a suitable value for performance and quality.
    * Will return scale(1,1) if there are no such animations.
    * Always returns a positive value.
+   * @param aVisibleSize is the size of the area we want to paint
+   * @param aDisplaySize is the size of the display area of the pres context
    */
-  static gfxSize ComputeSuitableScaleForAnimation(nsIContent* aContent);
+  static gfxSize ComputeSuitableScaleForAnimation(nsIContent* aContent,
+                                                  const nsSize& aVisibleSize,
+                                                  const nsSize& aDisplaySize);
 
   /**
    * Checks if we should forcibly use nearest pixel filtering for the
    * background.
    */
   static bool UseBackgroundNearestFiltering();
 
   /**