Bug 1364221 - Allow frames to be prerendered as long as the area of the frame is less than the area of the relative limit and the dimensions are less than the absolute limit. r=mattwoodrow,mstange
authorJared Wein <jwein@mozilla.com>
Mon, 05 Jun 2017 17:01:19 -0400
changeset 362433 c93d5b222a0f8bf1b3834071558a26d185139372
parent 362432 21e1a47c4852e1fd5463e79891a98f81fc2e930d
child 362434 4117a726b0b4f9c685080dd0f31c14ed80b76e78
push id31977
push userarchaeopteryx@coole-files.de
push dateTue, 06 Jun 2017 09:17:27 +0000
treeherdermozilla-central@d3b8e8571020 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, mstange
bugs1364221
milestone55.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 1364221 - Allow frames to be prerendered as long as the area of the frame is less than the area of the relative limit and the dimensions are less than the absolute limit. r=mattwoodrow,mstange MozReview-Commit-ID: C3QLjaCRbzE
dom/animation/AnimationPerformanceWarning.cpp
dom/animation/AnimationPerformanceWarning.h
dom/animation/test/chrome/test_animation_performance_warning.html
dom/locales/en-US/chrome/layout/layout_errors.properties
layout/painting/nsDisplayList.cpp
--- a/dom/animation/AnimationPerformanceWarning.cpp
+++ b/dom/animation/AnimationPerformanceWarning.cpp
@@ -30,21 +30,28 @@ bool
 AnimationPerformanceWarning::ToLocalizedString(
   nsXPIDLString& aLocalizedString) const
 {
   const char* key = nullptr;
 
   switch (mType) {
     case Type::ContentTooLarge:
       MOZ_ASSERT(mParams && mParams->Length() == 6,
-                 "Parameter's length should be 6 for ContentTooLarge");
+                 "Parameter's length should be 6 for ContentTooLarge2");
 
       return NS_SUCCEEDED(
         ToLocalizedStringWithIntParams<7>(
           "CompositorAnimationWarningContentTooLarge2", aLocalizedString));
+    case Type::ContentTooLargeArea:
+      MOZ_ASSERT(mParams && mParams->Length() == 2,
+                 "Parameter's length should be 2 for ContentTooLargeArea");
+
+      return NS_SUCCEEDED(
+        ToLocalizedStringWithIntParams<3>(
+          "CompositorAnimationWarningContentTooLargeArea", aLocalizedString));
     case Type::TransformBackfaceVisibilityHidden:
       key = "CompositorAnimationWarningTransformBackfaceVisibilityHidden";
       break;
     case Type::TransformPreserve3D:
       key = "CompositorAnimationWarningTransformPreserve3D";
       break;
     case Type::TransformSVG:
       key = "CompositorAnimationWarningTransformSVG";
--- a/dom/animation/AnimationPerformanceWarning.h
+++ b/dom/animation/AnimationPerformanceWarning.h
@@ -16,16 +16,17 @@ class nsXPIDLString;
 
 namespace mozilla {
 
 // Represents the reason why we can't run the CSS property on the compositor.
 struct AnimationPerformanceWarning
 {
   enum class Type : uint8_t {
     ContentTooLarge,
+    ContentTooLargeArea,
     TransformBackfaceVisibilityHidden,
     TransformPreserve3D,
     TransformSVG,
     TransformWithGeometricProperties,
     TransformWithSyncGeometricAnimations,
     TransformFrameInactive,
     OpacityFrameInactive,
     HasRenderingObserver,
--- a/dom/animation/test/chrome/test_animation_performance_warning.html
+++ b/dom/animation/test/chrome/test_animation_performance_warning.html
@@ -1170,23 +1170,58 @@ function start() {
                                      { class: 'compositable' },
                                      { transform: [ 'translate(0px)',
                                                     'translate(100px)'] },
                                      100 * MS_PER_SEC);
     return animation.ready.then(function() {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: true } ]);
-      animation.effect.target.style = 'width: 10000px; height: 10000px';
+      animation.effect.target.style = 'width: 5200px; height: 5200px';
+      return waitForFrame();
+    }).then(function() {
+      // viewport depends on test environment.
+      var expectedWarning = new RegExp(
+        "Animation cannot be run on the compositor because the area of the frame size " +
+        "\\(\\d+\\) is too large relative to the viewport " +
+        "\\(larger than \\d+\\)");
+      assert_animation_property_state_equals(
+        animation.effect.getProperties(),
+        [ {
+          property: 'transform',
+          runningOnCompositor: false,
+          warning: expectedWarning
+        } ]);
+      animation.effect.target.style = 'width: 100px; height: 100px';
+      return waitForFrame();
+    }).then(function() {
+      // FIXME: Bug 1253164: the animation should get back on compositor.
+      assert_animation_property_state_equals(
+        animation.effect.getProperties(),
+        [ { property: 'transform', runningOnCompositor: false } ]);
+    });
+  }, 'transform on too big element - area');
+
+  promise_test(function(t) {
+    var animation = addDivAndAnimate(t,
+                                     { class: 'compositable' },
+                                     { transform: [ 'translate(0px)',
+                                                    'translate(100px)'] },
+                                     100 * MS_PER_SEC);
+    return animation.ready.then(function() {
+      assert_animation_property_state_equals(
+        animation.effect.getProperties(),
+        [ { property: 'transform', runningOnCompositor: true } ]);
+      animation.effect.target.style = 'width: 5200px; height: 1px';
       return waitForFrame();
     }).then(function() {
       // viewport depends on test environment.
       var expectedWarning = new RegExp(
         "Animation cannot be run on the compositor because the frame size " +
-        "\\(10000, 10000\\) is too large relative to the viewport " + 
+        "\\(5200, 1\\) is too large relative to the viewport " +
         "\\(larger than \\(\\d+, \\d+\\)\\) or larger than the " +
         "maximum allowed value \\(\\d+, \\d+\\)");
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ {
           property: 'transform',
           runningOnCompositor: false,
           warning: expectedWarning
@@ -1194,17 +1229,17 @@ function start() {
       animation.effect.target.style = 'width: 100px; height: 100px';
       return waitForFrame();
     }).then(function() {
       // FIXME: Bug 1253164: the animation should get back on compositor.
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: false } ]);
     });
-  }, 'transform on too big element');
+  }, 'transform on too big element - dimensions');
 
   promise_test(function(t) {
     var svg  = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
     svg.setAttribute('width', '100');
     svg.setAttribute('height', '100');
     var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
     rect.setAttribute('width', '100');
     rect.setAttribute('height', '100');
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties
+++ b/dom/locales/en-US/chrome/layout/layout_errors.properties
@@ -6,16 +6,20 @@ ImageMapRectBoundsError=The “coords” attribute of the <area shape="rect"> tag is not in the “left,top,right,bottom” format.
 ImageMapCircleWrongNumberOfCoords=The “coords” attribute of the <area shape="circle"> tag is not in the “center-x,center-y,radius” format.
 ImageMapCircleNegativeRadius=The “coords” attribute of the <area shape="circle"> tag has a negative radius.
 ImageMapPolyWrongNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is not in the “x1,y1,x2,y2 …” format.
 ImageMapPolyOddNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is missing the last “y” coordinate (the correct format is “x1,y1,x2,y2 …”).
 
 TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect.
 ScrollLinkedEffectFound2=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!
 
+## LOCALIZATION NOTE(CompositorAnimationWarningContentTooLargeArea):
+## %1$S is an integer value of the area of the frame size
+## %2$S is an integer value of the area of a limit based on the viewport size
+CompositorAnimationWarningContentTooLargeArea=Animation cannot be run on the compositor because the area of the frame size (%1$S) is too large relative to the viewport (larger than %2$S)
 ## LOCALIZATION NOTE(CompositorAnimationWarningContentTooLarge2):
 ## (%1$S, %2$S) is a pair of integer values of the frame size
 ## (%3$S, %4$S) is a pair of integer values of a limit based on the viewport size
 ## (%5$S, %6$S) is a pair of integer values of an absolute limit
 CompositorAnimationWarningContentTooLarge2=Animation cannot be run on the compositor because the frame size (%1$S, %2$S) is too large relative to the viewport (larger than (%3$S, %4$S)) or larger than the maximum allowed value (%5$S, %6$S)
 ## LOCALIZATION NOTE(CompositorAnimationWarningTransformBackfaceVisibilityHidden):
 ## 'backface-visibility: hidden' is a CSS property, don't translate it.
 CompositorAnimationWarningTransformBackfaceVisibilityHidden=Animations of ‘backface-visibility: hidden’ transforms cannot be run on the compositor
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7351,38 +7351,53 @@ nsDisplayTransform::ShouldPrerenderTrans
   // reference frame size (~viewport), and less than an absolute limit.
   // Both the ratio and the absolute limit are configurable.
   nsSize relativeLimit(nscoord(refSize.width * viewportRatioX),
                        nscoord(refSize.height * viewportRatioY));
   nsSize absoluteLimit(aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
                        aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
   nsSize maxSize = Min(relativeLimit, absoluteLimit);
   gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(aFrame);
-  nsSize frameSize = nsSize(overflow.Size().width * scale.width,
-                            overflow.Size().height * scale.height);
-  if (frameSize <= maxSize) {
+  nsSize frameSize(overflow.Size().width * scale.width,
+                   overflow.Size().height * scale.height);
+  uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
+  uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
+  if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
     *aDirtyRect = overflow;
     return FullPrerender;
   } else if (gfxPrefs::PartiallyPrerenderAnimatedContent()) {
     *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(*aDirtyRect, overflow, maxSize);
     return PartialPrerender;
   }
 
-  EffectCompositor::SetPerformanceWarning(
-    aFrame, eCSSProperty_transform,
-    AnimationPerformanceWarning(
-      AnimationPerformanceWarning::Type::ContentTooLarge,
-      {
-        nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
-        nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
-        nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
-        nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
-        nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
-        nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
-      }));
+  if (frameArea > maxLimitArea) {
+    uint64_t appUnitsPerPixel = nsPresContext::AppUnitsPerCSSPixel();
+    EffectCompositor::SetPerformanceWarning(
+      aFrame, eCSSProperty_transform,
+      AnimationPerformanceWarning(
+        AnimationPerformanceWarning::Type::ContentTooLargeArea,
+        {
+          int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
+          int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
+        }));
+  } else {
+    EffectCompositor::SetPerformanceWarning(
+      aFrame, eCSSProperty_transform,
+      AnimationPerformanceWarning(
+        AnimationPerformanceWarning::Type::ContentTooLarge,
+        {
+          nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
+          nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
+          nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
+          nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
+          nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
+          nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
+        }));
+  }
+
   return NoPrerender;
 }
 
 /* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */
 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix)
 {
   if (aMatrix.IsSingular()) {
     return false;