Bug 1148582 - Apply async transforms to (ancestor) mask layers correctly. r=botond
authorDavid Anderson <danderson@mozilla.com>
Tue, 30 Jun 2015 21:07:09 -0400
changeset 250839 6234e37d912a054e8c07259c4dca19ce4c062055
parent 250838 be49031960aa935f2f0d6fa594251c9e1fa12536
child 250840 aff9ea7ce4fe01d4737aa6c6c059567cdbc6ba06
push id61681
push usermstange@themasta.com
push dateWed, 01 Jul 2015 04:52:34 +0000
treeherdermozilla-inbound@4a28b2cb9562 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1148582
milestone42.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 1148582 - Apply async transforms to (ancestor) mask layers correctly. r=botond
gfx/layers/ImageLayers.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicContainerLayer.cpp
gfx/layers/basic/BasicPaintedLayer.h
gfx/layers/composite/AsyncCompositionManager.cpp
gfx/layers/composite/ImageLayerComposite.cpp
--- a/gfx/layers/ImageLayers.cpp
+++ b/gfx/layers/ImageLayers.cpp
@@ -52,13 +52,13 @@ void ImageLayer::ComputeEffectiveTransfo
 
     mEffectiveTransformForBuffer =
         SnapTransform(local, sourceRect, nullptr) *
         SnapTransformTranslation(aTransformToSurface, nullptr);
   } else {
     mEffectiveTransformForBuffer = mEffectiveTransform;
   }
 
-  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+  ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 }
 
 }
 }
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -852,31 +852,41 @@ Layer::GetEffectiveMixBlendMode()
 
 gfxContext::GraphicsOperator
 Layer::DeprecatedGetEffectiveMixBlendMode()
 {
   return ThebesOp(GetEffectiveMixBlendMode());
 }
 
 void
-Layer::ComputeEffectiveTransformForMaskLayer(const Matrix4x4& aTransformToSurface)
+Layer::ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface)
 {
-  if (mMaskLayer) {
-    mMaskLayer->mEffectiveTransform = aTransformToSurface;
+  if (GetMaskLayer()) {
+    ComputeEffectiveTransformForMaskLayer(GetMaskLayer(), aTransformToSurface);
+  }
+  for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
+    Layer* maskLayer = GetAncestorMaskLayerAt(i);
+    ComputeEffectiveTransformForMaskLayer(maskLayer, aTransformToSurface);
+  }
+}
+
+/* static */ void
+Layer::ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer, const gfx::Matrix4x4& aTransformToSurface)
+{
+  aMaskLayer->mEffectiveTransform = aTransformToSurface;
 
 #ifdef DEBUG
-    bool maskIs2D = mMaskLayer->GetTransform().CanDraw2D();
-    NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+  bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D();
+  NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
 #endif
-    // The mask layer can have an async transform applied to it in some
-    // situations, so be sure to use its GetLocalTransform() rather than
-    // its GetTransform().
-    mMaskLayer->mEffectiveTransform = mMaskLayer->GetLocalTransform() *
-      mMaskLayer->mEffectiveTransform;
-  }
+  // The mask layer can have an async transform applied to it in some
+  // situations, so be sure to use its GetLocalTransform() rather than
+  // its GetTransform().
+  aMaskLayer->mEffectiveTransform = aMaskLayer->GetLocalTransform() *
+    aMaskLayer->mEffectiveTransform;
 }
 
 RenderTargetRect
 Layer::TransformRectToRenderTarget(const LayerIntRect& aRect)
 {
   LayerRect rect(aRect);
   RenderTargetRect quad = RenderTargetRect::FromUnknown(
     GetEffectiveTransform().TransformBounds(
@@ -1231,19 +1241,19 @@ ContainerLayer::DefaultComputeEffectiveT
   mUseIntermediateSurface = useIntermediateSurface && !GetEffectiveVisibleRegion().IsEmpty();
   if (useIntermediateSurface) {
     ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual));
   } else {
     ComputeEffectiveTransformsForChildren(idealTransform);
   }
 
   if (idealTransform.CanDraw2D()) {
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   } else {
-    ComputeEffectiveTransformForMaskLayer(Matrix4x4());
+    ComputeEffectiveTransformForMaskLayers(Matrix4x4());
   }
 }
 
 void
 ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy)
 {
   if (!(GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT) ||
       !Manager()->AreComponentAlphaLayersEnabled()) {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1509,19 +1509,21 @@ public:
    * pixel grid (whatever aTransformToSurface is relative to).
    *
    * We promise that when this is called on a layer, all ancestor layers
    * have already had ComputeEffectiveTransforms called.
    */
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) = 0;
 
   /**
-   * computes the effective transform for a mask layer, if this layer has one
+   * Computes the effective transform for mask layers, if this layer has any.
    */
-  void ComputeEffectiveTransformForMaskLayer(const gfx::Matrix4x4& aTransformToSurface);
+  void ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface);
+  static void ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer,
+                                                    const gfx::Matrix4x4& aTransformToSurface);
 
   /**
    * Calculate the scissor rect required when rendering this layer.
    * Returns a rectangle relative to the intermediate surface belonging to the
    * nearest ancestor that has an intermediate surface, or relative to the root
    * viewport if no ancestor has an intermediate surface, corresponding to the
    * clip rect for this layer intersected with aCurrentScissorRect.
    */
@@ -1827,17 +1829,17 @@ public:
                  "Residual transform can only be a translation");
     if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) {
       mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation());
       NS_ASSERTION(-0.5 <= mResidualTranslation.x && mResidualTranslation.x < 0.5 &&
                    -0.5 <= mResidualTranslation.y && mResidualTranslation.y < 0.5,
                    "Residual translation out of range");
       mValidRegion.SetEmpty();
     }
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
 
   LayerManager::PaintedLayerCreationHint GetCreationHint() const { return mCreationHint; }
 
   bool UsedForReadback() { return mUsedForReadback; }
   void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; }
   /**
    * Returns the residual translation. Apply this translation when drawing
@@ -2138,17 +2140,17 @@ public:
   virtual const gfxRGBA& GetColor() { return mColor; }
 
   MOZ_LAYER_DECL_NAME("ColorLayer", TYPE_COLOR)
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
     mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
 
 protected:
   ColorLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData),
       mColor(0.0, 0.0, 0.0, 0.0)
   {}
 
@@ -2290,17 +2292,17 @@ public:
     // Snap our local transform first, and snap the inherited transform as well.
     // This makes our snapping equivalent to what would happen if our content
     // was drawn into a PaintedLayer (gfxContext would snap using the local
     // transform, then we'd snap again when compositing the PaintedLayer).
     mEffectiveTransform =
         SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height),
                       nullptr)*
         SnapTransformTranslation(aTransformToSurface, nullptr);
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
 
 protected:
   CanvasLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData)
     , mPreTransCallback(nullptr)
     , mPreTransCallbackData(nullptr)
     , mPostTransCallback(nullptr)
--- a/gfx/layers/basic/BasicContainerLayer.cpp
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -39,27 +39,27 @@ BasicContainerLayer::ComputeEffectiveTra
   // containers.
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
   idealTransform.ProjectTo2D();
 
   if (!idealTransform.CanDraw2D()) {
     mEffectiveTransform = idealTransform;
     ComputeEffectiveTransformsForChildren(Matrix4x4());
-    ComputeEffectiveTransformForMaskLayer(Matrix4x4());
+    ComputeEffectiveTransformForMaskLayers(Matrix4x4());
     mUseIntermediateSurface = true;
     return;
   }
 
   mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
   // We always pass the ideal matrix down to our children, so there is no
   // need to apply any compensation using the residual from SnapTransformTranslation.
   ComputeEffectiveTransformsForChildren(idealTransform);
 
-  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+  ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 
   Layer* child = GetFirstChild();
   bool hasSingleBlendingChild = false;
   if (!HasMultipleChildren() && child) {
     hasSingleBlendingChild = child->GetMixBlendMode() != CompositionOp::OP_OVER;
   }
 
   /* If we have a single childand it is not blending,, it can just inherit our opacity,
--- a/gfx/layers/basic/BasicPaintedLayer.h
+++ b/gfx/layers/basic/BasicPaintedLayer.h
@@ -81,17 +81,17 @@ public:
     if (!BasicManager()->IsRetained()) {
       // Don't do any snapping of our transform, since we're just going to
       // draw straight through without intermediate buffers.
       mEffectiveTransform = GetLocalTransform() * aTransformToSurface;
       if (gfxPoint(0,0) != mResidualTranslation) {
         mResidualTranslation = gfxPoint(0,0);
         mValidRegion.SetEmpty();
       }
-      ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+      ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
       return;
     }
     PaintedLayer::ComputeEffectiveTransforms(aTransformToSurface);
   }
 
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -609,16 +609,28 @@ AsyncCompositionManager::ApplyAsyncConte
   // 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.
   //
   // The final clip for the layer is the intersection of these clips.
   Maybe<ParentLayerIntRect> asyncClip = aLayer->GetClipRect();
 
+  // The transform of a mask layer is relative to the masked layer's parent
+  // layer. So whenever we apply an async transform to a layer, we need to
+  // apply that same transform to the layer's own mask layer.
+  // A layer can also have "ancestor" mask layers for any rounded clips from
+  // its ancestor scroll frames. A scroll frame mask layer only needs to be
+  // async transformed for async scrolls of this scroll frame's ancestor
+  // scroll frames, not for async scrolls of this scroll frame itself.
+  // In the loop below, we iterate over scroll frames from inside to outside.
+  // At each iteration, this array contains the layer's ancestor mask layers 
+  // of all scroll frames inside the current one.
+  nsTArray<Layer*> ancestorMaskLayers;
+
   for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) {
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i);
     if (!controller) {
       continue;
     }
 
     hasAsyncTransform = true;
 
@@ -671,32 +683,53 @@ AsyncCompositionManager::ApplyAsyncConte
       ParentLayerIntRect clip = metrics.ClipRect();
       if (asyncClip) {
         asyncClip = Some(clip.Intersect(*asyncClip));
       } else {
         asyncClip = Some(clip);
       }
     }
 
+    // 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);
+    }
+
+    // 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);
+    }
+
     combinedAsyncTransformWithoutOverscroll *= asyncTransformWithoutOverscroll;
     combinedAsyncTransform *= asyncTransform;
   }
 
   if (hasAsyncTransform) {
     if (asyncClip) {
       aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
     }
 
     // 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));
 
+    // Do the same for the layer's own mask layer, if it has one.
+    if (Layer* maskLayer = aLayer->GetMaskLayer()) {
+      SetShadowTransform(maskLayer,
+          maskLayer->GetLocalTransform() * combinedAsyncTransform);
+    }
+
     const FrameMetrics& bottom = LayerMetricsWrapper::BottommostScrollableMetrics(aLayer);
     MOZ_ASSERT(bottom.IsScrollable());  // must be true because hasAsyncTransform is true
 
     // 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
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -138,17 +138,17 @@ ImageLayerComposite::ComputeEffectiveTra
 
     mEffectiveTransformForBuffer =
         SnapTransform(local, sourceRect, nullptr) *
         SnapTransformTranslation(aTransformToSurface, nullptr);
   } else {
     mEffectiveTransformForBuffer = mEffectiveTransform;
   }
 
-  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+  ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 }
 
 CompositableHost*
 ImageLayerComposite::GetCompositableHost()
 {
   if (mImageHost && mImageHost->IsAttached()) {
     return mImageHost.get();
   }