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 75980 bbb80094e71ac267a31142e902f57568f63caa87
parent 75979 be7cefceceac32401cf37147142aefec214a5ee6
child 75981 6c8a909977d32284bbddd60a45e1780e824b5d7c
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersderf
bugs681858
milestone9.0a1
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)