Bug 1165185 - Try to avoid invalidations when scrolling transformed elements. r=roc
authorMarkus Stange <mstange@themasta.com>
Mon, 15 Jun 2015 19:20:59 -0400
changeset 249905 3f025568ad349d7b4a4da45feef6812c4e4d4101
parent 249904 cd741d3ae78a7ef127b6baf7c4efc0fff0472267
child 249906 5f5c445f7e20abef8665dc47d92f9e3a64893ace
push id28940
push usercbook@mozilla.com
push dateMon, 22 Jun 2015 12:03:34 +0000
treeherdermozilla-central@be81b8d6fae9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1165185
milestone41.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 1165185 - Try to avoid invalidations when scrolling transformed elements. r=roc
gfx/2d/Matrix.h
gfx/layers/LayerTreeInvalidation.cpp
layout/base/FrameLayerBuilder.cpp
layout/reftests/backgrounds/vector/empty/reftest.list
layout/reftests/invalidation/fractional-transform-1.html
layout/reftests/invalidation/fractional-transform-2.html
layout/reftests/invalidation/fractional-transform-3.html
layout/reftests/invalidation/reftest.list
mfbt/FloatingPoint.h
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -8,16 +8,17 @@
 
 #include "Types.h"
 #include "Rect.h"
 #include "Point.h"
 #include <iosfwd>
 #include <math.h>
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/FloatingPoint.h"
 
 namespace mozilla {
 namespace gfx {
 
 class Quaternion;
 
 static bool FuzzyEqual(Float aV1, Float aV2) {
   // XXX - Check if fabs does the smart thing and just negates the sign bit.
@@ -852,16 +853,36 @@ public:
            gfx::FuzzyEqual(_21, o._21) && gfx::FuzzyEqual(_22, o._22) &&
            gfx::FuzzyEqual(_23, o._23) && gfx::FuzzyEqual(_24, o._24) &&
            gfx::FuzzyEqual(_31, o._31) && gfx::FuzzyEqual(_32, o._32) &&
            gfx::FuzzyEqual(_33, o._33) && gfx::FuzzyEqual(_34, o._34) &&
            gfx::FuzzyEqual(_41, o._41) && gfx::FuzzyEqual(_42, o._42) &&
            gfx::FuzzyEqual(_43, o._43) && gfx::FuzzyEqual(_44, o._44);
   }
 
+  bool FuzzyEqualsMultiplicative(const Matrix4x4& o) const
+  {
+    return ::mozilla::FuzzyEqualsMultiplicative(_11, o._11) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_12, o._12) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_13, o._13) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_14, o._14) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_21, o._21) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_22, o._22) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_23, o._23) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_24, o._24) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_31, o._31) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_32, o._32) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_33, o._33) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_34, o._34) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_41, o._41) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_42, o._42) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_43, o._43) &&
+           ::mozilla::FuzzyEqualsMultiplicative(_44, o._44);
+  }
+
   bool IsBackfaceVisible() const
   {
     // Inverse()._33 < 0;
     Float det = Determinant();
     Float __33 = _12*_24*_41 - _14*_22*_41 +
                 _14*_21*_42 - _11*_24*_42 -
                 _12*_21*_44 + _11*_22*_44;
     return (__33 * det) < 0;
@@ -884,16 +905,34 @@ public:
     static const float error = 1e-5f;
     NudgeToInteger(&_41, error);
     NudgeToInteger(&_42, error);
     NudgeToInteger(&_43, error);
     NudgeToInteger(&_44, error);
     return *this;
   }
 
+  // Nudge the 3D components to integer so that this matrix will become 2D if
+  // it's very close to already being 2D.
+  // This doesn't change the _41 and _42 components.
+  Matrix4x4 &NudgeTo2D()
+  {
+    NudgeToInteger(&_13);
+    NudgeToInteger(&_14);
+    NudgeToInteger(&_23);
+    NudgeToInteger(&_24);
+    NudgeToInteger(&_31);
+    NudgeToInteger(&_32);
+    NudgeToInteger(&_33);
+    NudgeToInteger(&_34);
+    NudgeToInteger(&_43);
+    NudgeToInteger(&_44);
+    return *this;
+  }
+
   Point4D TransposedVector(int aIndex) const
   {
       MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
       return Point4D(*((&_11)+aIndex), *((&_21)+aIndex), *((&_31)+aIndex), *((&_41)+aIndex));
   }
 
   void SetTransposedVector(int aIndex, Point4D &aVector)
   {
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -131,17 +131,17 @@ struct LayerPropertiesBase : public Laye
                                          NotifySubDocInvalidationFunc aCallback,
                                          bool* aGeometryChanged);
 
   virtual void MoveBy(const IntPoint& aOffset);
 
   nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
                             bool& aGeometryChanged)
   {
-    bool transformChanged = !mTransform.FuzzyEqual(mLayer->GetLocalTransform()) ||
+    bool transformChanged = !mTransform.FuzzyEqualsMultiplicative(mLayer->GetLocalTransform()) ||
                             mLayer->GetPostXScale() != mPostXScale ||
                             mLayer->GetPostYScale() != mPostYScale;
     Layer* otherMask = mLayer->GetMaskLayer();
     const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetClipRect();
     nsIntRegion result;
     if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
         (mUseClipRect != !!otherClip) ||
         mLayer->GetLocalOpacity() != mOpacity ||
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4625,22 +4625,23 @@ ChooseScaleAndSetTransform(FrameLayerBui
 {
   nsIntPoint offset;
 
   Matrix4x4 transform =
     Matrix4x4::Scaling(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
   if (aTransform) {
     // aTransform is applied first, then the scale is applied to the result
     transform = (*aTransform)*transform;
-    // Set any matrix entries close to integers to be those exact integers.
-    // This protects against floating-point inaccuracies causing problems
-    // in the checks below.
-    // We use the fixed epsilon version here because we don't want the nudging
-    // to depend on the scroll position.
-    transform.NudgeToIntegersFixedEpsilon();
+    // Set relevant 3d matrix entries that are close to integers to be those
+    // exact integers. This protects against floating-point inaccuracies
+    // causing problems in the CanDraw2D / Is2D checks below.
+    // We don't nudge all matrix components here. In particular, we don't want to
+    // nudge the X/Y translation components, because those include the scroll
+    // offset, and we don't want scrolling to affect whether we nudge or not.
+    transform.NudgeTo2D();
   }
   Matrix transform2d;
   if (aContainerFrame &&
       (aState == LAYER_INACTIVE || aState == LAYER_SVG_EFFECTS) &&
       (!aTransform || (aTransform->Is2D(&transform2d) &&
                        !transform2d.HasNonTranslation()))) {
     // When we have an inactive ContainerLayer, translate the container by the offset to the
     // reference frame (and offset all child layers by the reverse) so that the coordinate
--- a/layout/reftests/backgrounds/vector/empty/reftest.list
+++ b/layout/reftests/backgrounds/vector/empty/reftest.list
@@ -1,18 +1,17 @@
 == tall--contain--height.html ref-tall-empty.html
 == tall--contain--width.html ref-tall-empty.html
 == wide--contain--height.html ref-wide-empty.html
 == wide--contain--width.html ref-wide-empty.html
 
-# These tests fail because of integer overflow; see bug 894555.
-fails == tall--cover--height.html ref-tall-lime.html
-fails == tall--cover--width.html ref-tall-lime.html
-fails == wide--cover--height.html ref-wide-lime.html
-fails == wide--cover--width.html ref-wide-lime.html
+== tall--cover--height.html ref-tall-lime.html
+== tall--cover--width.html ref-tall-lime.html
+== wide--cover--height.html ref-wide-lime.html
+== wide--cover--width.html ref-wide-lime.html
 
 == zero-height-ratio-contain.html ref-tall-empty.html
 == zero-height-ratio-cover.html ref-tall-empty.html
 == zero-height-ratio-auto-auto.html ref-tall-empty.html
 == zero-height-ratio-auto-5px.html ref-tall-empty.html
 == zero-height-ratio-5px-auto.html ref-tall-empty.html
 == zero-width-ratio-contain.html ref-tall-empty.html
 == zero-width-ratio-cover.html ref-tall-empty.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/fractional-transform-1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Scrolling shouldn't invalidate either rect.</title>
+
+<style>
+body {
+  margin: 0;
+  height: 5000px;
+}
+</style>
+
+<body>
+
+<svg width="768" height="1000">
+
+  <g transform="translate(0 -2000.3234)">
+    <rect x="100" y="2300" height="50" width="50" fill="grey" class="reftest-no-paint"/>
+  </g>
+
+  <g transform="translate(0 -4000.6468)">
+    <rect x="200" y="4300" height="50" width="50" fill="grey" class="reftest-no-paint"/>
+  </g>
+
+</svg>
+
+<script>
+
+document.documentElement.scrollTop = 177;
+
+window.addEventListener("MozReftestInvalidate", function (e) {
+  document.documentElement.scrollTop = 30;
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/fractional-transform-2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Scrolling shouldn't invalidate the square.</title>
+
+<style>
+body {
+  margin: 0;
+  height: 5000px;
+}
+</style>
+
+<body>
+
+<svg width="768" height="1000">
+
+  <g transform="translate(0 112.152992)">
+    <rect x="100" y="650" height="50" width="50" fill="grey" class="reftest-no-paint"/>
+  </g>
+
+</svg>
+
+<script>
+
+document.documentElement.scrollTop = 709;
+
+window.addEventListener("MozReftestInvalidate", function (e) {
+  document.documentElement.scrollTop = 617;
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/fractional-transform-3.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Scrolling shouldn't invalidate the square.</title>
+
+<style>
+body {
+  margin: 0;
+  height: 5000px;
+}
+</style>
+
+<body>
+
+<svg width="768" height="1000">
+
+  <g transform="translate(0 0.999948799610138)">
+    <rect x="100" y="100" height="50" width="50" fill="grey" class="reftest-no-paint"/>
+  </g>
+
+</svg>
+
+<script>
+
+document.documentElement.scrollTop = 11;
+
+window.addEventListener("MozReftestInvalidate", function (e) {
+  document.documentElement.scrollTop = 51;
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -62,8 +62,11 @@ pref(layout.animated-image-layers.enable
 != layer-splitting-3.html about:blank
 != layer-splitting-4.html about:blank
 != layer-splitting-5.html about:blank
 != layer-splitting-6.html about:blank
 != layer-splitting-7.html about:blank
 fuzzy-if(gtkWidget,2,4) == image-scrolling-zoom-1.html image-scrolling-zoom-1-ref.html
 != image-scrolling-zoom-1-ref.html image-scrolling-zoom-1-notref.html
 != fast-scrolling.html about:blank
+!= fractional-transform-1.html about:blank
+!= fractional-transform-2.html about:blank
+!= fractional-transform-3.html about:blank
--- a/mfbt/FloatingPoint.h
+++ b/mfbt/FloatingPoint.h
@@ -396,16 +396,23 @@ FuzzyEqualsAdditive(T aValue1, T aValue2
  * magnitude those numbers are at.
  */
 template<typename T>
 static MOZ_ALWAYS_INLINE bool
 FuzzyEqualsMultiplicative(T aValue1, T aValue2,
                           T aEpsilon = detail::FuzzyEqualsEpsilon<T>::value())
 {
   static_assert(IsFloatingPoint<T>::value, "floating point type required");
+
+  // Short-circuit the common case in order to avoid the expensive operations
+  // below.
+  if (aValue1 == aValue2) {
+    return true;
+  }
+
   // can't use std::min because of bug 965340
   T smaller = Abs(aValue1) < Abs(aValue2) ? Abs(aValue1) : Abs(aValue2);
   return Abs(aValue1 - aValue2) <= aEpsilon * smaller;
 }
 
 /**
  * Returns true if the given value can be losslessly represented as an IEEE-754
  * single format number, false otherwise.  All NaN values are considered