Bug 1052063 - Reverse the order of transforms applied for layers during composition. r=botond

This commit is contained in:
Kartikaya Gupta 2014-08-19 21:17:09 -04:00
parent 9449a0378b
commit d0bdd89470
7 changed files with 90 additions and 127 deletions

View File

@ -1114,13 +1114,13 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc,
// childUntransform takes points from aApzc's parent APZC's layer coordinates
// to aApzc's layer coordinates (which are aApzc's children's ParentLayer coordinates).
// 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.
Matrix4x4 cssUntransform = aApzc->GetCSSTransform();
cssUntransform.Invert();
// It is OC.Inverse() * NC.Inverse() * MC.Inverse() * LA.Inverse() * LC.Inverse() at L
// and RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() at P.
Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransform();
asyncUntransform.Invert();
Matrix4x4 childUntransform = ancestorUntransform * cssUntransform * asyncUntransform;
Matrix4x4 cssUntransform = aApzc->GetCSSTransform();
cssUntransform.Invert();
Matrix4x4 childUntransform = ancestorUntransform * asyncUntransform * cssUntransform;
gfxPointH3D hitTestPointForChildLayers = To3DMatrix(childUntransform).ProjectPoint(aHitTestPoint);
APZCTM_LOG("Untransformed %f %f to layer coordinates %f %f for APZC %p\n",
aHitTestPoint.x, aHitTestPoint.y,
@ -1168,34 +1168,32 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc,
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 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)
L's nontransient async transform (hereafter referred to as transform matrix LN)
L's transient async transform (hereafter referred to as transform matrix LT)
M's CSS transform (hereafter referred to as transform matrix MC)
M's nontransient async transform (hereafter referred to as transform matrix MN)
M's transient async transform (hereafter referred to as transform matrix MT)
...
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)
R's nontransient async transform (hereafter referred to as transform matrix RN)
R's transient async transform (hereafter referred to as transform matrix RT)
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()
LA === LN * LT
LA.Inverse() === LT.Inverse() * LN.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()
RN.Inverse()
RC.Inverse()
...
MC.Inverse()
MN.Inverse()
MT.Inverse()
LC.Inverse()
LN.Inverse()
MN.Inverse()
MC.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
@ -1210,35 +1208,34 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc,
transform be represented by LD, MD, ... RD.
Therefore, given a user input in screen space, the following transforms need to be applied
(in order from top to bottom):
RC.Inverse()
RN.Inverse()
RT.Inverse()
RN.Inverse()
RC.Inverse()
...
MC.Inverse()
MN.Inverse()
MT.Inverse()
LC.Inverse()
LN.Inverse()
MN.Inverse()
MC.Inverse()
LT.Inverse()
LD
LN.Inverse()
LC.Inverse()
LC
MD
LD
MC
MD
...
RD
RC
RD
This sequence can be simplified and refactored to the following:
aTransformToApzcOut
LT.Inverse()
LA.Inverse()
LD
LC
MD
MC
MD
...
RD
RC
RD
Since aTransformToApzcOut is already one of the out-parameters, we set aTransformToGeckoOut
to the remaining transforms (LT.Inverse() * LD * ... * RC), so that the caller code can
to the remaining transforms (LA.Inverse() * LD * ... * RD), 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
@ -1271,19 +1268,11 @@ APZCTreeManager::GetInputTransforms(AsyncPanZoomController *aApzc, Matrix4x4& aT
// asyncUntransform is LA.Inverse()
Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransform();
asyncUntransform.Invert();
// nontransientAsyncTransform is LN
Matrix4x4 nontransientAsyncTransform = aApzc->GetNontransientAsyncTransform();
// transientAsyncUntransform is LT.Inverse()
Matrix4x4 transientAsyncUntransform = nontransientAsyncTransform * asyncUntransform;
// aTransformToApzcOut is initialized to OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
Matrix4x4 cssUntransform = aApzc->GetCSSTransform();
cssUntransform.Invert();
Matrix4x4 nontransientAsyncUntransform = nontransientAsyncTransform;
nontransientAsyncUntransform.Invert();
aTransformToApzcOut = ancestorUntransform * cssUntransform * nontransientAsyncUntransform;
// aTransformToGeckoOut is initialized to LT.Inverse() * LD * LC * MC * NC * OC
aTransformToGeckoOut = transientAsyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetCSSTransform() * aApzc->GetAncestorTransform();
// aTransformToApzcOut is initialized to OC.Inverse() * NC.Inverse() * MC.Inverse()
aTransformToApzcOut = ancestorUntransform;
// aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC
aTransformToGeckoOut = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
// ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
@ -1292,15 +1281,15 @@ APZCTreeManager::GetInputTransforms(AsyncPanZoomController *aApzc, Matrix4x4& aT
// asyncUntransform is updated to PA.Inverse() when parent == P
asyncUntransform = parent->GetCurrentAsyncTransform();
asyncUntransform.Invert();
// untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()
cssUntransform = parent->GetCSSTransform();
// untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse()
Matrix4x4 cssUntransform = parent->GetCSSTransform();
cssUntransform.Invert();
Matrix4x4 untransformSinceLastApzc = ancestorUntransform * cssUntransform * asyncUntransform;
Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform * cssUntransform;
// aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse()
// aTransformToApzcOut is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
aTransformToApzcOut = untransformSinceLastApzc * aTransformToApzcOut;
// aTransformToGeckoOut is LT.Inverse() * LD * LC * MC * NC * OC * PD * PC * QC * RC
aTransformToGeckoOut = aTransformToGeckoOut * parent->GetTransformToLastDispatchedPaint() * parent->GetCSSTransform() * parent->GetAncestorTransform();
// aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
aTransformToGeckoOut = aTransformToGeckoOut * parent->GetCSSTransform() * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
// The above values for aTransformToApzcOut and aTransformToGeckoOut when parent == P match
// the required output as explained in the comment above this method. Note that any missing

View File

@ -2215,14 +2215,9 @@ void AsyncPanZoomController::GetOverscrollTransform(ViewTransform* aTransform) c
float scale = 1 - (kZEffect * spaceProp);
// In a ViewTransform, the translation is applied before the scale. We want
// to apply our translation after our scale, so we compensate for that here.
translation.x /= scale;
translation.y /= scale;
// Finally, apply the transformations.
aTransform->mScale.scale *= scale;
aTransform->mTranslation += translation * mFrameMetrics.LayersPixelsPerCSSPixel();
aTransform->mTranslation += translation * mFrameMetrics.GetZoom();
}
bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
@ -2249,16 +2244,6 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
// additional transform that produces an overscroll effect.
if (aOutOverscrollTransform && IsOverscrolled()) {
GetOverscrollTransform(aOutOverscrollTransform);
// Since the caller will apply aOverscrollTransform after aNewTransform,
// aOverscrollTransform's translation will be not be scaled by
// aNewTransform's scale. Since the resulting transform is then
// multiplied by the CSS transform, which cancels out the non-transient
// part of aNewTransform->mScale, this results in an overscroll
// translation whose magnitude varies with the zoom. To avoid this,
// we adjust for that here.
aOutOverscrollTransform->mTranslation.x *= aOutTransform->mScale.scale;
aOutOverscrollTransform->mTranslation.y *= aOutTransform->mScale.scale;
}
LogRendertraceRect(GetGuid(), "viewport", "red",
@ -2336,13 +2321,13 @@ ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() {
}
}
LayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
* mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
ParentLayerToScreenScale scale = mFrameMetrics.GetZoom()
/ mLastContentPaintMetrics.mDevPixelsPerCSSPixel
/ mFrameMetrics.GetParentResolution();
ScreenPoint translation = (currentScrollOffset - lastPaintScrollOffset)
* mFrameMetrics.GetZoom();
return ViewTransform(-translation,
mFrameMetrics.GetZoom()
/ mLastContentPaintMetrics.mDevPixelsPerCSSPixel
/ mFrameMetrics.GetParentResolution());
return ViewTransform(scale, -translation);
}
Matrix4x4 AsyncPanZoomController::GetNontransientAsyncTransform() {
@ -2906,16 +2891,16 @@ void AsyncPanZoomController::ShareCompositorFrameMetrics() {
ParentLayerPoint AsyncPanZoomController::ToParentLayerCoords(const ScreenPoint& aPoint)
{
return TransformTo<ParentLayerPixel>(GetNontransientAsyncTransform() * GetCSSTransform(), aPoint);
// The parent layer pixel space and the screen space for a given layer are the
// same as of bug 1052063. FIXME: Unify these two coordinate systems.
return ParentLayerPoint(aPoint.x, aPoint.y);
}
void AsyncPanZoomController::UpdateTransformScale()
{
Matrix4x4 nontransientTransforms = GetNontransientAsyncTransform() * GetCSSTransform();
if (!FuzzyEqualsMultiplicative(nontransientTransforms._11, nontransientTransforms._22)) {
NS_WARNING("The x- and y-scales of the nontransient transforms should be equal");
}
mFrameMetrics.mTransformScale.scale = nontransientTransforms._11;
// The parent layer pixel space and the screen space for a given layer are the
// same as of bug 1052063. FIXME: Unify these two coordinate systems.
mFrameMetrics.mTransformScale.scale = 1;
}
}

View File

@ -68,12 +68,11 @@ GetTransformToAncestorsParentLayer(Layer* aStart, Layer* aAncestor)
gfx::Matrix4x4 transform;
Layer* ancestorParent = aAncestor->GetParent();
for (Layer* iter = aStart; iter != ancestorParent; iter = iter->GetParent()) {
transform = transform * iter->GetTransform();
// If the layer has a non-transient async transform then we need to apply it here
// because it will get applied by the APZ in the compositor as well
const FrameMetrics& metrics = iter->GetFrameMetrics();
transform = transform * gfx::Matrix4x4().Scale(metrics.mResolution.scale, metrics.mResolution.scale, 1.f);
transform = transform * iter->GetTransform();
}
return transform;
}

View File

@ -148,12 +148,12 @@ ComputeViewTransform(const FrameMetrics& aContentMetrics, const FrameMetrics& aC
// but with aContentMetrics used in place of mLastContentPaintMetrics, because they
// should be equivalent, modulo race conditions while transactions are inflight.
LayerPoint translation = (aCompositorMetrics.GetScrollOffset() - aContentMetrics.GetScrollOffset())
* aContentMetrics.LayersPixelsPerCSSPixel();
return ViewTransform(-translation,
aCompositorMetrics.GetZoom()
ParentLayerToScreenScale scale = aCompositorMetrics.GetZoom()
/ aContentMetrics.mDevPixelsPerCSSPixel
/ aCompositorMetrics.GetParentResolution());
/ aCompositorMetrics.GetParentResolution();
ScreenPoint translation = (aCompositorMetrics.GetScrollOffset() - aContentMetrics.GetScrollOffset())
* aCompositorMetrics.GetZoom();
return ViewTransform(scale, -translation);
}
bool
@ -1338,22 +1338,15 @@ GetCompositorSideCompositionBounds(Layer* aScrollAncestor,
1.f);
nonTransientAPZUntransform.Invert();
Matrix4x4 layerTransform = aScrollAncestor->GetTransform();
Matrix4x4 layerUntransform = layerTransform;
layerUntransform.Invert();
// First take off the last two "terms" of aTransformToCompBounds, which
// are the scroll ancestor's local transform and the APZ's nontransient async
// transform.
Matrix4x4 transform = aTransformToCompBounds * layerUntransform * nonTransientAPZUntransform;
// Next, apply the APZ's async transform (this includes the nontransient component
// as well).
transform = transform * Matrix4x4(aAPZTransform);
// Finally, put back the scroll ancestor's local transform.
transform = transform * layerTransform;
// Take off the last "term" of aTransformToCompBounds, which
// is the APZ's nontransient async transform. Replace it with
// the APZ's async transform (this includes the nontransient
// component as well).
Matrix4x4 transform = aTransformToCompBounds
* nonTransientAPZUntransform
* Matrix4x4(aAPZTransform);
transform.Invert();
return TransformTo<LayerPixel>(transform,
aScrollAncestor->GetFrameMetrics().mCompositionBounds);
}

View File

@ -523,7 +523,7 @@ AdjustAndCombineWithCSSTransform(const Matrix4x4& asyncTransform, Layer* aLayer)
}
// Combine the async transform with the layer's CSS transform.
result = result * aLayer->GetTransform();
result = aLayer->GetTransform() * result;
return result;
}
@ -827,23 +827,20 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
// primary scrollable layer. We compare this to the user zoom and scroll
// offset in the view transform we obtained from Java in order to compute the
// transformation we need to apply.
LayerToScreenScale zoomAdjust = userZoom / geckoZoom;
LayerPoint geckoScroll(0, 0);
ScreenPoint geckoScroll(0, 0);
if (metrics.IsScrollable()) {
geckoScroll = metrics.GetScrollOffset() * geckoZoom;
geckoScroll = metrics.GetScrollOffset() * userZoom;
}
LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll;
Matrix4x4 treeTransform = ViewTransform(-translation,
userZoom
/ metrics.mDevPixelsPerCSSPixel
/ metrics.GetParentResolution());
ParentLayerToScreenScale scale = userZoom
/ metrics.mDevPixelsPerCSSPixel
/ metrics.GetParentResolution();
ScreenPoint translation = userScroll - geckoScroll;
Matrix4x4 treeTransform = ViewTransform(scale, -translation);
// The transform already takes the resolution scale into account. Since we
// will apply the resolution scale again when computing the effective
// transform, we must apply the inverse resolution scale here.
Matrix4x4 computedTransform = treeTransform * oldTransform;
Matrix4x4 computedTransform = oldTransform * treeTransform;
if (ContainerLayer* container = aLayer->AsContainerLayer()) {
computedTransform.Scale(1.0f/container->GetPreXScale(),
1.0f/container->GetPreYScale(),

View File

@ -6,7 +6,7 @@
#ifndef GFX_ASYNCCOMPOSITIONMANAGER_H
#define GFX_ASYNCCOMPOSITIONMANAGER_H
#include "Units.h" // for LayerPoint, etc
#include "Units.h" // for ScreenPoint, etc
#include "mozilla/layers/LayerManagerComposite.h" // for LayerManagerComposite
#include "mozilla/Attributes.h" // for MOZ_DELETE, MOZ_FINAL, etc
#include "mozilla/RefPtr.h" // for RefCounted
@ -28,17 +28,17 @@ class AutoResolveRefLayers;
// Represents (affine) transforms that are calculated from a content view.
struct ViewTransform {
ViewTransform(LayerPoint aTranslation = LayerPoint(),
ParentLayerToScreenScale aScale = ParentLayerToScreenScale())
: mTranslation(aTranslation)
, mScale(aScale)
ViewTransform(ParentLayerToScreenScale aScale = ParentLayerToScreenScale(),
ScreenPoint aTranslation = ScreenPoint())
: mScale(aScale)
, mTranslation(aTranslation)
{}
operator gfx::Matrix4x4() const
{
return
gfx::Matrix4x4().Translate(mTranslation.x, mTranslation.y, 0) *
gfx::Matrix4x4().Scale(mScale.scale, mScale.scale, 1);
gfx::Matrix4x4().Scale(mScale.scale, mScale.scale, 1)
.PostTranslate(mTranslation.x, mTranslation.y, 0);
}
// For convenience, to avoid writing the cumbersome
@ -55,8 +55,8 @@ struct ViewTransform {
return !(*this == rhs);
}
LayerPoint mTranslation;
ParentLayerToScreenScale mScale;
ScreenPoint mTranslation;
};
/**

View File

@ -721,39 +721,39 @@ TEST_F(APZCBasicTester, ComplexTransform) {
apzc->SetFrameMetrics(metrics);
apzc->NotifyLayersUpdated(metrics, true);
apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut);
EXPECT_EQ(ViewTransform(ParentLayerToScreenScale(2), ScreenPoint()), viewTransformOut);
EXPECT_EQ(ScreenPoint(60, 60), pointOut);
childApzc->SetFrameMetrics(childMetrics);
childApzc->NotifyLayersUpdated(childMetrics, true);
childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut);
EXPECT_EQ(ViewTransform(ParentLayerToScreenScale(2), ScreenPoint()), viewTransformOut);
EXPECT_EQ(ScreenPoint(60, 60), pointOut);
// do an async scroll by 5 pixels and check the transform
metrics.ScrollBy(CSSPoint(5, 0));
apzc->SetFrameMetrics(metrics);
apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut);
EXPECT_EQ(ViewTransform(ParentLayerToScreenScale(2), ScreenPoint(-30, 0)), viewTransformOut);
EXPECT_EQ(ScreenPoint(90, 60), pointOut);
childMetrics.ScrollBy(CSSPoint(5, 0));
childApzc->SetFrameMetrics(childMetrics);
childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut);
EXPECT_EQ(ViewTransform(ParentLayerToScreenScale(2), ScreenPoint(-30, 0)), viewTransformOut);
EXPECT_EQ(ScreenPoint(90, 60), pointOut);
// do an async zoom of 1.5x and check the transform
metrics.ZoomBy(1.5f);
apzc->SetFrameMetrics(metrics);
apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut);
EXPECT_EQ(ViewTransform(ParentLayerToScreenScale(3), ScreenPoint(-45, 0)), viewTransformOut);
EXPECT_EQ(ScreenPoint(135, 90), pointOut);
childMetrics.ZoomBy(1.5f);
childApzc->SetFrameMetrics(childMetrics);
childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut);
EXPECT_EQ(ViewTransform(ParentLayerToScreenScale(3), ScreenPoint(-45, 0)), viewTransformOut);
EXPECT_EQ(ScreenPoint(135, 90), pointOut);
}