Bug 1532375 - Implement Matrix4x4Double and QuaternionDouble r=lsalzman
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Tue, 19 Mar 2019 17:19:47 +0000
changeset 465076 ce4539262b1bc2357300080f866457b6fa274748
parent 465075 beaf8e80224d73c9aa5058c579073dd99f834357
child 465077 7981b7ae2fafca9d8fc79f12ecbda06b220e8627
push id80882
push userkgilbert@mozilla.com
push dateTue, 19 Mar 2019 17:26:39 +0000
treeherderautoland@ce4539262b1b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1532375, 1419190
milestone68.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 1532375 - Implement Matrix4x4Double and QuaternionDouble r=lsalzman The upcoming WebXR API (Bug 1419190) requires intermediate calculations of real-world space coordinates to have more precision with larger ranges. I expect that double precision matrix and quaternions will also be useful in other graphics and layout work. It would not be ideal to expand the existing classes to always use double precision, as it would incur a significant performance penalty on certain platforms (eg, Arm). The double-precision variants should be used only when required. The existing gfx::Matrix4x4 and gfx::Quaternion implementation can be extended with templates to generate both single and double precision variants. Differential Revision: https://phabricator.services.mozilla.com/D22010
gfx/2d/Matrix.h
gfx/2d/MatrixFwd.h
gfx/2d/Quaternion.cpp
gfx/2d/Quaternion.h
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -319,18 +319,18 @@ class BaseMatrix {
     return false;
   }
 
   /**
    * Returns true if the matrix is anything other than a straight
    * translation by integers.
    */
   bool HasNonIntegerTranslation() const {
-    return HasNonTranslation() || !FuzzyEqual(_31, floor(_31 + T(0.5))) ||
-           !FuzzyEqual(_32, floor(_32 + T(0.5)));
+    return HasNonTranslation() || !FuzzyEqual(_31, floor(_31 + 0.5f)) ||
+           !FuzzyEqual(_32, floor(_32 + 0.5f));
   }
 
   /**
    * Returns true if the matrix only has an integer translation.
    */
   bool HasOnlyIntegerTranslation() const { return !HasNonIntegerTranslation(); }
 
   /**
@@ -476,27 +476,27 @@ Point4DTyped<Units, F> ComputePerspectiv
   // Since we know what we want the w component to be, we can rearrange the
   // interpolation equation and solve for t.
   float t = -aFirst.w / (aSecond.w - aFirst.w);
 
   // Use t to find the remainder of the components
   return aFirst + (aSecond - aFirst) * t;
 }
 
-template <typename SourceUnits, typename TargetUnits>
+template <class SourceUnits, class TargetUnits, class T>
 class Matrix4x4Typed {
  public:
-  typedef PointTyped<SourceUnits> SourcePoint;
-  typedef PointTyped<TargetUnits> TargetPoint;
-  typedef Point3DTyped<SourceUnits> SourcePoint3D;
-  typedef Point3DTyped<TargetUnits> TargetPoint3D;
-  typedef Point4DTyped<SourceUnits> SourcePoint4D;
-  typedef Point4DTyped<TargetUnits> TargetPoint4D;
-  typedef RectTyped<SourceUnits> SourceRect;
-  typedef RectTyped<TargetUnits> TargetRect;
+  typedef PointTyped<SourceUnits, T> SourcePoint;
+  typedef PointTyped<TargetUnits, T> TargetPoint;
+  typedef Point3DTyped<SourceUnits, T> SourcePoint3D;
+  typedef Point3DTyped<TargetUnits, T> TargetPoint3D;
+  typedef Point4DTyped<SourceUnits, T> SourcePoint4D;
+  typedef Point4DTyped<TargetUnits, T> TargetPoint4D;
+  typedef RectTyped<SourceUnits, T> SourceRect;
+  typedef RectTyped<TargetUnits, T> TargetRect;
 
   Matrix4x4Typed()
       : _11(1.0f),
         _12(0.0f),
         _13(0.0f),
         _14(0.0f),
         _21(0.0f),
         _22(1.0f),
@@ -506,20 +506,18 @@ class Matrix4x4Typed {
         _32(0.0f),
         _33(1.0f),
         _34(0.0f),
         _41(0.0f),
         _42(0.0f),
         _43(0.0f),
         _44(1.0f) {}
 
-  Matrix4x4Typed(Float a11, Float a12, Float a13, Float a14, Float a21,
-                 Float a22, Float a23, Float a24, Float a31, Float a32,
-                 Float a33, Float a34, Float a41, Float a42, Float a43,
-                 Float a44)
+  Matrix4x4Typed(T a11, T a12, T a13, T a14, T a21, T a22, T a23, T a24, T a31,
+                 T a32, T a33, T a34, T a41, T a42, T a43, T a44)
       : _11(a11),
         _12(a12),
         _13(a13),
         _14(a14),
         _21(a21),
         _22(a22),
         _23(a23),
         _24(a24),
@@ -527,94 +525,96 @@ class Matrix4x4Typed {
         _32(a32),
         _33(a33),
         _34(a34),
         _41(a41),
         _42(a42),
         _43(a43),
         _44(a44) {}
 
-  explicit Matrix4x4Typed(const Float aArray[16]) {
+  explicit Matrix4x4Typed(const T aArray[16]) {
     memcpy(components, aArray, sizeof(components));
   }
 
   Matrix4x4Typed(const Matrix4x4Typed& aOther) {
-    memcpy(this, &aOther, sizeof(*this));
+    memcpy(components, aOther.components, sizeof(components));
   }
 
   union {
     struct {
-      Float _11, _12, _13, _14;
-      Float _21, _22, _23, _24;
-      Float _31, _32, _33, _34;
-      Float _41, _42, _43, _44;
+      T _11, _12, _13, _14;
+      T _21, _22, _23, _24;
+      T _31, _32, _33, _34;
+      T _41, _42, _43, _44;
     };
-    Float components[16];
+    T components[16];
   };
 
   friend std::ostream& operator<<(std::ostream& aStream,
                                   const Matrix4x4Typed& aMatrix) {
-    const Float* f = &aMatrix._11;
+    const T* f = &aMatrix._11;
     aStream << "[ " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;"
             << std::endl;
     f += 4;
     aStream << "  " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;"
             << std::endl;
     f += 4;
     aStream << "  " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ;"
             << std::endl;
     f += 4;
     aStream << "  " << f[0] << " " << f[1] << " " << f[2] << " " << f[3] << " ]"
             << std::endl;
     return aStream;
   }
 
-  Point4D& operator[](int aIndex) {
+  Point4DTyped<UnknownUnits, T>& operator[](int aIndex) {
     MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
-    return *reinterpret_cast<Point4D*>((&_11) + 4 * aIndex);
+    return *reinterpret_cast<Point4DTyped<UnknownUnits, T>*>((&_11) +
+                                                             4 * aIndex);
   }
-  const Point4D& operator[](int aIndex) const {
+  const Point4DTyped<UnknownUnits, T>& operator[](int aIndex) const {
     MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
-    return *reinterpret_cast<const Point4D*>((&_11) + 4 * aIndex);
+    return *reinterpret_cast<const Point4DTyped<UnknownUnits, T>*>((&_11) +
+                                                                   4 * aIndex);
   }
 
   /**
    * Returns true if the matrix is isomorphic to a 2D affine transformation.
    */
   bool 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 false;
     }
     return true;
   }
 
-  bool Is2D(Matrix* aMatrix) const {
+  bool Is2D(BaseMatrix<T>* aMatrix) const {
     if (!Is2D()) {
       return false;
     }
     if (aMatrix) {
       aMatrix->_11 = _11;
       aMatrix->_12 = _12;
       aMatrix->_21 = _21;
       aMatrix->_22 = _22;
       aMatrix->_31 = _41;
       aMatrix->_32 = _42;
     }
     return true;
   }
 
-  Matrix As2D() const {
+  BaseMatrix<T> As2D() const {
     MOZ_ASSERT(Is2D(), "Matrix is not a 2D affine transform");
 
-    return Matrix(_11, _12, _21, _22, _41, _42);
+    return BaseMatrix<T>(_11, _12, _21, _22, _41, _42);
   }
 
-  bool CanDraw2D(Matrix* aMatrix = nullptr) const {
+  bool CanDraw2D(BaseMatrix<T>* aMatrix = nullptr) const {
     if (_14 != 0.0f || _24 != 0.0f || _44 != 1.0f) {
       return false;
     }
     if (aMatrix) {
       aMatrix->_11 = _11;
       aMatrix->_12 = _12;
       aMatrix->_21 = _21;
       aMatrix->_22 = _22;
@@ -635,17 +635,17 @@ class Matrix4x4Typed {
     // Some matrices, such as those derived from perspective transforms,
     // can modify _44 from 1, while leaving the rest of the fourth column
     // (_14, _24) at 0. In this case, after resetting the third row and
     // third column above, the value of _44 functions only to scale the
     // coordinate transform divide by W. The matrix can be converted to
     // a true 2D matrix by normalizing out the scaling effect of _44 on
     // the remaining components ahead of time.
     if (_14 == 0.0f && _24 == 0.0f && _44 != 1.0f && _44 != 0.0f) {
-      Float scale = 1.0f / _44;
+      T scale = 1.0f / _44;
       _11 *= scale;
       _12 *= scale;
       _21 *= scale;
       _22 *= scale;
       _41 *= scale;
       _42 *= scale;
       _44 = 1.0f;
     }
@@ -882,17 +882,17 @@ class Matrix4x4Typed {
       }
     }
 
     return dstPointCount;
   }
 
   static const int kTransformAndClipRectMaxVerts = 32;
 
-  static Matrix4x4Typed From2D(const Matrix& aMatrix) {
+  static Matrix4x4Typed From2D(const BaseMatrix<T>& aMatrix) {
     Matrix4x4Typed matrix;
     matrix._11 = aMatrix._11;
     matrix._12 = aMatrix._12;
     matrix._21 = aMatrix._21;
     matrix._22 = aMatrix._22;
     matrix._41 = aMatrix._31;
     matrix._42 = aMatrix._32;
     return matrix;
@@ -978,17 +978,17 @@ class Matrix4x4Typed {
         max_y = quad[i].y;
       }
     }
 
     return RectTyped<TargetUnits, F>(min_x, min_y, max_x - min_x,
                                      max_y - min_y);
   }
 
-  static Matrix4x4Typed Translation(Float aX, Float aY, Float aZ) {
+  static Matrix4x4Typed Translation(T aX, T aY, T aZ) {
     return Matrix4x4Typed(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
                           0.0f, 1.0f, 0.0f, aX, aY, aZ, 1.0f);
   }
 
   static Matrix4x4Typed Translation(const TargetPoint3D& aP) {
     return Translation(aP.x, aP.y, aP.z);
   }
 
@@ -1010,42 +1010,42 @@ class Matrix4x4Typed {
    *   Matrix4x4::Translation(x, y) * this
    *
    * (Note that in performance critical code multiplying by the result of a
    * Translation()/Scaling() call is not recommended since that results in a
    * full matrix multiply involving 64 floating-point multiplications. Calling
    * this method would be preferred since it only involves 12 floating-point
    * multiplications.)
    */
-  Matrix4x4Typed& PreTranslate(Float aX, Float aY, Float aZ) {
+  Matrix4x4Typed& PreTranslate(T aX, T aY, T aZ) {
     _41 += aX * _11 + aY * _21 + aZ * _31;
     _42 += aX * _12 + aY * _22 + aZ * _32;
     _43 += aX * _13 + aY * _23 + aZ * _33;
     _44 += aX * _14 + aY * _24 + aZ * _34;
 
     return *this;
   }
 
-  Matrix4x4Typed& PreTranslate(const Point3D& aPoint) {
+  Matrix4x4Typed& PreTranslate(const Point3DTyped<UnknownUnits, T>& aPoint) {
     return PreTranslate(aPoint.x, aPoint.y, aPoint.z);
   }
 
   /**
    * Similar to PreTranslate, but the translation is applied -after- this
    * matrix's existing transformation instead of before it.
    *
    * This method is generally less used than PreTranslate since typically code
    * wants to adjust an existing user space to device space matrix to create a
    * transform to device space from a -new- user space (translated from the
    * previous user space). In that case consumers will need to use the Pre*
    * variants of the matrix methods rather than using the Post* methods, since
    * the Post* methods add a transform to the device space end of the
    * transformation.
    */
-  Matrix4x4Typed& PostTranslate(Float aX, Float aY, Float aZ) {
+  Matrix4x4Typed& PostTranslate(T aX, T aY, T aZ) {
     _11 += _14 * aX;
     _21 += _24 * aX;
     _31 += _34 * aX;
     _41 += _44 * aX;
     _12 += _14 * aY;
     _22 += _24 * aY;
     _32 += _34 * aY;
     _42 += _44 * aY;
@@ -1060,25 +1060,25 @@ class Matrix4x4Typed {
   Matrix4x4Typed& PostTranslate(const TargetPoint3D& aPoint) {
     return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
   }
 
   Matrix4x4Typed& PostTranslate(const TargetPoint& aPoint) {
     return PostTranslate(aPoint.x, aPoint.y, 0);
   }
 
-  static Matrix4x4Typed Scaling(Float aScaleX, Float aScaleY, float aScaleZ) {
+  static Matrix4x4Typed Scaling(T aScaleX, T aScaleY, T aScaleZ) {
     return Matrix4x4Typed(aScaleX, 0.0f, 0.0f, 0.0f, 0.0f, aScaleY, 0.0f, 0.0f,
                           0.0f, 0.0f, aScaleZ, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
   }
 
   /**
    * Similar to PreTranslate, but applies a scale instead of a translation.
    */
-  Matrix4x4Typed& PreScale(Float aX, Float aY, Float aZ) {
+  Matrix4x4Typed& PreScale(T aX, T aY, T aZ) {
     _11 *= aX;
     _12 *= aX;
     _13 *= aX;
     _14 *= aX;
     _21 *= aY;
     _22 *= aY;
     _23 *= aY;
     _24 *= aY;
@@ -1088,44 +1088,44 @@ class Matrix4x4Typed {
     _34 *= aZ;
 
     return *this;
   }
 
   /**
    * Similar to PostTranslate, but applies a scale instead of a translation.
    */
-  Matrix4x4Typed& PostScale(Float aScaleX, Float aScaleY, Float aScaleZ) {
+  Matrix4x4Typed& PostScale(T aScaleX, T aScaleY, T aScaleZ) {
     _11 *= aScaleX;
     _21 *= aScaleX;
     _31 *= aScaleX;
     _41 *= aScaleX;
     _12 *= aScaleY;
     _22 *= aScaleY;
     _32 *= aScaleY;
     _42 *= aScaleY;
     _13 *= aScaleZ;
     _23 *= aScaleZ;
     _33 *= aScaleZ;
     _43 *= aScaleZ;
 
     return *this;
   }
 
-  void SkewXY(Float aSkew) { (*this)[1] += (*this)[0] * aSkew; }
+  void SkewXY(T aSkew) { (*this)[1] += (*this)[0] * aSkew; }
 
-  void SkewXZ(Float aSkew) { (*this)[2] += (*this)[0] * aSkew; }
+  void SkewXZ(T aSkew) { (*this)[2] += (*this)[0] * aSkew; }
 
-  void SkewYZ(Float aSkew) { (*this)[2] += (*this)[1] * aSkew; }
+  void SkewYZ(T aSkew) { (*this)[2] += (*this)[1] * aSkew; }
 
-  Matrix4x4Typed& ChangeBasis(const Point3D& aOrigin) {
+  Matrix4x4Typed& ChangeBasis(const Point3DTyped<UnknownUnits, T>& aOrigin) {
     return ChangeBasis(aOrigin.x, aOrigin.y, aOrigin.z);
   }
 
-  Matrix4x4Typed& ChangeBasis(Float aX, Float aY, Float aZ) {
+  Matrix4x4Typed& ChangeBasis(T aX, T aY, T aZ) {
     // Translate to the origin before applying this matrix
     PreTranslate(-aX, -aY, -aZ);
 
     // Translate back into position after applying this matrix
     PostTranslate(aX, aY, aZ);
 
     return *this;
   }
@@ -1149,17 +1149,17 @@ class Matrix4x4Typed {
            _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;
   }
 
   bool operator!=(const Matrix4x4Typed& o) const { return !((*this) == o); }
 
   template <typename NewTargetUnits>
-  Matrix4x4Typed<SourceUnits, NewTargetUnits> operator*(
+  Matrix4x4Typed<SourceUnits, NewTargetUnits, T> operator*(
       const Matrix4x4Typed<TargetUnits, NewTargetUnits>& aMatrix) const {
     Matrix4x4Typed<SourceUnits, NewTargetUnits> 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 +
@@ -1190,55 +1190,55 @@ class Matrix4x4Typed {
                  _34 * aMatrix._44;
     matrix._44 = _41 * aMatrix._14 + _42 * aMatrix._24 + _43 * aMatrix._34 +
                  _44 * aMatrix._44;
 
     return matrix;
   }
 
   Matrix4x4Typed& operator*=(
-      const Matrix4x4Typed<TargetUnits, TargetUnits>& aMatrix) {
+      const Matrix4x4Typed<TargetUnits, TargetUnits, T>& aMatrix) {
     *this = *this * aMatrix;
     return *this;
   }
 
   /* Returns true if the matrix is an identity matrix.
    */
   bool IsIdentity() const {
     return _11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f &&
            _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f &&
            _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f &&
            _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f;
   }
 
   bool IsSingular() const { return Determinant() == 0.0; }
 
-  Float Determinant() const {
+  T Determinant() const {
     return _14 * _23 * _32 * _41 - _13 * _24 * _32 * _41 -
            _14 * _22 * _33 * _41 + _12 * _24 * _33 * _41 +
            _13 * _22 * _34 * _41 - _12 * _23 * _34 * _41 -
            _14 * _23 * _31 * _42 + _13 * _24 * _31 * _42 +
            _14 * _21 * _33 * _42 - _11 * _24 * _33 * _42 -
            _13 * _21 * _34 * _42 + _11 * _23 * _34 * _42 +
            _14 * _22 * _31 * _43 - _12 * _24 * _31 * _43 -
            _14 * _21 * _32 * _43 + _11 * _24 * _32 * _43 +
            _12 * _21 * _34 * _43 - _11 * _22 * _34 * _43 -
            _13 * _22 * _31 * _44 + _12 * _23 * _31 * _44 +
            _13 * _21 * _32 * _44 - _11 * _23 * _32 * _44 -
            _12 * _21 * _33 * _44 + _11 * _22 * _33 * _44;
   }
 
   // Invert() is not unit-correct. Prefer Inverse() where possible.
   bool Invert() {
-    Float det = Determinant();
+    T det = Determinant();
     if (!det) {
       return false;
     }
 
-    Matrix4x4Typed<SourceUnits, TargetUnits> result;
+    Matrix4x4Typed<SourceUnits, TargetUnits, T> result;
     result._11 = _23 * _34 * _42 - _24 * _33 * _42 + _24 * _32 * _43 -
                  _22 * _34 * _43 - _23 * _32 * _44 + _22 * _33 * _44;
     result._12 = _14 * _33 * _42 - _13 * _34 * _42 - _14 * _32 * _43 +
                  _12 * _34 * _43 + _13 * _32 * _44 - _12 * _33 * _44;
     result._13 = _13 * _24 * _42 - _14 * _23 * _42 + _14 * _22 * _43 -
                  _12 * _24 * _43 - _13 * _22 * _44 + _12 * _23 * _44;
     result._14 = _14 * _23 * _32 - _13 * _24 * _32 - _14 * _22 * _33 +
                  _12 * _24 * _33 + _13 * _22 * _34 - _12 * _23 * _34;
@@ -1283,27 +1283,27 @@ class Matrix4x4Typed {
     result._42 /= det;
     result._43 /= det;
     result._44 /= det;
     *this = result;
 
     return true;
   }
 
-  Matrix4x4Typed<TargetUnits, SourceUnits> Inverse() const {
-    typedef Matrix4x4Typed<TargetUnits, SourceUnits> InvertedMatrix;
+  Matrix4x4Typed<TargetUnits, SourceUnits, T> Inverse() const {
+    typedef Matrix4x4Typed<TargetUnits, SourceUnits, T> InvertedMatrix;
     InvertedMatrix clone = InvertedMatrix::FromUnknownMatrix(ToUnknownMatrix());
     DebugOnly<bool> inverted = clone.Invert();
     MOZ_ASSERT(inverted,
                "Attempted to get the inverse of a non-invertible matrix");
     return clone;
   }
 
-  Maybe<Matrix4x4Typed<TargetUnits, SourceUnits>> MaybeInverse() const {
-    typedef Matrix4x4Typed<TargetUnits, SourceUnits> InvertedMatrix;
+  Maybe<Matrix4x4Typed<TargetUnits, SourceUnits, T>> MaybeInverse() const {
+    typedef Matrix4x4Typed<TargetUnits, SourceUnits, T> InvertedMatrix;
     InvertedMatrix clone = InvertedMatrix::FromUnknownMatrix(ToUnknownMatrix());
     if (clone.Invert()) {
       return Some(clone);
     }
     return Nothing();
   }
 
   void Normalize() {
@@ -1341,19 +1341,19 @@ class Matrix4x4Typed {
            ::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;
+    T det = Determinant();
+    T __33 = _12 * _24 * _41 - _14 * _22 * _41 + _14 * _21 * _42 -
+             _11 * _24 * _42 - _12 * _21 * _44 + _11 * _22 * _44;
     return (__33 * det) < 0;
   }
 
   Matrix4x4Typed& NudgeToIntegersFixedEpsilon() {
     NudgeToInteger(&_11);
     NudgeToInteger(&_12);
     NudgeToInteger(&_13);
     NudgeToInteger(&_14);
@@ -1370,30 +1370,32 @@ class Matrix4x4Typed {
     NudgeToInteger(&_42, error);
     NudgeToInteger(&_43, error);
     NudgeToInteger(&_44, error);
     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));
+    return Point4DTyped<UnknownUnits, T>(*((&_11) + aIndex), *((&_21) + aIndex),
+                                         *((&_31) + aIndex),
+                                         *((&_41) + aIndex));
   }
 
-  void SetTransposedVector(int aIndex, Point4D& aVector) {
+  void SetTransposedVector(int aIndex, Point4DTyped<UnknownUnits, T>& aVector) {
     MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid matrix array index");
     *((&_11) + aIndex) = aVector.x;
     *((&_21) + aIndex) = aVector.y;
     *((&_31) + aIndex) = aVector.z;
     *((&_41) + aIndex) = aVector.w;
   }
 
-  bool Decompose(Point3D& translation, Quaternion& rotation,
-                 Point3D& scale) const {
+  bool Decompose(Point3DTyped<UnknownUnits, T>& translation,
+                 BaseQuaternion<T>& rotation,
+                 Point3DTyped<UnknownUnits, T>& scale) const {
     // Ensure matrix can be normalized
     if (gfx::FuzzyEqual(_44, 0.0f)) {
       return false;
     }
     Matrix4x4Typed mat = *this;
     mat.Normalize();
     if (HasPerspectiveComponent()) {
       // We do not support projection matrices
@@ -1416,19 +1418,19 @@ class Matrix4x4Typed {
     scale.z = sqrtf(_13 * _13 + _23 * _23 + _33 * _33);
 
     // Remove scale
     if (gfx::FuzzyEqual(scale.x, 0.0f) || gfx::FuzzyEqual(scale.y, 0.0f) ||
         gfx::FuzzyEqual(scale.z, 0.0f)) {
       // We do not support matrices with a zero scale component
       return false;
     }
-    Float invXS = 1.0f / scale.x;
-    Float invYS = 1.0f / scale.y;
-    Float invZS = 1.0f / scale.z;
+    T invXS = 1.0f / scale.x;
+    T invYS = 1.0f / scale.y;
+    T invZS = 1.0f / scale.z;
     mat._11 *= invXS;
     mat._21 *= invXS;
     mat._31 *= invXS;
     mat._12 *= invYS;
     mat._22 *= invYS;
     mat._32 *= invYS;
     mat._13 *= invZS;
     mat._23 *= invZS;
@@ -1437,21 +1439,21 @@ class Matrix4x4Typed {
     // Extract rotation
     rotation.SetFromRotationMatrix(mat);
     return true;
   }
 
   // Sets this matrix to a rotation matrix given by aQuat.
   // This quaternion *MUST* be normalized!
   // Implemented in Quaternion.cpp
-  void SetRotationFromQuaternion(const Quaternion& q) {
-    const Float x2 = q.x + q.x, y2 = q.y + q.y, z2 = q.z + q.z;
-    const Float xx = q.x * x2, xy = q.x * y2, xz = q.x * z2;
-    const Float yy = q.y * y2, yz = q.y * z2, zz = q.z * z2;
-    const Float wx = q.w * x2, wy = q.w * y2, wz = q.w * z2;
+  void SetRotationFromQuaternion(const BaseQuaternion<T>& q) {
+    const T x2 = q.x + q.x, y2 = q.y + q.y, z2 = q.z + q.z;
+    const T xx = q.x * x2, xy = q.x * y2, xz = q.x * z2;
+    const T yy = q.y * y2, yz = q.y * z2, zz = q.z * z2;
+    const T wx = q.w * x2, wy = q.w * y2, wz = q.w * z2;
 
     _11 = 1.0f - (yy + zz);
     _21 = xy + wz;
     _31 = xz - wy;
     _41 = 0.0f;
 
     _12 = xy - wz;
     _22 = 1.0f - (xx + zz);
@@ -1464,39 +1466,39 @@ class Matrix4x4Typed {
     _43 = 0.0f;
 
     _14 = _42 = _43 = 0.0f;
     _44 = 1.0f;
   }
 
   // Set all the members of the matrix to NaN
   void SetNAN() {
-    _11 = UnspecifiedNaN<Float>();
-    _21 = UnspecifiedNaN<Float>();
-    _31 = UnspecifiedNaN<Float>();
-    _41 = UnspecifiedNaN<Float>();
-    _12 = UnspecifiedNaN<Float>();
-    _22 = UnspecifiedNaN<Float>();
-    _32 = UnspecifiedNaN<Float>();
-    _42 = UnspecifiedNaN<Float>();
-    _13 = UnspecifiedNaN<Float>();
-    _23 = UnspecifiedNaN<Float>();
-    _33 = UnspecifiedNaN<Float>();
-    _43 = UnspecifiedNaN<Float>();
-    _14 = UnspecifiedNaN<Float>();
-    _24 = UnspecifiedNaN<Float>();
-    _34 = UnspecifiedNaN<Float>();
-    _44 = UnspecifiedNaN<Float>();
+    _11 = UnspecifiedNaN<T>();
+    _21 = UnspecifiedNaN<T>();
+    _31 = UnspecifiedNaN<T>();
+    _41 = UnspecifiedNaN<T>();
+    _12 = UnspecifiedNaN<T>();
+    _22 = UnspecifiedNaN<T>();
+    _32 = UnspecifiedNaN<T>();
+    _42 = UnspecifiedNaN<T>();
+    _13 = UnspecifiedNaN<T>();
+    _23 = UnspecifiedNaN<T>();
+    _33 = UnspecifiedNaN<T>();
+    _43 = UnspecifiedNaN<T>();
+    _14 = UnspecifiedNaN<T>();
+    _24 = UnspecifiedNaN<T>();
+    _34 = UnspecifiedNaN<T>();
+    _44 = UnspecifiedNaN<T>();
   }
 
   void SkewXY(double aXSkew, double aYSkew) {
     // XXX Is double precision really necessary here
-    float tanX = SafeTangent(aXSkew);
-    float tanY = SafeTangent(aYSkew);
-    float temp;
+    T tanX = SafeTangent(aXSkew);
+    T tanY = SafeTangent(aYSkew);
+    T temp;
 
     temp = _11;
     _11 += tanY * _21;
     _21 += tanX * temp;
 
     temp = _12;
     _12 += tanY * _22;
     _22 += tanX * temp;
@@ -1510,17 +1512,17 @@ class Matrix4x4Typed {
     _24 += tanX * temp;
   }
 
   void RotateX(double aTheta) {
     // XXX Is double precision really necessary here
     double cosTheta = FlushToZero(cos(aTheta));
     double sinTheta = FlushToZero(sin(aTheta));
 
-    float temp;
+    T temp;
 
     temp = _21;
     _21 = cosTheta * _21 + sinTheta * _31;
     _31 = -sinTheta * temp + cosTheta * _31;
 
     temp = _22;
     _22 = cosTheta * _22 + sinTheta * _32;
     _32 = -sinTheta * temp + cosTheta * _32;
@@ -1534,17 +1536,17 @@ class Matrix4x4Typed {
     _34 = -sinTheta * temp + cosTheta * _34;
   }
 
   void RotateY(double aTheta) {
     // XXX Is double precision really necessary here
     double cosTheta = FlushToZero(cos(aTheta));
     double sinTheta = FlushToZero(sin(aTheta));
 
-    float temp;
+    T temp;
 
     temp = _11;
     _11 = cosTheta * _11 + -sinTheta * _31;
     _31 = sinTheta * temp + cosTheta * _31;
 
     temp = _12;
     _12 = cosTheta * _12 + -sinTheta * _32;
     _32 = sinTheta * temp + cosTheta * _32;
@@ -1558,17 +1560,17 @@ class Matrix4x4Typed {
     _34 = sinTheta * temp + cosTheta * _34;
   }
 
   void RotateZ(double aTheta) {
     // XXX Is double precision really necessary here
     double cosTheta = FlushToZero(cos(aTheta));
     double sinTheta = FlushToZero(sin(aTheta));
 
-    float temp;
+    T temp;
 
     temp = _11;
     _11 = cosTheta * _11 + sinTheta * _21;
     _21 = -sinTheta * temp + cosTheta * _21;
 
     temp = _12;
     _12 = cosTheta * _12 + sinTheta * _22;
     _22 = -sinTheta * temp + cosTheta * _22;
@@ -1582,17 +1584,17 @@ class Matrix4x4Typed {
     _24 = -sinTheta * temp + cosTheta * _24;
   }
 
   // Sets this matrix to a rotation matrix about a
   // vector [x,y,z] by angle theta. The vector is normalized
   // to a unit vector.
   // https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
   void SetRotateAxisAngle(double aX, double aY, double aZ, double aTheta) {
-    Point3D vector(aX, aY, aZ);
+    Point3DTyped<UnknownUnits, T> vector(aX, aY, aZ);
     if (!vector.Length()) {
       return;
     }
     vector.RobustNormalize();
 
     double x = vector.x;
     double y = vector.y;
     double z = vector.z;
@@ -1618,34 +1620,37 @@ class Matrix4x4Typed {
     _33 = 1 - 2 * (x * x + y * y) * sq;
     _34 = 0.0f;
     _41 = 0.0f;
     _42 = 0.0f;
     _43 = 0.0f;
     _44 = 1.0f;
   }
 
-  void Perspective(float aDepth) {
+  void Perspective(T aDepth) {
     MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!");
     _31 += -1.0 / aDepth * _41;
     _32 += -1.0 / aDepth * _42;
     _33 += -1.0 / aDepth * _43;
     _34 += -1.0 / aDepth * _44;
   }
 
   Point3D GetNormalVector() const {
     // Define a plane in transformed space as the transformations
     // of 3 points on the z=0 screen plane.
-    Point3D a = TransformPoint(Point3D(0, 0, 0));
-    Point3D b = TransformPoint(Point3D(0, 1, 0));
-    Point3D c = TransformPoint(Point3D(1, 0, 0));
+    Point3DTyped<UnknownUnits, T> a =
+        TransformPoint(Point3DTyped<UnknownUnits, T>(0, 0, 0));
+    Point3DTyped<UnknownUnits, T> b =
+        TransformPoint(Point3DTyped<UnknownUnits, T>(0, 1, 0));
+    Point3DTyped<UnknownUnits, T> c =
+        TransformPoint(Point3DTyped<UnknownUnits, T>(1, 0, 0));
 
     // Convert to two vectors on the surface of the plane.
-    Point3D ab = b - a;
-    Point3D ac = c - a;
+    Point3DTyped<UnknownUnits, T> ab = b - a;
+    Point3DTyped<UnknownUnits, T> ac = c - a;
 
     return ac.CrossProduct(ab);
   }
 
   /**
    * Returns true if the matrix has any transform other
    * than a straight translation.
    */
@@ -1700,16 +1705,17 @@ class Matrix4x4Typed {
         aUnknown._11, aUnknown._12, aUnknown._13, aUnknown._14,
         aUnknown._21, aUnknown._22, aUnknown._23, aUnknown._24,
         aUnknown._31, aUnknown._32, aUnknown._33, aUnknown._34,
         aUnknown._41, aUnknown._42, aUnknown._43, aUnknown._44};
   }
 };
 
 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
+typedef Matrix4x4Typed<UnknownUnits, UnknownUnits, double> Matrix4x4Double;
 
 class Matrix5x4 {
  public:
   Matrix5x4()
       : _11(1.0f),
         _12(0),
         _13(0),
         _14(0),
--- a/gfx/2d/MatrixFwd.h
+++ b/gfx/2d/MatrixFwd.h
@@ -19,20 +19,21 @@ class BaseMatrix;
 typedef float Float;
 typedef BaseMatrix<Float> Matrix;
 
 typedef double Double;
 typedef BaseMatrix<Double> MatrixDouble;
 
 struct UnknownUnits;
 
-template <class SourceUnits, class TargetUnits>
+template <class SourceUnits, class TargetUnits, class T = Float>
 class Matrix4x4Typed;
 template <class SourceUnits, class TargetUnits>
 class Matrix4x4TypedFlagged;
 
 typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
+typedef Matrix4x4Typed<UnknownUnits, UnknownUnits, double> Matrix4x4Double;
 typedef Matrix4x4TypedFlagged<UnknownUnits, UnknownUnits> Matrix4x4Flagged;
 
 }  // namespace gfx
 }  // namespace mozilla
 
 #endif
--- a/gfx/2d/Quaternion.cpp
+++ b/gfx/2d/Quaternion.cpp
@@ -16,41 +16,10 @@ using namespace std;
 namespace mozilla {
 namespace gfx {
 
 std::ostream& operator<<(std::ostream& aStream, const Quaternion& aQuat) {
   return aStream << "< " << aQuat.x << " " << aQuat.y << " " << aQuat.z << " "
                  << aQuat.w << ">";
 }
 
-void Quaternion::SetFromRotationMatrix(const Matrix4x4& m) {
-  // see
-  // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
-  const Float trace = m._11 + m._22 + m._33;
-  if (trace > 0.0) {
-    const Float s = 0.5f / sqrt(trace + 1.0f);
-    w = 0.25f / s;
-    x = (m._32 - m._23) * s;
-    y = (m._13 - m._31) * s;
-    z = (m._21 - m._12) * s;
-  } else if (m._11 > m._22 && m._11 > m._33) {
-    const Float s = 2.0f * sqrt(1.0f + m._11 - m._22 - m._33);
-    w = (m._32 - m._23) / s;
-    x = 0.25f * s;
-    y = (m._12 + m._21) / s;
-    z = (m._13 + m._31) / s;
-  } else if (m._22 > m._33) {
-    const Float s = 2.0 * sqrt(1.0f + m._22 - m._11 - m._33);
-    w = (m._13 - m._31) / s;
-    x = (m._12 + m._21) / s;
-    y = 0.25f * s;
-    z = (m._23 + m._32) / s;
-  } else {
-    const Float s = 2.0 * sqrt(1.0f + m._33 - m._11 - m._22);
-    w = (m._21 - m._12) / s;
-    x = (m._13 + m._31) / s;
-    y = (m._23 + m._32) / s;
-    z = 0.25f * s;
-  }
-}
-
 }  // namespace gfx
 }  // namespace mozilla
--- a/gfx/2d/Quaternion.h
+++ b/gfx/2d/Quaternion.h
@@ -13,90 +13,131 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/MatrixFwd.h"
 #include "mozilla/gfx/Point.h"
 
 namespace mozilla {
 namespace gfx {
 
-class Quaternion {
+template <class T>
+class BaseQuaternion {
  public:
-  Quaternion() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {}
+  BaseQuaternion() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {}
 
-  Quaternion(Float aX, Float aY, Float aZ, Float aW)
-      : x(aX), y(aY), z(aZ), w(aW) {}
+  BaseQuaternion(T aX, T aY, T aZ, T aW) : x(aX), y(aY), z(aZ), w(aW) {}
 
-  Quaternion(const Quaternion& aOther) { memcpy(this, &aOther, sizeof(*this)); }
-
-  Float x, y, z, w;
+  BaseQuaternion(const BaseQuaternion& aOther) {
+    x = aOther.x;
+    y = aOther.y;
+    z = aOther.y;
+    w = aOther.w;
+  }
 
+  T x, y, z, w;
+
+  template <class U>
   friend std::ostream& operator<<(std::ostream& aStream,
-                                  const Quaternion& aQuat);
+                                  const BaseQuaternion<U>& aQuat);
 
-  void Set(Float aX, Float aY, Float aZ, Float aW) {
+  void Set(T aX, T aY, T aZ, T aW) {
     x = aX;
     y = aY;
     z = aZ;
     w = aW;
   }
 
   // Assumes upper 3x3 of aMatrix is a pure rotation matrix (no scaling)
-  void SetFromRotationMatrix(const Matrix4x4& aMatrix);
+  void SetFromRotationMatrix(
+      const Matrix4x4Typed<UnknownUnits, UnknownUnits, T>& m) {
+    // see
+    // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+    const T trace = m._11 + m._22 + m._33;
+    if (trace > 0.0) {
+      const T s = 0.5f / sqrt(trace + 1.0f);
+      w = 0.25f / s;
+      x = (m._32 - m._23) * s;
+      y = (m._13 - m._31) * s;
+      z = (m._21 - m._12) * s;
+    } else if (m._11 > m._22 && m._11 > m._33) {
+      const T s = 2.0f * sqrt(1.0f + m._11 - m._22 - m._33);
+      w = (m._32 - m._23) / s;
+      x = 0.25f * s;
+      y = (m._12 + m._21) / s;
+      z = (m._13 + m._31) / s;
+    } else if (m._22 > m._33) {
+      const T s = 2.0 * sqrt(1.0f + m._22 - m._11 - m._33);
+      w = (m._13 - m._31) / s;
+      x = (m._12 + m._21) / s;
+      y = 0.25f * s;
+      z = (m._23 + m._32) / s;
+    } else {
+      const T s = 2.0 * sqrt(1.0f + m._33 - m._11 - m._22);
+      w = (m._21 - m._12) / s;
+      x = (m._13 + m._31) / s;
+      y = (m._23 + m._32) / s;
+      z = 0.25f * s;
+    }
+  }
 
   // result = this * aQuat
-  Quaternion operator*(const Quaternion& aQuat) const {
-    Quaternion o;
-    const Float bx = aQuat.x, by = aQuat.y, bz = aQuat.z, bw = aQuat.w;
+  BaseQuaternion operator*(const BaseQuaternion& aQuat) const {
+    BaseQuaternion o;
+    const T bx = aQuat.x, by = aQuat.y, bz = aQuat.z, bw = aQuat.w;
 
     o.x = x * bw + w * bx + y * bz - z * by;
     o.y = y * bw + w * by + z * bx - x * bz;
     o.z = z * bw + w * bz + x * by - y * bx;
     o.w = w * bw - x * bx - y * by - z * bz;
     return o;
   }
 
-  Quaternion& operator*=(const Quaternion& aQuat) {
+  BaseQuaternion& operator*=(const BaseQuaternion& aQuat) {
     *this = *this * aQuat;
     return *this;
   }
 
-  Float Length() const { return sqrt(x * x + y * y + z * z + w * w); }
+  T Length() const { return sqrt(x * x + y * y + z * z + w * w); }
 
-  Quaternion& Conjugate() {
+  BaseQuaternion& Conjugate() {
     x *= -1.f;
     y *= -1.f;
     z *= -1.f;
     return *this;
   }
 
-  Quaternion& Normalize() {
-    Float l = Length();
+  BaseQuaternion& Normalize() {
+    T l = Length();
     if (l) {
       l = 1.0f / l;
       x *= l;
       y *= l;
       z *= l;
       w *= l;
     } else {
       x = y = z = 0.f;
       w = 1.f;
     }
     return *this;
   }
 
-  Quaternion& Invert() { return Conjugate().Normalize(); }
+  BaseQuaternion& Invert() { return Conjugate().Normalize(); }
 
-  Point3D RotatePoint(const Point3D& aPoint) {
-    Float uvx = Float(2.0) * (y * aPoint.z - z * aPoint.y);
-    Float uvy = Float(2.0) * (z * aPoint.x - x * aPoint.z);
-    Float uvz = Float(2.0) * (x * aPoint.y - y * aPoint.x);
+  Point3DTyped<UnknownUnits, T> RotatePoint(
+      const Point3DTyped<UnknownUnits, T>& aPoint) {
+    T uvx = T(2.0) * (y * aPoint.z - z * aPoint.y);
+    T uvy = T(2.0) * (z * aPoint.x - x * aPoint.z);
+    T uvz = T(2.0) * (x * aPoint.y - y * aPoint.x);
 
-    return Point3D(aPoint.x + w * uvx + y * uvz - z * uvy,
-                   aPoint.y + w * uvy + z * uvx - x * uvz,
-                   aPoint.z + w * uvz + x * uvy - y * uvx);
+    return Point3DTyped<UnknownUnits, T>(
+        aPoint.x + w * uvx + y * uvz - z * uvy,
+        aPoint.y + w * uvy + z * uvx - x * uvz,
+        aPoint.z + w * uvz + x * uvy - y * uvx);
   }
 };
 
+typedef BaseQuaternion<Float> Quaternion;
+typedef BaseQuaternion<Double> QuaternionDouble;
+
 }  // namespace gfx
 }  // namespace mozilla
 
 #endif