Bug 902505 - Adjust the hit-test and input transformations in APZCTreeManager to be more correct. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 08 Nov 2013 13:56:31 -0500
changeset 168769 14366dd910b6041b8fe857d96888e94aeef0a3e9
parent 168768 57213b64023b7ad16014b296c5817d39a29c3907
child 168770 79b22871f81eff4c6abfc5f8e047e12c5f861a17
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs902505
milestone28.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 902505 - Adjust the hit-test and input transformations in APZCTreeManager to be more correct. r=botond
gfx/layers/composite/APZCTreeManager.cpp
gfx/layers/ipc/AsyncPanZoomController.cpp
gfx/layers/ipc/AsyncPanZoomController.h
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -633,37 +633,42 @@ APZCTreeManager::FindTargetAPZC(AsyncPan
 AsyncPanZoomController*
 APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, const gfxPoint& aHitTestPoint)
 {
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment on GetInputTransforms. This function will recurse with aApzc at L and P, and 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.
 
-  // ancestorUntransform takes points from aApzc's parent APZC's screen coordinates
+  // ancestorUntransform takes points from aApzc's parent APZC's layer coordinates
   // to aApzc's screen coordinates.
   // It is OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L,
   //   and RC.Inverse() * QC.Inverse()                at recursion level for P.
   gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
 
-  // Hit testing for this layer is performed in aApzc's screen coordinates.
-  gfxPoint hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
-  APZC_LOG("Untransformed %f %f to screen coordinates %f %f for hit-testing APZC %p\n",
+  // hitTestTransform takes points from aApzc's parent APZC's layer coordinates to
+  // the hit test space (which is aApzc's transient coordinates).
+  // It is OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse() at L,
+  //   and RC.Inverse() * QC.Inverse() * PC.Inverse() * PN.Inverse()                at P.
+  gfx3DMatrix hitTestTransform = ancestorUntransform
+                               * aApzc->GetCSSTransform().Inverse()
+                               * aApzc->GetNontransientAsyncTransform().Inverse();
+  gfxPoint hitTestPointForThisLayer = hitTestTransform.ProjectPoint(aHitTestPoint);
+  APZC_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
            aHitTestPoint.x, aHitTestPoint.y,
            hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
 
-  // myUntransform takes points from aApzc's screen coordinates
+  // childUntransform takes points from aApzc's parent APZC's layer coordinates
   // to aApzc's layer coordinates (which are aApzc's children's screen coordinates).
-  // It is LA.Inverse() * LC.Inverse() at L
-  //   and PA.Inverse() * PC.Inverse() at P.
-  gfx3DMatrix myUntransform = gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse()
-                            * aApzc->GetCSSTransform().Inverse();
-
-  // Hit testing for child layers is performed in aApzc's layer coordinates.
-  gfxPoint hitTestPointForChildLayers = myUntransform.ProjectPoint(hitTestPointForThisLayer);
+  // It is OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LA.Inverse() at L
+  //   and RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()                at P.
+  gfx3DMatrix childUntransform = ancestorUntransform
+                               * aApzc->GetCSSTransform().Inverse()
+                               * gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
+  gfxPoint hitTestPointForChildLayers = childUntransform.ProjectPoint(aHitTestPoint);
   APZC_LOG("Untransformed %f %f to layer coordinates %f %f for APZC %p\n",
            aHitTestPoint.x, aHitTestPoint.y,
            hitTestPointForChildLayers.x, hitTestPointForChildLayers.y, aApzc);
 
   // This walks the tree in depth-first, reverse order, so that it encounters
   // APZCs front-to-back on the screen.
   for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
     AsyncPanZoomController* match = GetAPZCAtPoint(child, hitTestPointForChildLayers);
@@ -682,100 +687,121 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPan
 /* This function sets the aTransformToApzcOut and aTransformToGeckoOut out-parameters
    to some useful transformations that input events may need applied. This is best
    illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
    is the layer that corresponds to the returned APZC instance, and layer R is the root
    of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
    When layer L is displayed to the screen by the compositor, the set of transforms that
    are applied to L are (in order from top to bottom):
 
-        L's CSS transform      (hereafter referred to as transform matrix LC)
-        L's async transform    (hereafter referred to as transform matrix LA)
-        M's CSS transform      (hereafter referred to as transform matrix MC)
-        M's async transform    (hereafter referred to as transform matrix MA)
+        L's transient async transform       (hereafter referred to as transform matrix LT)
+        L's nontransient async transform    (hereafter referred to as transform matrix LN)
+        L's CSS transform                   (hereafter referred to as transform matrix LC)
+        M's transient async transform       (hereafter referred to as transform matrix MT)
+        M's nontransient async transform    (hereafter referred to as transform matrix MN)
+        M's CSS transform                   (hereafter referred to as transform matrix MC)
         ...
-        R's CSS transform      (hereafter referred to as transform matrix RC)
-        R's async transform    (hereafter referred to as transform matrix RA)
+        R's transient async transform       (hereafter referred to as transform matrix RT)
+        R's nontransient async transform    (hereafter referred to as transform matrix RN)
+        R's CSS transform                   (hereafter referred to as transform matrix RC)
 
-   Therefore, if we want user input to modify L's async transform, we have to first convert
-   user input from screen space to the coordinate space of L's async transform. Doing this
-   involves applying the following transforms (in order from top to bottom):
-        RA.Inverse()
+   Also, for any layer, the async transform is the combination of its transient and non-transient
+   parts. That is, for any layer L:
+                  LA === LT * LN
+        LA.Inverse() === LN.Inverse() * LT.Inverse()
+
+   If we want user input to modify L's transient async transform, we have to first convert
+   user input from screen space to the coordinate space of L's transient async transform. Doing
+   this involves applying the following transforms (in order from top to bottom):
         RC.Inverse()
+        RN.Inverse()
+        RT.Inverse()
         ...
-        MA.Inverse()
         MC.Inverse()
+        MN.Inverse()
+        MT.Inverse()
+        LC.Inverse()
+        LN.Inverse()
    This combined transformation is returned in the aTransformToApzcOut out-parameter.
 
    Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
    out all of the async transforms that are involved in this chain. This is because async
    transforms are stored only in the compositor and gecko does not account for them when
    doing display-list-based hit-testing for event dispatching. Therefore, given a user input
    in screen space, the following transforms need to be applied (in order from top to bottom):
-        RA.Inverse()
         RC.Inverse()
+        RN.Inverse()
+        RT.Inverse()
         ...
-        MA.Inverse()
         MC.Inverse()
-        LA.Inverse()
+        MN.Inverse()
+        MT.Inverse()
         LC.Inverse()
+        LN.Inverse()
+        LT.Inverse()
         LC
         MC
         ...
         RC
    This sequence can be simplified and refactored to the following:
         aTransformToApzcOut
-        LA.Inverse()
+        LT.Inverse()
+        LC
         MC
         ...
         RC
    Since aTransformToApzcOut is already one of the out-parameters, we set aTransformToGeckoOut
-   to the remaining transforms (LA.Inverse() * MC * ... * RC), so that the caller code can
+   to the remaining transforms (LT.Inverse() * LC * ... * RC), so that the caller code can
    combine it with aTransformToApzcOut to get the final transform required in this case.
 
    Note that for many of these layers, there will be no AsyncPanZoomController attached, and
    so the async transform will be the identity transform. So, in the example above, if layers
-   L and P have APZC instances attached, MA, NA, OA, QA, and RA will be identity transforms.
-   Additionally, for space-saving purposes, each APZC instance stores its layers individual
+   L and P have APZC instances attached, MT, MN, NT, NN, OT, ON, QT, QN, RT and RN will be
+   identity transforms.
+   Additionally, for space-saving purposes, each APZC instance stores its layer's individual
    CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
    layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
-   The APZCs also obviously have LA and PA, so all of the above transformation combinations
+   The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
    required can be generated.
  */
 void
 APZCTreeManager::GetInputTransforms(AsyncPanZoomController *aApzc, gfx3DMatrix& aTransformToApzcOut,
                                     gfx3DMatrix& aTransformToGeckoOut)
 {
   // 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.
 
   // ancestorUntransform is OC.Inverse() * NC.Inverse() * MC.Inverse()
   gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
   // asyncUntransform is LA.Inverse()
   gfx3DMatrix asyncUntransform = gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
+  // nontransientAsyncTransform is LN
+  gfx3DMatrix nontransientAsyncTransform = aApzc->GetNontransientAsyncTransform();
+  // transientAsyncUntransform is LT.Inverse()
+  gfx3DMatrix transientAsyncUntransform = nontransientAsyncTransform * asyncUntransform;
 
-  // aTransformToApzcOut is initialized to OC.Inverse() * NC.Inverse() * MC.Inverse()
-  aTransformToApzcOut = ancestorUntransform;
-  // aTransformToGeckoOut is initialized to LA.Inverse() * MC * NC * OC
-  aTransformToGeckoOut = asyncUntransform * aApzc->GetAncestorTransform();
+  // aTransformToApzcOut is initialized to OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
+  aTransformToApzcOut = ancestorUntransform * aApzc->GetCSSTransform().Inverse() * nontransientAsyncTransform.Inverse();
+  // aTransformToGeckoOut is initialized to LT.Inverse() * LC * MC * NC * OC
+  aTransformToGeckoOut = transientAsyncUntransform * aApzc->GetCSSTransform() * aApzc->GetAncestorTransform();
 
   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
     asyncUntransform = gfx3DMatrix(parent->GetCurrentAsyncTransform()).Inverse();
-    // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse()
-    gfx3DMatrix untransformSinceLastApzc = ancestorUntransform * asyncUntransform * parent->GetCSSTransform().Inverse();
+    // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()
+    gfx3DMatrix untransformSinceLastApzc = ancestorUntransform * parent->GetCSSTransform().Inverse() * asyncUntransform;
 
-    // aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
+    // aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
     aTransformToApzcOut = untransformSinceLastApzc * aTransformToApzcOut;
-    // aTransformToGeckoOut is LA.Inverse() * MC * NC * OC * PC * QC * RC
+    // aTransformToGeckoOut is LT.Inverse() * LC * MC * NC * OC * PC * QC * RC
     aTransformToGeckoOut = aTransformToGeckoOut * parent->GetCSSTransform() * parent->GetAncestorTransform();
 
     // The above values for aTransformToApzcOut and aTransformToGeckoOut when parent == P match
     // the required output as explained in the comment above GetTargetAPZC. Note that any missing terms
     // are async transforms that are guaranteed to be identity transforms.
   }
 }
 
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -1257,16 +1257,23 @@ ViewTransform AsyncPanZoomController::Ge
                          * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
 
   return ViewTransform(-translation,
                        mFrameMetrics.mZoom
                      / mLastContentPaintMetrics.mDevPixelsPerCSSPixel
                      / mFrameMetrics.GetParentResolution());
 }
 
+gfx3DMatrix AsyncPanZoomController::GetNontransientAsyncTransform() {
+  ReentrantMonitorAutoEnter lock(mMonitor);
+  return gfx3DMatrix::ScalingMatrix(mLastContentPaintMetrics.mResolution.scale,
+                                    mLastContentPaintMetrics.mResolution.scale,
+                                    1.0f);
+}
+
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   mLastContentPaintMetrics = aLayerMetrics;
 
   bool isDefault = mFrameMetrics.IsDefault();
   mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -196,16 +196,23 @@ public:
    * Returns the incremental transformation corresponding to the async pan/zoom
    * in progress. That is, when this transform is multiplied with the layer's
    * existing transform, it will make the layer appear with the desired pan/zoom
    * amount.
    */
   ViewTransform GetCurrentAsyncTransform();
 
   /**
+   * Returns the part of the async transform that will remain once Gecko does a
+   * repaint at the desired metrics. That is, in the steady state:
+   * gfx3DMatrix(GetCurrentAsyncTransform()) === GetNontransientAsyncTransform()
+   */
+  gfx3DMatrix GetNontransientAsyncTransform();
+
+  /**
    * Recalculates the displayport. Ideally, this should paint an area bigger
    * than the composite-to dimensions so that when you scroll down, you don't
    * checkerboard immediately. This includes a bunch of logic, including
    * algorithms to bias painting in the direction of the velocity.
    */
   static const CSSRect CalculatePendingDisplayPort(
     const FrameMetrics& aFrameMetrics,
     const gfx::Point& aVelocity,