Bug 1236750 - Use strongly-typed matrices to represent layer transforms in APZ code. r=kats draft
authorBotond Ballo <botond@mozilla.com>
Mon, 04 Jan 2016 20:07:54 -0500
changeset 319431 a9d765830fb7196bdcdb6de4cdbbdafc02270755
parent 319430 a8b991a115b6374c7df774ae4bcbeca2cd4a645a
child 512601 0fa0c10cc29a99688a2156363ab117ad29b8e6e2
push id9040
push userbballo@mozilla.com
push dateWed, 06 Jan 2016 23:57:33 +0000
reviewerskats
bugs1236750
milestone45.0a1
Bug 1236750 - Use strongly-typed matrices to represent layer transforms in APZ code. r=kats
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/HitTestingTreeNode.cpp
gfx/layers/apz/src/HitTestingTreeNode.h
gfx/layers/client/TiledContentClient.cpp
gfx/layers/composite/AsyncCompositionManager.cpp
gfx/layers/composite/AsyncCompositionManager.h
gfx/layers/composite/ContainerLayerComposite.cpp
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -382,17 +382,19 @@ APZCTreeManager::PrepareNodeForLayer(con
   if (!(state && state->mController.get())) {
     needsApzc = false;
   }
 
   RefPtr<HitTestingTreeNode> node = nullptr;
   if (!needsApzc) {
     node = RecycleOrCreateNode(aState, nullptr, aLayersId);
     AttachNodeToTree(node, aParent, aNextSibling);
-    node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(),
+    node->SetHitTestData(
+        GetEventRegions(aLayer),
+        aLayer.GetTransformTyped(),
         aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(),
         GetEventRegionsOverride(aParent, aLayer));
     node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                            aLayer.GetScrollbarDirection(),
                            aLayer.GetScrollbarSize());
     return node;
   }
 
@@ -491,17 +493,20 @@ APZCTreeManager::PrepareNodeForLayer(con
         aState.mIsFirstPaint && (aLayersId == aState.mOriginatingLayersId));
 
     // Since this is the first time we are encountering an APZC with this guid,
     // the node holding it must be the primary holder. It may be newly-created
     // or not, depending on whether it went through the newApzc branch above.
     MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
 
     ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
-    node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion),
+    node->SetHitTestData(
+        GetEventRegions(aLayer),
+        aLayer.GetTransformTyped(),
+        Some(clipRegion),
         GetEventRegionsOverride(aParent, aLayer));
     apzc->SetAncestorTransform(aAncestorTransform);
 
     PrintAPZCInfo(aLayer, apzc);
 
     // Bind the APZC instance into the tree of APZCs
     AttachNodeToTree(node, aParent, aNextSibling);
 
@@ -549,17 +554,20 @@ APZCTreeManager::PrepareNodeForLayer(con
 
     // Even though different layers associated with a given APZC may be at
     // different levels in the layer tree (e.g. one being an uncle of another),
     // we require from Layout that the CSS transforms up to their common
     // ancestor be the same.
     MOZ_ASSERT(aAncestorTransform == apzc->GetAncestorTransform());
 
     ParentLayerIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
-    node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion),
+    node->SetHitTestData(
+        GetEventRegions(aLayer),
+        aLayer.GetTransformTyped(),
+        Some(clipRegion),
         GetEventRegionsOverride(aParent, aLayer));
   }
 
   node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                          aLayer.GetScrollbarDirection(),
                          aLayer.GetScrollbarSize());
   return node;
 }
@@ -1867,17 +1875,17 @@ APZCTreeManager::GetScreenToApzcTransfor
 
   // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
   result = ancestorUntransform;
 
   for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
     // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
     ancestorUntransform = parent->GetAncestorTransform().Inverse();
     // asyncUntransform is updated to PA.Inverse() when parent == P
-    Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll().Inverse();
+    Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix();
     // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
     Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
 
     // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
     result = untransformSinceLastApzc * result;
 
     // The above value for result when parent == P matches the required output
     // as explained in the comment above this method. Note that any missing
@@ -1899,17 +1907,17 @@ APZCTreeManager::GetApzcToGeckoTransform
 
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment above. This function is called with aApzc at L, and the loop
   // below performs one iteration, where parent is at P. The comments explain what values are stored
   // in the variables at these two levels. All the comments use standard matrix notation where the
   // leftmost matrix in a multiplication is applied first.
 
   // asyncUntransform is LA.Inverse()
-  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll().Inverse();
+  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix();
 
   // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
   result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
 
   for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
     // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
     result = result * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2901,20 +2901,20 @@ bool AsyncPanZoomController::UpdateAnima
       RequestContentRepaint();
     }
     UpdateSharedCompositorFrameMetrics();
     return true;
   }
   return false;
 }
 
-Matrix4x4 AsyncPanZoomController::GetOverscrollTransform() const {
+AsyncTransformComponentMatrix AsyncPanZoomController::GetOverscrollTransform() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
   if (!IsOverscrolled()) {
-    return Matrix4x4();
+    return AsyncTransformComponentMatrix();
   }
 
   // The overscroll effect is a uniform stretch along the overscrolled axis,
   // with the edge of the content where we have reached the end of the
   // scrollable area pinned into place.
 
   // The kStretchFactor parameter determines how much overscroll can stretch the
   // content.
@@ -2941,17 +2941,17 @@ Matrix4x4 AsyncPanZoomController::GetOve
   bool overscrolledAtBottom = mY.GetOverscroll() > 0;
   if (overscrolledAtBottom) {
     ParentLayerCoord overscrolledCompositionHeight = scaleY * compositionSize.height;
     ParentLayerCoord extraCompositionHeight = overscrolledCompositionHeight - compositionSize.height;
     translation.y = -extraCompositionHeight;
   }
 
   // Combine the transformations into a matrix.
-  return Matrix4x4::Scaling(scaleX, scaleY, 1)
+  return AsyncTransformComponentMatrix::Scaling(scaleX, scaleY, 1)
                     .PostTranslate(translation.x, translation.y, 0);
 }
 
 bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
 {
   APZThreadUtils::AssertOnCompositorThread();
 
   // Don't send any state-change notifications until the end of the function,
@@ -3035,18 +3035,19 @@ AsyncTransform AsyncPanZoomController::G
   ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
                                * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
 
   return AsyncTransform(
     LayerToParentLayerScale(mFrameMetrics.GetAsyncZoom().scale * mTestAsyncZoom.scale),
     -translation);
 }
 
-Matrix4x4 AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll() const {
-  return Matrix4x4(GetCurrentAsyncTransform()) * GetOverscrollTransform();
+AsyncTransformComponentMatrix AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll() const {
+  return AsyncTransformComponentMatrix(GetCurrentAsyncTransform())
+       * GetOverscrollTransform();
 }
 
 Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   LayerPoint scrollChange =
     (mLastContentPaintMetrics.GetScrollOffset() - mLastDispatchedPaintMetrics.GetScrollOffset())
     * mLastContentPaintMetrics.GetDevPixelsPerCSSPixel()
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -170,17 +170,17 @@ public:
    */
   void SampleContentTransformForFrame(AsyncTransform* aOutTransform,
                                       ParentLayerPoint& aScrollOffset);
 
   /**
    * Return a visual effect that reflects this apzc's
    * overscrolled state, if any.
    */
-  Matrix4x4 GetOverscrollTransform() const;
+  AsyncTransformComponentMatrix GetOverscrollTransform() const;
 
   /**
    * A shadow layer update has arrived. |aLayerMetrics| is the new FrameMetrics
    * for the container layer corresponding to this APZC.
    * |aIsFirstPaint| is a flag passed from the shadow
    * layers code indicating that the frame metrics being sent with this call are
    * the initial metrics and the initial paint of the frame has just happened.
    */
@@ -226,17 +226,17 @@ public:
    * amount.
    */
   AsyncTransform GetCurrentAsyncTransform() const;
 
   /**
    * Returns the same transform as GetCurrentAsyncTransform(), but includes
    * any transform due to axis over-scroll.
    */
-  Matrix4x4 GetCurrentAsyncTransformWithOverscroll() const;
+  AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll() const;
 
   /**
    * Returns the transform to take something from the coordinate space of the
    * last thing we know gecko painted, to the coordinate space of the last thing
    * we asked gecko to paint. In cases where that last request has not yet been
    * processed, this is needed to transform input events properly into a space
    * gecko will understand.
    */
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HitTestingTreeNode.h"
 
 #include "AsyncPanZoomController.h"                     // for AsyncPanZoomController
 #include "LayersLogging.h"                              // for Stringify
 #include "mozilla/gfx/Point.h"                          // for Point4D
+#include "mozilla/layers/APZUtils.h"                    // for CompleteAsyncTransform
 #include "mozilla/layers/APZThreadUtils.h"              // for AssertOnCompositorThread
 #include "mozilla/layers/AsyncCompositionManager.h"     // for ViewTransform::operator Matrix4x4()
 #include "nsPrintfCString.h"                            // for nsPrintfCString
 #include "UnitTransforms.h"                             // for ViewAs
 
 namespace mozilla {
 namespace layers {
 
@@ -201,17 +202,17 @@ HitTestingTreeNode::IsPrimaryHolder() co
 uint64_t
 HitTestingTreeNode::GetLayersId() const
 {
   return mLayersId;
 }
 
 void
 HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
-                                   const gfx::Matrix4x4& aTransform,
+                                   const CSSTransformMatrix& aTransform,
                                    const Maybe<ParentLayerIntRegion>& aClipRegion,
                                    const EventRegionsOverride& aOverride)
 {
   mEventRegions = aRegions;
   mTransform = aTransform;
   mClipRegion = aClipRegion;
   mOverride = aOverride;
 }
@@ -222,22 +223,22 @@ HitTestingTreeNode::IsOutsideClip(const 
   // test against clip rect in ParentLayer coordinate space
   return (mClipRegion.isSome() && !mClipRegion->Contains(aPoint.x, aPoint.y));
 }
 
 Maybe<LayerPoint>
 HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
 {
   // convert into Layer coordinate space
-  gfx::Matrix4x4 localTransform = mTransform;
-  if (mApzc) {
-    localTransform = localTransform * mApzc->GetCurrentAsyncTransformWithOverscroll();
-  }
-  return UntransformBy(
-      ViewAs<LayerToParentLayerMatrix4x4>(localTransform).Inverse(), aPoint);
+  LayerToParentLayerMatrix4x4 transform = mTransform *
+      CompleteAsyncTransform(
+        mApzc
+      ? mApzc->GetCurrentAsyncTransformWithOverscroll()
+      : AsyncTransformComponentMatrix());
+  return UntransformBy(transform.Inverse(), aPoint);
 }
 
 HitTestResult
 HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
 {
   // This should only ever get called if the point is inside the clip region
   // for this node.
   MOZ_ASSERT(!IsOutsideClip(aPoint));
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -80,17 +80,17 @@ public:
   AsyncPanZoomController* GetNearestContainingApzc() const;
   AsyncPanZoomController* GetNearestContainingApzcWithSameLayersId() const;
   bool IsPrimaryHolder() const;
   uint64_t GetLayersId() const;
 
   /* Hit test related methods */
 
   void SetHitTestData(const EventRegions& aRegions,
-                      const gfx::Matrix4x4& aTransform,
+                      const CSSTransformMatrix& aTransform,
                       const Maybe<ParentLayerIntRegion>& aClipRegion,
                       const EventRegionsOverride& aOverride);
   bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
 
   /* Scrollbar info */
 
   void SetScrollbarData(FrameMetrics::ViewID aScrollViewId, Layer::ScrollDirection aDir, int32_t aScrollSize);
   bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
@@ -130,17 +130,17 @@ private:
    * disabled, it will contain the visible region of L, which we use as an
    * approximation to the hit region for the purposes of obscuring other layers.
    * This value is in L's LayerPixels.
    */
   EventRegions mEventRegions;
 
   /* This is the transform from layer L. This does NOT include any async
    * transforms. */
-  gfx::Matrix4x4 mTransform;
+  CSSTransformMatrix mTransform;
 
   /* This is clip rect for L that we wish to use for hit-testing purposes. Note
    * that this may not be exactly the same as the clip rect on layer L because
    * of the touch-sensitive region provided by the GeckoContentController, or
    * because we may use the composition bounds of the layer if the clip is not
    * present. This value is in L's ParentLayerPixels. */
   Maybe<ParentLayerIntRegion> mClipRegion;
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1400,17 +1400,17 @@ ClientMultiTiledLayerBuffer::ValidateTil
  */
 static Maybe<LayerRect>
 GetCompositorSideCompositionBounds(const LayerMetricsWrapper& aScrollAncestor,
                                    const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
                                    const AsyncTransform& aAPZTransform,
                                    const LayerRect& aClip)
 {
   LayerToParentLayerMatrix4x4 transform = aTransformToCompBounds *
-      ViewAs<ParentLayerToParentLayerMatrix4x4>(aAPZTransform);
+      AsyncTransformComponentMatrix(aAPZTransform);
 
   return UntransformBy(transform.Inverse(),
     aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
 }
 
 bool
 ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
                                                             const nsIntRegion& aOldValidRegion,
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
 #include "mozilla/WidgetUtils.h"        // for ComputeTransformForRotation
 #include "mozilla/dom/KeyframeEffect.h" // for KeyframeEffectReadOnly
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for RoundedToInt, PointTyped
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, RectTyped
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
+#include "mozilla/layers/APZUtils.h"    // for CompleteAsyncTransform
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorParent.h" // for CompositorParent, etc
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels, etc
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsDeviceContext.h"            // for nsDeviceContext
 #include "nsDisplayList.h"              // for nsDisplayTransform, etc
 #include "nsMathUtils.h"                // for NS_round
@@ -216,41 +217,41 @@ TransformClipRect(Layer* aLayer,
  * Set the given transform as the shadow transform on the layer, assuming
  * that the given transform already has the pre- and post-scales applied.
  * That is, this function cancels out the pre- and post-scales from aTransform
  * before setting it as the shadow transform on the layer, so that when
  * the layer's effective transform is computed, the pre- and post-scales will
  * only be applied once.
  */
 static void
-SetShadowTransform(Layer* aLayer, Matrix4x4 aTransform)
+SetShadowTransform(Layer* aLayer, LayerToParentLayerMatrix4x4 aTransform)
 {
   if (ContainerLayer* c = aLayer->AsContainerLayer()) {
     aTransform.PreScale(1.0f / c->GetPreXScale(),
                         1.0f / c->GetPreYScale(),
                         1);
   }
   aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
                        1.0f / aLayer->GetPostYScale(),
                        1);
-  aLayer->AsLayerComposite()->SetShadowTransform(aTransform);
+  aLayer->AsLayerComposite()->SetShadowTransform(aTransform.ToUnknownMatrix());
 }
 
 static void
 TranslateShadowLayer(Layer* aLayer,
                      const gfxPoint& aTranslation,
                      bool aAdjustClipRect)
 {
   // This layer might also be a scrollable layer and have an async transform.
   // To make sure we don't clobber that, we start with the shadow transform.
   // (i.e. GetLocalTransform() instead of GetTransform()).
   // Note that the shadow transform is reset on every frame of composition so
   // we don't have to worry about the adjustments compounding over successive
   // frames.
-  Matrix4x4 layerTransform = aLayer->GetLocalTransform();
+  LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
 
   // Apply the translation to the layer transform.
   layerTransform.PostTranslate(aTranslation.x, aTranslation.y, 0);
 
   SetShadowTransform(aLayer, layerTransform);
   aLayer->AsLayerComposite()->SetShadowTransformSetByAnimation(false);
 
   if (aAdjustClipRect) {
@@ -378,18 +379,18 @@ AsyncTransformShouldBeUnapplied(Layer* a
   }
   return false;
 }
 
 void
 AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
                                                    Layer* aTransformedSubtreeRoot,
                                                    FrameMetrics::ViewID aTransformScrollId,
-                                                   const Matrix4x4& aPreviousTransformForRoot,
-                                                   const Matrix4x4& aCurrentTransformForRoot,
+                                                   const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+                                                   const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
                                                    const ScreenMargin& aFixedLayerMargins)
 {
   FrameMetrics::ViewID fixedTo;  // the scroll id of the scroll frame we are fixed/sticky to
   bool isRootOfFixedSubtree = aLayer->GetIsFixedPosition() &&
     !aLayer->GetParent()->GetIsFixedPosition();
   if (isRootOfFixedSubtree) {
     fixedTo = aLayer->GetFixedPositionScrollContainerId();
   }
@@ -421,18 +422,18 @@ AsyncCompositionManager::AlignFixedAndSt
 
   // Accumulate the transforms between this layer and the subtree root layer.
   Matrix4x4 ancestorTransform;
   AccumulateLayerTransforms(aLayer->GetParent(), aTransformedSubtreeRoot,
                             ancestorTransform);
 
   // Calculate the cumulative transforms between the subtree root with the
   // old transform and the current transform.
-  Matrix4x4 oldCumulativeTransform = ancestorTransform * aPreviousTransformForRoot;
-  Matrix4x4 newCumulativeTransform = ancestorTransform * aCurrentTransformForRoot;
+  Matrix4x4 oldCumulativeTransform = ancestorTransform * aPreviousTransformForRoot.ToUnknownMatrix();
+  Matrix4x4 newCumulativeTransform = ancestorTransform * aCurrentTransformForRoot.ToUnknownMatrix();
   if (newCumulativeTransform.IsSingular()) {
     return;
   }
 
   // Add in the layer's local transform, if it isn't already included in
   // |aPreviousTransformForRoot| and |aCurrentTransformForRoot| (this happens
   // when the fixed/sticky layer is itself the transformed subtree root).
   Matrix4x4 localTransform;
@@ -692,20 +693,20 @@ AsyncCompositionManager::RecordShadowTra
     if (transform.IsTranslation() && !shadowTransform.IsIdentity()) {
       Point translation = transform.GetTranslation();
       mLayerTransformRecorder.RecordTransform(aLayer, translation);
       return;
     }
   }
 }
 
-Matrix4x4
-AdjustForClip(const Matrix4x4& asyncTransform, Layer* aLayer)
+static AsyncTransformComponentMatrix
+AdjustForClip(const AsyncTransformComponentMatrix& asyncTransform, Layer* aLayer)
 {
-  Matrix4x4 result = asyncTransform;
+  AsyncTransformComponentMatrix result = asyncTransform;
 
   // Container layers start at the origin, but they are clipped to where they
   // actually have content on the screen. The tree transform is meant to apply
   // to the clipped area. If the tree transform includes a scale component,
   // then applying it to container as-is will produce incorrect results. To
   // avoid this, translate the layer so that the clip rect starts at the origin,
   // apply the tree transform, and translate back.
   if (const Maybe<ParentLayerIntRect>& shadowClipRect = aLayer->AsLayerComposite()->GetShadowClipRect()) {
@@ -796,19 +797,20 @@ AsyncCompositionManager::ApplyAsyncConte
   bool appliedTransform = false;
   for (Layer* child = aLayer->GetFirstChild();
       child; child = child->GetNextSibling()) {
     appliedTransform |=
       ApplyAsyncContentTransformToTree(child, aOutFoundRoot,
           clipDeferredFromChildren);
   }
 
-  Matrix4x4 oldTransform = aLayer->GetTransform();
+  LayerToParentLayerMatrix4x4 oldTransform = aLayer->GetTransformTyped() *
+      AsyncTransformMatrix();
 
-  Matrix4x4 combinedAsyncTransform;
+  AsyncTransformComponentMatrix combinedAsyncTransform;
   bool hasAsyncTransform = false;
   ScreenMargin fixedLayerMargins;
 
   // Each layer has multiple clips. Its local clip, which must move with async
   // transforms, and its scrollframe clips, which are the clips between each
   // scrollframe and its ancestor scrollframe. Scrollframe clips include the
   // composition bounds and any other clips induced by layout.
   //
@@ -834,19 +836,20 @@ AsyncCompositionManager::ApplyAsyncConte
     }
 
     hasAsyncTransform = true;
 
     AsyncTransform asyncTransformWithoutOverscroll;
     ParentLayerPoint scrollOffset;
     controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
                                                scrollOffset);
-    Matrix4x4 overscrollTransform = controller->GetOverscrollTransform();
-    Matrix4x4 asyncTransform =
-      Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform;
+    AsyncTransformComponentMatrix overscrollTransform = controller->GetOverscrollTransform();
+    AsyncTransformComponentMatrix asyncTransform =
+        AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll)
+      * overscrollTransform;
 
     if (!aLayer->IsScrollInfoLayer()) {
       controller->MarkAsyncTransformAppliedToContent();
     }
 
     const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
 
 #if defined(MOZ_ANDROID_APZ)
@@ -892,18 +895,17 @@ AsyncCompositionManager::ApplyAsyncConte
     mIsFirstPaint = false;
 #endif
 
     // Transform the current local clip by this APZC's async transform. If we're
     // using containerful scrolling, then the clip is not part of the scrolled
     // frame and should not be transformed.
     if (asyncClip && !metrics.UsesContainerScrolling()) {
       MOZ_ASSERT(asyncTransform.Is2D());
-      asyncClip = Some(TransformBy(
-          ViewAs<ParentLayerToParentLayerMatrix4x4>(asyncTransform), *asyncClip));
+      asyncClip = Some(TransformBy(asyncTransform, *asyncClip));
     }
 
     // Combine the local clip with the ancestor scrollframe clip. This is not
     // included in the async transform above, since the ancestor clip should not
     // move with this APZC.
     if (metrics.HasClipRect()) {
       ParentLayerIntRect clip = metrics.ClipRect();
       if (aLayer->GetParent() && aLayer->GetParent()->GetTransformIsPerspective()) {
@@ -918,17 +920,17 @@ AsyncCompositionManager::ApplyAsyncConte
       }
     }
 
     // Do the same for the ancestor mask layers: ancestorMaskLayers contains
     // the ancestor mask layers for scroll frames *inside* the current scroll
     // frame, so these are the ones we need to shift by our async transform.
     for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
       SetShadowTransform(ancestorMaskLayer,
-          ancestorMaskLayer->GetLocalTransform() * asyncTransform);
+          ancestorMaskLayer->GetLocalTransformTyped() * asyncTransform);
     }
 
     // Append the ancestor mask layer for this scroll frame to ancestorMaskLayers.
     if (metrics.GetMaskLayerIndex()) {
       size_t maskLayerIndex = metrics.GetMaskLayerIndex().value();
       Layer* ancestorMaskLayer = aLayer->GetAncestorMaskLayerAt(maskLayerIndex);
       ancestorMaskLayers.AppendElement(ancestorMaskLayer);
     }
@@ -937,18 +939,20 @@ AsyncCompositionManager::ApplyAsyncConte
 
     // For the purpose of aligning fixed and sticky layers, we disregard
     // the overscroll transform as well as any OMTA transform when computing the
     // 'aCurrentTransformForRoot' parameter. This ensures that the overscroll
     // and OMTA transforms are not unapplied, and therefore that the visual
     // effects apply to fixed and sticky layers. We do this by using
     // GetTransform() as the base transform rather than GetLocalTransform(),
     // which would include those factors.
-    Matrix4x4 transformWithoutOverscrollOrOmta = aLayer->GetTransform() *
-        AdjustForClip(asyncTransformWithoutOverscroll, aLayer);
+    LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
+        aLayer->GetTransformTyped()
+      * CompleteAsyncTransform(
+          AdjustForClip(asyncTransformWithoutOverscroll, aLayer));
 
     // Since fixed/sticky layers are relative to their nearest scrolling ancestor,
     // we use the ViewID from the bottommost scrollable metrics here.
     AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
                               transformWithoutOverscrollOrOmta, fixedLayerMargins);
   }
 
   if (hasAsyncTransform || clipDeferredFromChildren) {
@@ -957,22 +961,23 @@ AsyncCompositionManager::ApplyAsyncConte
   }
 
   if (hasAsyncTransform) {
     // Apply the APZ transform on top of GetLocalTransform() here (rather than
     // GetTransform()) in case the OMTA code in SampleAnimations already set a
     // shadow transform; in that case we want to apply ours on top of that one
     // rather than clobber it.
     SetShadowTransform(aLayer,
-        aLayer->GetLocalTransform() * AdjustForClip(combinedAsyncTransform, aLayer));
+        aLayer->GetLocalTransformTyped()
+      * AdjustForClip(combinedAsyncTransform, aLayer));
 
     // Do the same for the layer's own mask layer, if it has one.
     if (Layer* maskLayer = aLayer->GetMaskLayer()) {
       SetShadowTransform(maskLayer,
-          maskLayer->GetLocalTransform() * combinedAsyncTransform);
+          maskLayer->GetLocalTransformTyped() * combinedAsyncTransform);
     }
 
     appliedTransform = true;
   }
 
   ExpandRootClipRect(aLayer, fixedLayerMargins);
 
   if (aLayer->GetScrollbarDirection() != Layer::NONE) {
@@ -1007,23 +1012,23 @@ ApplyAsyncTransformToScrollbarForContent
   // disparity between scrollbars and visible content.
   if (aContent.IsScrollInfoLayer()) {
     return;
   }
 
   const FrameMetrics& metrics = aContent.Metrics();
   AsyncPanZoomController* apzc = aContent.GetApzc();
 
-  Matrix4x4 asyncTransform = apzc->GetCurrentAsyncTransform();
+  AsyncTransformComponentMatrix asyncTransform = apzc->GetCurrentAsyncTransform();
 
   // |asyncTransform| represents the amount by which we have scrolled and
   // zoomed since the last paint. Because the scrollbar was sized and positioned based
   // on the painted content, we need to adjust it based on asyncTransform so that
   // it reflects what the user is actually seeing now.
-  Matrix4x4 scrollbarTransform;
+  AsyncTransformComponentMatrix scrollbarTransform;
   if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
     const ParentLayerCoord asyncScrollY = asyncTransform._42;
     const float asyncZoomY = asyncTransform._22;
 
     // The scroll thumb needs to be scaled in the direction of scrolling by the
     // inverse of the async zoom. This is because zooming in decreases the
     // fraction of the whole srollable rect that is in view.
     const float yScale = 1.f / asyncZoomY;
@@ -1094,58 +1099,62 @@ ApplyAsyncTransformToScrollbarForContent
     if (metrics.IsRootContent()) {
       xTranslation *= metrics.GetPresShellResolution();
     }
 
     scrollbarTransform.PostScale(xScale, 1.f, 1.f);
     scrollbarTransform.PostTranslate(xTranslation, 0, 0);
   }
 
-  Matrix4x4 transform = aScrollbar->GetLocalTransform() * scrollbarTransform;
+  LayerToParentLayerMatrix4x4 transform =
+      aScrollbar->GetLocalTransformTyped() * scrollbarTransform;
 
-  Matrix4x4 compensation;
+  AsyncTransformComponentMatrix compensation;
   // If the scrollbar layer is for the root then the content's resolution
   // applies to the scrollbar as well. Since we don't actually want the scroll
   // thumb's size to vary with the zoom (other than its length reflecting the
   // fraction of the scrollable length that's in view, which is taken care of
   // above), we apply a transform to cancel out this resolution.
   if (metrics.IsRootContent()) {
     compensation =
-        Matrix4x4::Scaling(metrics.GetPresShellResolution(),
-                           metrics.GetPresShellResolution(),
-                           1.0f).Inverse();
+        AsyncTransformComponentMatrix::Scaling(
+            metrics.GetPresShellResolution(),
+            metrics.GetPresShellResolution(),
+            1.0f).Inverse();
   }
   // If the scrollbar layer is a child of the content it is a scrollbar for,
   // then we need to adjust for any async transform (including an overscroll
   // transform) on the content. This needs to be cancelled out because layout
   // positions and sizes the scrollbar on the assumption that there is no async
   // transform, and without this adjustment the scrollbar will end up in the
   // wrong place.
   //
   // Note that since the async transform is applied on top of the content's
   // regular transform, we need to make sure to unapply the async transform in
   // the same coordinate space. This requires applying the content transform
   // and then unapplying it after unapplying the async transform.
   if (aScrollbarIsDescendant) {
-    Matrix4x4 asyncUntransform = (asyncTransform * apzc->GetOverscrollTransform()).Inverse();
+    Matrix4x4 asyncUntransform = (asyncTransform * apzc->GetOverscrollTransform()).Inverse().ToUnknownMatrix();
     Matrix4x4 contentTransform = aContent.GetTransform();
     Matrix4x4 contentUntransform = contentTransform.Inverse();
 
-    Matrix4x4 asyncCompensation = contentTransform
-                                * asyncUntransform
-                                * contentUntransform;
+    AsyncTransformComponentMatrix asyncCompensation =
+        ViewAs<AsyncTransformComponentMatrix>(
+            contentTransform
+          * asyncUntransform
+          * contentUntransform);
 
     compensation = compensation * asyncCompensation;
 
     // We also need to make a corresponding change on the clip rect of all the
     // layers on the ancestor chain from the scrollbar layer up to but not
     // including the layer with the async transform. Otherwise the scrollbar
     // shifts but gets clipped and so appears to flicker.
     for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer(); ancestor = ancestor->GetParent()) {
-      TransformClipRect(ancestor, ViewAs<ParentLayerToParentLayerMatrix4x4>(asyncCompensation));
+      TransformClipRect(ancestor, asyncCompensation);
     }
   }
   transform = transform * compensation;
 
   SetShadowTransform(aScrollbar, transform);
 }
 
 static LayerMetricsWrapper
@@ -1224,17 +1233,18 @@ AsyncCompositionManager::TransformScroll
     // tree, and this function just gets called with the root layer. In that
     // case TopmostScrollableMetrics will return an empty FrameMetrics but we
     // still want to use the actual non-scrollable metrics from the layer.
     metrics = LayerMetricsWrapper::BottommostMetrics(aLayer);
   }
 
   // We must apply the resolution scale before a pan/zoom transform, so we call
   // GetTransform here.
-  Matrix4x4 oldTransform = aLayer->GetTransform();
+  LayerToParentLayerMatrix4x4 oldTransform = aLayer->GetTransformTyped() *
+      AsyncTransformMatrix();
 
   CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
 
   LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
 
   if (mIsFirstPaint) {
     mContentRect = metrics.GetScrollableRect();
     SetFirstPaintViewport(scrollOffsetLayerPixels,
@@ -1283,23 +1293,23 @@ AsyncCompositionManager::TransformScroll
   // transformation we need to apply.
   ParentLayerPoint geckoScroll(0, 0);
   if (metrics.IsScrollable()) {
     geckoScroll = metrics.GetScrollOffset() * userZoom;
   }
 
   LayerToParentLayerScale asyncZoom = userZoom / metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
   ParentLayerPoint translation = userRect.TopLeft() - geckoScroll;
-  Matrix4x4 treeTransform = AsyncTransform(asyncZoom, -translation);
+  AsyncTransformComponentMatrix treeTransform = AsyncTransform(asyncZoom, -translation);
 
   // Apply the tree transform on top of GetLocalTransform() here (rather than
   // GetTransform()) in case the OMTA code in SampleAnimations already set a
   // shadow transform; in that case we want to apply ours on top of that one
   // rather than clobber it.
-  SetShadowTransform(aLayer, aLayer->GetLocalTransform() * treeTransform);
+  SetShadowTransform(aLayer, aLayer->GetLocalTransformTyped() * treeTransform);
 
   // Make sure that overscroll and under-zoom are represented in the old
   // transform so that fixed position content moves and scales accordingly.
   // These calculations will effectively scale and offset fixed position layers
   // in screen space when the compensatory transform is performed in
   // AlignFixedAndStickyLayers.
   ParentLayerRect contentScreenRect = mContentRect * userZoom;
   Point3D overscrollTranslation;
@@ -1326,17 +1336,18 @@ AsyncCompositionManager::TransformScroll
     underZoomScale.height = (mContentRect.height * userZoom.scale) /
       metrics.GetCompositionBounds().height;
   }
   oldTransform.PreScale(underZoomScale.width, underZoomScale.height, 1);
 
   // Make sure fixed position layers don't move away from their anchor points
   // when we're asynchronously panning or zooming
   AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
-                            aLayer->GetLocalTransform(), fixedLayerMargins);
+                            aLayer->GetLocalTransformTyped(),
+                            fixedLayerMargins);
 
   ExpandRootClipRect(aLayer, fixedLayerMargins);
 }
 
 void
 AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
 {
   MOZ_ASSERT(CompositorParent::IsInCompositorThread());
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -31,27 +31,20 @@ class CompositorParent;
 // Represents async transforms consisting of a scale and a translation.
 struct AsyncTransform {
   explicit AsyncTransform(LayerToParentLayerScale aScale = LayerToParentLayerScale(),
                           ParentLayerPoint aTranslation = ParentLayerPoint())
     : mScale(aScale)
     , mTranslation(aTranslation)
   {}
 
-  operator gfx::Matrix4x4() const
+  operator AsyncTransformComponentMatrix() const
   {
-    return
-      gfx::Matrix4x4::Scaling(mScale.scale, mScale.scale, 1)
-                      .PostTranslate(mTranslation.x, mTranslation.y, 0);
-  }
-
-  // For convenience, to avoid writing the cumbersome
-  // "gfx::Matrix4x4(a) * gfx::Matrix4x4(b)".
-  friend gfx::Matrix4x4 operator*(const AsyncTransform& a, const AsyncTransform& b) {
-    return gfx::Matrix4x4(a) * gfx::Matrix4x4(b);
+    return AsyncTransformComponentMatrix::Scaling(mScale.scale, mScale.scale, 1)
+        .PostTranslate(mTranslation.x, mTranslation.y, 0);
   }
 
   bool operator==(const AsyncTransform& rhs) const {
     return mTranslation == rhs.mTranslation && mScale == rhs.mScale;
   }
 
   bool operator!=(const AsyncTransform& rhs) const {
     return !(*this == rhs);
@@ -175,18 +168,18 @@ private:
    * For sticky position layers, the translation is further intersected with
    * the layer's sticky scroll ranges.
    * This function will also adjust layers so that the given content document
    * fixed position margins will be respected during asynchronous panning and
    * zooming.
    */
   void AlignFixedAndStickyLayers(Layer* aLayer, Layer* aTransformedSubtreeRoot,
                                  FrameMetrics::ViewID aTransformScrollId,
-                                 const gfx::Matrix4x4& aPreviousTransformForRoot,
-                                 const gfx::Matrix4x4& aCurrentTransformForRoot,
+                                 const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
+                                 const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
                                  const ScreenMargin& aFixedLayerMargins);
 
   /**
    * DRAWING PHASE ONLY
    *
    * For reach RefLayer in our layer tree, look up its referent and connect it
    * to the layer tree, if found.
    * aHasRemoteContent - indicates if the layer tree contains a remote reflayer.
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -597,17 +597,17 @@ RenderLayers(ContainerT* aContainer,
         // Since the composition bounds are in the parent layer's coordinates,
         // use the parent's effective transform rather than the layer's own.
         ParentLayerRect compositionBounds = layer->GetFrameMetrics(i - 1).GetCompositionBounds();
         aManager->GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTAINER,
                                                    compositionBounds.ToUnknownRect(),
                                                    gfx::Rect(aClipRect.ToUnknownRect()),
                                                    asyncTransform * aContainer->GetEffectiveTransform());
         if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) {
-          asyncTransform = apzc->GetCurrentAsyncTransformWithOverscroll()
+          asyncTransform = apzc->GetCurrentAsyncTransformWithOverscroll().ToUnknownMatrix()
                          * asyncTransform;
         }
       }
     }
 
     if (gfxPrefs::APZMinimap()) {
       RenderMinimap(aContainer, aManager, aClipRect, layer);
     }
@@ -747,17 +747,17 @@ ContainerRender(ContainerT* aContainer,
   // enabled).
   if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) {
     // Since aContainer doesn't have any children we can just iterate from the top metrics
     // on it down to the bottom using GetFirstChild and not worry about walking onto another
     // underlying layer.
     for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
       if (AsyncPanZoomController* apzc = i.GetApzc()) {
         if (!apzc->GetAsyncTransformAppliedToContent()
-            && !Matrix4x4(apzc->GetCurrentAsyncTransform()).IsIdentity()) {
+            && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform()).IsIdentity()) {
           aManager->UnusedApzTransformWarning();
           break;
         }
       }
     }
   }
 }