From 0f2c4e94eabf06b009f6c1d5966793bb571c146b Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Thu, 5 Sep 2013 18:26:59 -0400 Subject: [PATCH] Bug 912806 - Store both cumulative and non-cumulative resolutions in FrameMetrics and use whichever is appropriate. r=kats --- dom/ipc/TabChild.cpp | 16 ++++++++--- gfx/ipc/GfxMessageUtils.h | 2 ++ gfx/layers/FrameMetrics.h | 28 ++++++++++++++++--- .../composite/AsyncCompositionManager.cpp | 11 +++++--- .../composite/AsyncCompositionManager.h | 4 +-- gfx/layers/ipc/AsyncPanZoomController.cpp | 12 ++++++-- .../gtest/TestAsyncPanZoomController.cpp | 15 +++++----- layout/base/nsDisplayList.cpp | 21 ++++++++++---- 8 files changed, 80 insertions(+), 29 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 46590e467b2c..d44489f8b366 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -427,8 +427,11 @@ TabChild::Observe(nsISupports *aSubject, mLastMetrics.mZoom = mLastMetrics.CalculateIntrinsicScale(); // We use ScreenToLayerScale(1) below in order to turn the // async zoom amount into the gecko zoom amount. - mLastMetrics.mResolution = + mLastMetrics.mCumulativeResolution = mLastMetrics.mZoom / mLastMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); + // This is the root layer, so the cumulative resolution is the same + // as the resolution. + mLastMetrics.mResolution = mLastMetrics.mCumulativeResolution / LayoutDeviceToParentLayerScale(1); mLastMetrics.mScrollOffset = CSSPoint(0, 0); utils->SetResolution(mLastMetrics.mResolution.scale, @@ -692,7 +695,10 @@ TabChild::HandlePossibleViewportChange() // new CSS viewport, so we know that there's no velocity, acceleration, and // we have no idea how long painting will take. metrics, gfx::Point(0.0f, 0.0f), gfx::Point(0.0f, 0.0f), 0.0); - metrics.mResolution = metrics.mZoom / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); + metrics.mCumulativeResolution = metrics.mZoom / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); + // This is the root layer, so the cumulative resolution is the same + // as the resolution. + metrics.mResolution = metrics.mCumulativeResolution / LayoutDeviceToParentLayerScale(1); utils->SetResolution(metrics.mResolution.scale, metrics.mResolution.scale); // Force a repaint with these metrics. This, among other things, sets the @@ -1616,8 +1622,10 @@ TabChild::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics) } // set the resolution - LayoutDeviceToLayerScale resolution = aFrameMetrics.mZoom - / aFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); + ParentLayerToLayerScale resolution = aFrameMetrics.mZoom + / aFrameMetrics.mDevPixelsPerCSSPixel + / aFrameMetrics.GetParentResolution() + * ScreenToLayerScale(1); utils->SetResolution(resolution.scale, resolution.scale); // and set the display port diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 66a536a5313a..70bfed3d7f8c 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -576,6 +576,7 @@ struct ParamTraits WriteParam(aMsg, aParam.mCompositionBounds); WriteParam(aMsg, aParam.mScrollId); WriteParam(aMsg, aParam.mResolution); + WriteParam(aMsg, aParam.mCumulativeResolution); WriteParam(aMsg, aParam.mZoom); WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel); WriteParam(aMsg, aParam.mMayHaveTouchListeners); @@ -592,6 +593,7 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->mCompositionBounds) && ReadParam(aMsg, aIter, &aResult->mScrollId) && ReadParam(aMsg, aIter, &aResult->mResolution) && + ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) && ReadParam(aMsg, aIter, &aResult->mZoom) && ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) && ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners) && diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 04d0ee653ca7..0f1d04248860 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -15,6 +15,16 @@ namespace mozilla { namespace layers { +// The layer coordinates of the parent layer. Like the layer coordinates +// of the current layer (LayerPixel) but doesn't include the current layer's +// resolution. +struct ParentLayerPixel {}; + +typedef gfx::ScaleFactor LayoutDeviceToParentLayerScale; +typedef gfx::ScaleFactor ParentLayerToLayerScale; + +typedef gfx::ScaleFactor ParentLayerToScreenScale; + /** * The viewport and displayport metrics for the painted frame at the * time of a layer-tree transaction. These metrics are especially @@ -39,6 +49,7 @@ public: , mScrollId(NULL_SCROLL_ID) , mScrollableRect(0, 0, 0, 0) , mResolution(1) + , mCumulativeResolution(1) , mZoom(1) , mDevPixelsPerCSSPixel(1) , mMayHaveTouchListeners(false) @@ -57,6 +68,7 @@ public: mScrollId == aOther.mScrollId && mScrollableRect.IsEqualEdges(aOther.mScrollableRect) && mResolution == aOther.mResolution && + mCumulativeResolution == aOther.mCumulativeResolution && mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel && mMayHaveTouchListeners == aOther.mMayHaveTouchListeners && mPresShellId == aOther.mPresShellId; @@ -86,7 +98,7 @@ public: CSSToLayerScale LayersPixelsPerCSSPixel() const { - return mResolution * mDevPixelsPerCSSPixel; + return mCumulativeResolution * mDevPixelsPerCSSPixel; } LayerPoint GetScrollOffsetInLayerPixels() const @@ -94,6 +106,11 @@ public: return mScrollOffset * LayersPixelsPerCSSPixel(); } + LayoutDeviceToParentLayerScale GetParentResolution() const + { + return mCumulativeResolution / mResolution; + } + /** * Return the scale factor needed to fit the viewport * into its composition bounds. @@ -211,15 +228,18 @@ public: // The following metrics are dimensionless. // - // The resolution, along both axes, that the current frame has been painted - // at. + // The resolution that the current frame has been painted at. // // Every time this frame is composited and the compositor samples its // transform, this metric is used to create a transform which is // post-multiplied into the parent's transform. Since this only happens when // we walk the layer tree, the resulting transform isn't stored here. Thus the // resolution of parent layers is opaque to this metric. - LayoutDeviceToLayerScale mResolution; + ParentLayerToLayerScale mResolution; + + // The cumulative resolution that the current frame has been painted at. + // This is the product of our mResolution and the mResolutions of our parent frames. + LayoutDeviceToLayerScale mCumulativeResolution; // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel, // but will be drawn to the screen at mZoom. In the steady state, the diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp index d78ed66b792c..ec8f5690a06f 100644 --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -497,7 +497,7 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram LayoutDeviceToLayerScale resolution(1.0 / rootTransform.GetXScale(), 1.0 / rootTransform.GetYScale()); #else - LayoutDeviceToLayerScale resolution = metrics.mResolution; + LayoutDeviceToLayerScale resolution = metrics.mCumulativeResolution; #endif oldTransform.Scale(resolution.scale, resolution.scale, 1); @@ -556,7 +556,7 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const LayoutDev // appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios, // however, we can assume there is no async zooming in progress and so the following statement // works fine. - CSSToScreenScale userZoom(metrics.mDevPixelsPerCSSPixel.scale * metrics.mResolution.scale); + CSSToScreenScale userZoom(metrics.mDevPixelsPerCSSPixel * metrics.mCumulativeResolution * LayerToScreenScale(1)); ScreenPoint userScroll = metrics.mScrollOffset * userZoom; SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated, userScroll, userZoom, fixedLayerMargins, @@ -580,7 +580,10 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const LayoutDev } LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll; - treeTransform = gfx3DMatrix(ViewTransform(-translation, userZoom / metrics.mDevPixelsPerCSSPixel)); + treeTransform = gfx3DMatrix(ViewTransform(-translation, + userZoom + / metrics.mDevPixelsPerCSSPixel + / metrics.GetParentResolution())); // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective @@ -680,7 +683,7 @@ AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame) 1.0 / rootTransform.GetYScale()); #else LayoutDeviceToLayerScale resolution = - scrollableLayers[i]->AsContainerLayer()->GetFrameMetrics().mResolution; + scrollableLayers[i]->AsContainerLayer()->GetFrameMetrics().mCumulativeResolution; #endif TransformScrollableLayer(scrollableLayers[i], resolution); } diff --git a/gfx/layers/composite/AsyncCompositionManager.h b/gfx/layers/composite/AsyncCompositionManager.h index bc5daad28727..7584839d59ef 100644 --- a/gfx/layers/composite/AsyncCompositionManager.h +++ b/gfx/layers/composite/AsyncCompositionManager.h @@ -30,7 +30,7 @@ class AutoResolveRefLayers; // Represents (affine) transforms that are calculated from a content view. struct ViewTransform { ViewTransform(LayerPoint aTranslation = LayerPoint(), - LayoutDeviceToScreenScale aScale = LayoutDeviceToScreenScale()) + ParentLayerToScreenScale aScale = ParentLayerToScreenScale()) : mTranslation(aTranslation) , mScale(aScale) {} @@ -51,7 +51,7 @@ struct ViewTransform { } LayerPoint mTranslation; - LayoutDeviceToScreenScale mScale; + ParentLayerToScreenScale mScale; }; /** diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index 4ca926ef10fa..a8703abdb0e8 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -963,7 +963,7 @@ void AsyncPanZoomController::RequestContentRepaint() { mFrameMetrics.mScrollOffset.x) < EPSILON && fabsf(mLastPaintRequestMetrics.mScrollOffset.y - mFrameMetrics.mScrollOffset.y) < EPSILON && - mFrameMetrics.mResolution == mLastPaintRequestMetrics.mResolution) { + mFrameMetrics.mCumulativeResolution == mLastPaintRequestMetrics.mCumulativeResolution) { return; } @@ -1110,8 +1110,11 @@ ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() { } LayerPoint translation = (mFrameMetrics.mScrollOffset - lastPaintScrollOffset) * mLastContentPaintMetrics.LayersPixelsPerCSSPixel(); + return ViewTransform(-translation, - mFrameMetrics.mZoom / mLastContentPaintMetrics.mDevPixelsPerCSSPixel); + mFrameMetrics.mZoom + / mLastContentPaintMetrics.mDevPixelsPerCSSPixel + / mFrameMetrics.GetParentResolution()); } void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) { @@ -1347,11 +1350,14 @@ void AsyncPanZoomController::TimeoutTouchListeners() { void AsyncPanZoomController::SetZoomAndResolution(const CSSToScreenScale& aZoom) { mMonitor.AssertCurrentThreadIn(); + LayoutDeviceToParentLayerScale parentResolution = mFrameMetrics.GetParentResolution(); mFrameMetrics.mZoom = aZoom; // We use ScreenToLayerScale(1) below in order to ask gecko to render // what's currently visible on the screen. This is effectively turning // the async zoom amount into the gecko zoom amount. - mFrameMetrics.mResolution = aZoom / mFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); + mFrameMetrics.mCumulativeResolution = aZoom / mFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); + // The parent resolution will not have changed. + mFrameMetrics.mResolution = mFrameMetrics.mCumulativeResolution / parentResolution; } void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom, diff --git a/gfx/tests/gtest/TestAsyncPanZoomController.cpp b/gfx/tests/gtest/TestAsyncPanZoomController.cpp index e1492260c3f8..c354a9632916 100644 --- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp @@ -177,7 +177,8 @@ TEST(AsyncPanZoomController, ComplexTransform) { metrics.mViewport = CSSRect(0, 0, 4, 4); metrics.mScrollOffset = CSSPoint(10, 10); metrics.mScrollableRect = CSSRect(0, 0, 50, 50); - metrics.mResolution = LayoutDeviceToLayerScale(2); + metrics.mCumulativeResolution = LayoutDeviceToLayerScale(2); + metrics.mResolution = ParentLayerToLayerScale(2); metrics.mZoom = CSSToScreenScale(6); metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(3); metrics.mScrollId = FrameMetrics::ROOT_SCROLL_ID; @@ -198,39 +199,39 @@ TEST(AsyncPanZoomController, ComplexTransform) { apzc->SetFrameMetrics(metrics); apzc->NotifyLayersUpdated(metrics, true); apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); - EXPECT_EQ(ViewTransform(LayerPoint(), LayoutDeviceToScreenScale(2)), viewTransformOut); + EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(60, 60), pointOut); childApzc->SetFrameMetrics(childMetrics); childApzc->NotifyLayersUpdated(childMetrics, true); childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); - EXPECT_EQ(ViewTransform(LayerPoint(), LayoutDeviceToScreenScale(2)), viewTransformOut); + EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(60, 60), pointOut); // do an async scroll by 5 pixels and check the transform metrics.mScrollOffset += CSSPoint(5, 0); apzc->SetFrameMetrics(metrics); apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); - EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(2)), viewTransformOut); + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(90, 60), pointOut); childMetrics.mScrollOffset += CSSPoint(5, 0); childApzc->SetFrameMetrics(childMetrics); childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); - EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(2)), viewTransformOut); + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(90, 60), pointOut); // do an async zoom of 1.5x and check the transform metrics.mZoom.scale *= 1.5f; apzc->SetFrameMetrics(metrics); apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); - EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(3)), viewTransformOut); + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut); EXPECT_EQ(ScreenPoint(135, 90), pointOut); childMetrics.mZoom.scale *= 1.5f; childApzc->SetFrameMetrics(childMetrics); childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); - EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(3)), viewTransformOut); + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut); EXPECT_EQ(ScreenPoint(135, 90), pointOut); } diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 48e763c227e9..e740804bb402 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -663,20 +663,31 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, nsIPresShell* presShell = presContext->GetPresShell(); if (metrics.mScrollId == FrameMetrics::ROOT_SCROLL_ID) { - metrics.mResolution = LayoutDeviceToLayerScale(presShell->GetXResolution(), - presShell->GetYResolution()); + metrics.mResolution = ParentLayerToLayerScale(presShell->GetXResolution(), + presShell->GetYResolution()); } else { // Only the root scrollable frame for a given presShell should pick up // the presShell's resolution. All the other subframes are 1.0. - metrics.mResolution = LayoutDeviceToLayerScale(1.0f); + metrics.mResolution = ParentLayerToLayerScale(1.0f); } + + metrics.mCumulativeResolution = LayoutDeviceToLayerScale(1.0f); + nsIPresShell* curPresShell = presShell; + while (curPresShell != nullptr) { + ParentLayerToLayerScale presShellResolution(curPresShell->GetXResolution(), + curPresShell->GetYResolution()); + metrics.mCumulativeResolution.scale *= presShellResolution.scale; + nsPresContext* parentContext = curPresShell->GetPresContext()->GetParentPresContext(); + curPresShell = parentContext ? parentContext->GetPresShell() : nullptr; + } + metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale( (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel); // Initially, AsyncPanZoomController should render the content to the screen // at the painted resolution. const LayerToScreenScale layerToScreenScale(1.0f); - metrics.mZoom = metrics.mResolution * metrics.mDevPixelsPerCSSPixel + metrics.mZoom = metrics.mCumulativeResolution * metrics.mDevPixelsPerCSSPixel * layerToScreenScale; metrics.mMayHaveTouchListeners = aMayHaveTouchListeners; @@ -689,7 +700,7 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame), frameForCompositionBoundsCalculation->GetSize()); metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel) - * metrics.mResolution + * metrics.mCumulativeResolution * layerToScreenScale); // Adjust composition bounds for the size of scroll bars.