Bug 681858 - Improve performance of gfx3DMatrix multiplication by reverting to 2d operations where possible. r=derf
authorMatt Woodrow <mwoodrow@mozilla.com>
Sat, 27 Aug 2011 12:07:05 +1200
changeset 75973 bbb80094e71ac267a31142e902f57568f63caa87
parent 75972 be7cefceceac32401cf37147142aefec214a5ee6
child 75981 6c8a909977d32284bbddd60a45e1780e824b5d7c
push id1522
push usermwoodrow@mozilla.com
push dateSat, 27 Aug 2011 00:08:57 +0000
treeherdermozilla-inbound@bbb80094e71a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersderf
bugs681858
milestone9.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 681858 - Improve performance of gfx3DMatrix multiplication by reverting to 2d operations where possible. r=derf
gfx/thebes/gfx3DMatrix.cpp
gfx/thebes/gfx3DMatrix.h
layout/base/nsLayoutUtils.cpp
--- a/gfx/thebes/gfx3DMatrix.cpp
+++ b/gfx/thebes/gfx3DMatrix.cpp
@@ -49,16 +49,20 @@ gfx3DMatrix::gfx3DMatrix(void)
   _21 = _23 = _24 = 0.0f;
   _31 = _32 = _34 = 0.0f;
   _41 = _42 = _43 = 0.0f;
 }
 
 gfx3DMatrix
 gfx3DMatrix::operator*(const gfx3DMatrix &aMatrix) const
 {
+  if (Is2D() && aMatrix.Is2D()) {
+    return Multiply2D(aMatrix);
+  }
+
   gfx3DMatrix matrix;
 
   matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21 + _13 * aMatrix._31 + _14 * aMatrix._41;
   matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21 + _23 * aMatrix._31 + _24 * aMatrix._41;
   matrix._31 = _31 * aMatrix._11 + _32 * aMatrix._21 + _33 * aMatrix._31 + _34 * aMatrix._41;
   matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + _43 * aMatrix._31 + _44 * aMatrix._41;
   matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22 + _13 * aMatrix._32 + _14 * aMatrix._42;
   matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22 + _23 * aMatrix._32 + _24 * aMatrix._42;
@@ -77,16 +81,31 @@ gfx3DMatrix::operator*(const gfx3DMatrix
 }
 
 gfx3DMatrix&
 gfx3DMatrix::operator*=(const gfx3DMatrix &aMatrix)
 {
   return *this = *this * aMatrix;
 }
 
+gfx3DMatrix
+gfx3DMatrix::Multiply2D(const gfx3DMatrix &aMatrix) const
+{
+  gfx3DMatrix matrix;
+
+  matrix._11 = _11 * aMatrix._11 + _12 * aMatrix._21;
+  matrix._21 = _21 * aMatrix._11 + _22 * aMatrix._21;
+  matrix._41 = _41 * aMatrix._11 + _42 * aMatrix._21 + aMatrix._41;
+  matrix._12 = _11 * aMatrix._12 + _12 * aMatrix._22;
+  matrix._22 = _21 * aMatrix._12 + _22 * aMatrix._22;
+  matrix._42 = _41 * aMatrix._12 + _42 * aMatrix._22 + aMatrix._42;
+
+  return matrix;
+}
+
 bool
 gfx3DMatrix::operator==(const gfx3DMatrix& o) const
 {
   // XXX would be nice to memcmp here, but that breaks IEEE 754 semantics
   return _11 == o._11 && _12 == o._12 && _13 == o._13 && _14 == o._14 &&
          _21 == o._21 && _22 == o._22 && _23 == o._23 && _24 == o._24 &&
          _31 == o._31 && _32 == o._32 && _33 == o._33 && _34 == o._34 &&
          _41 == o._41 && _42 == o._42 && _43 == o._43 && _44 == o._44;
@@ -141,16 +160,33 @@ gfx3DMatrix::Translate(const gfxPoint3D&
 {
     _41 += aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31;
     _42 += aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32;
     _43 += aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33;
     _44 += aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34;
 }
 
 void
+gfx3DMatrix::TranslatePost(const gfxPoint3D& aPoint)
+{
+    _11 += _14 * aPoint.x;
+    _21 += _24 * aPoint.x;
+    _31 += _34 * aPoint.x;
+    _41 += _44 * aPoint.x;
+    _12 += _14 * aPoint.y;
+    _22 += _24 * aPoint.y;
+    _32 += _34 * aPoint.y;
+    _42 += _44 * aPoint.y;
+    _13 += _14 * aPoint.z;
+    _23 += _24 * aPoint.z;
+    _33 += _34 * aPoint.z;
+    _43 += _44 * aPoint.z;
+}
+
+void
 gfx3DMatrix::SkewXY(float aSkew)
 {
     (*this)[1] += (*this)[0] * aSkew;
 }
 
 void 
 gfx3DMatrix::SkewXZ(float aSkew)
 {
@@ -455,24 +491,33 @@ gfx3DMatrix::TransformBounds(const gfxRe
     min_y = min(points[i].y, min_y);
     max_y = max(points[i].y, max_y);
   }
 
   return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
 }
 
 PRBool
-gfx3DMatrix::Is2D(gfxMatrix* aMatrix) const
+gfx3DMatrix::Is2D() const
 {
   if (_13 != 0.0f || _14 != 0.0f ||
       _23 != 0.0f || _24 != 0.0f ||
       _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f ||
       _43 != 0.0f || _44 != 1.0f) {
     return PR_FALSE;
   }
+  return PR_TRUE;
+}
+
+PRBool
+gfx3DMatrix::Is2D(gfxMatrix* aMatrix) const
+{
+  if (!Is2D()) {
+    return PR_FALSE;
+  }
   if (aMatrix) {
     aMatrix->xx = _11;
     aMatrix->yx = _12;
     aMatrix->xy = _21;
     aMatrix->yy = _22;
     aMatrix->x0 = _41;
     aMatrix->y0 = _42;
   }
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -101,17 +101,18 @@ public:
    */
   static gfx3DMatrix From2D(const gfxMatrix &aMatrix);
 
   /**
    * Returns true if the matrix is isomorphic to a 2D affine transformation
    * (i.e. as obtained by From2D). If it is, optionally returns the 2D
    * matrix in aMatrix.
    */
-  PRBool Is2D(gfxMatrix* aMatrix = nsnull) const;
+  PRBool Is2D(gfxMatrix* aMatrix) const;
+  PRBool Is2D() const;
 
   /**
    * Returns true if the matrix can be reduced to a 2D affine transformation
    * (i.e. as obtained by From2D). If it is, optionally returns the 2D
    * matrix in aMatrix. This should only be used on matrices required for
    * rendering, not for intermediate calculations.
    *
    * Since drawing is to a 2d plane, any 3d transform without perspective
@@ -127,16 +128,23 @@ public:
 
   /**
    * Add a translation by aPoint to the matrix.
    * This is functionally equivalent to:
    * gfx3DMatrix::Translation(aPoint) * matrix
    */
   void Translate(const gfxPoint3D& aPoint);
 
+  /**
+   * Add a translation by aPoint after the matrix.
+   * This is functionally equivalent to:
+   * matrix *gfx3DMatrix::Translation(aPoint)
+   */
+  void TranslatePost(const gfxPoint3D& aPoint);
+
   void SkewXY(float aSkew);
   void SkewXZ(float aSkew);
   void SkewYZ(float aSkew);
 
   void Scale(float aX, float aY, float aZ);
 
   /**
    * Transforms a point according to this matrix.
@@ -226,16 +234,18 @@ public:
 
   gfxFloat Determinant() const;
 
 private:
 
   gfxFloat Determinant3x3() const;
   gfx3DMatrix Inverse3x3() const;
 
+  gfx3DMatrix Multiply2D(const gfx3DMatrix &aMatrix) const;
+
 public:
 
   /** Matrix elements */
   float _11, _12, _13, _14;
   float _21, _22, _23, _24;
   float _31, _32, _33, _34;
   float _41, _42, _43, _44;
 };
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1003,24 +1003,25 @@ nsLayoutUtils::GetPopupFrameForEventCoor
 #endif
   return nsnull;
 }
 
 gfx3DMatrix
 nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin,
                                  const gfx3DMatrix &aMatrix)
 {
-  /* These are translation matrices from world-to-origin of relative frame and
-   * vice-versa.
-   */
-  gfx3DMatrix worldToOrigin = gfx3DMatrix::Translation(-aOrigin);
-  gfx3DMatrix originToWorld = gfx3DMatrix::Translation(aOrigin);
-
-  /* Multiply all three to get the transform! */
-  return worldToOrigin * aMatrix * originToWorld;
+  gfx3DMatrix result = aMatrix;
+
+  /* Translate to the origin before aMatrix */
+  result.Translate(-aOrigin);
+
+  /* Translate back into position after aMatrix */
+  result.TranslatePost(aOrigin);
+
+  return result; 
 }
 
 /**
  * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX.
  *
  * @param aVal The value to constrain (in/out)
  */
 static void ConstrainToCoordValues(gfxFloat &aVal)