Bug 1526941 - Part 5: Rewrite nsDisplayTransform bounds calculations r=mattwoodrow
authorMiko Mynttinen <mikokm@gmail.com>
Thu, 07 Mar 2019 17:13:45 +0000
changeset 520854 02fe822afa8c6252970f779dcbfd4a7b57fefd13
parent 520853 c5a66f2c239f6bcadf8ec01954c31efcafa6b3c2
child 520855 6490553fc843801070935e47e715e64ff0071615
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1526941
milestone67.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 1526941 - Part 5: Rewrite nsDisplayTransform bounds calculations r=mattwoodrow Differential Revision: https://phabricator.services.mozilla.com/D22333
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7325,17 +7325,16 @@ nsDisplayTransform::nsDisplayTransform(n
       mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
       mChildrenBuildingRect(aChildrenBuildingRect),
       mIndex(aIndex),
       mIsTransformSeparator(true),
       mAllowAsyncAnimation(false) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   Init(aBuilder, aList);
-  UpdateBoundsFor3D(aBuilder);
 }
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame* aFrame, nsDisplayList* aList,
                                        const nsRect& aChildrenBuildingRect,
                                        uint32_t aIndex,
                                        bool aAllowAsyncAnimation)
     : nsDisplayHitTestInfoItem(aBuilder, aFrame),
@@ -7345,17 +7344,16 @@ nsDisplayTransform::nsDisplayTransform(n
       mChildrenBuildingRect(aChildrenBuildingRect),
       mIndex(aIndex),
       mIsTransformSeparator(false),
       mAllowAsyncAnimation(aAllowAsyncAnimation) {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   SetReferenceFrameToAncestor(aBuilder);
   Init(aBuilder, aList);
-  UpdateBoundsFor3D(aBuilder);
 }
 
 nsDisplayTransform::nsDisplayTransform(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
     const nsRect& aChildrenBuildingRect, uint32_t aIndex,
     ComputeTransformFunction aTransformGetter)
     : nsDisplayHitTestInfoItem(aBuilder, aFrame),
       mTransformGetter(aTransformGetter),
@@ -7404,19 +7402,18 @@ void nsDisplayTransform::SetReferenceFra
   }
 
   SetBuildingRect(aBuilder->GetVisibleRect() + mToReferenceFrame);
 }
 
 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder,
                               nsDisplayList* aChildren) {
   mShouldFlatten = false;
-  mHasBounds = false;
   mChildren.AppendToTop(aChildren);
-  UpdateUntransformedBounds(aBuilder);
+  UpdateBounds(aBuilder);
 }
 
 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) {
   if (gfxVars::UseWebRender() || !gfxPrefs::LayoutFlattenTransform()) {
     return false;
   }
 
   MOZ_ASSERT(!mShouldFlatten);
@@ -8168,44 +8165,121 @@ bool nsDisplayTransform::ComputeVisibili
   nsRegion visibleRegion;
   visibleRegion.And(bounds, untransformedVisibleRect);
   GetChildren()->ComputeVisibilityForSublist(aBuilder, &visibleRegion,
                                              visibleRegion.GetBounds());
 
   return true;
 }
 
+nsRect nsDisplayTransform::TransformUntransformedBounds(
+    nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const {
+  bool snap;
+  const nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
+  // GetTransform always operates in dev pixels.
+  const float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
+  return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix,
+                                            factor);
+}
+
+/**
+ * Returns the bounds for this transform. The bounds are calculated during
+ * display list building and merging, see |nsDisplayTransform::UpdateBounds()|.
+ */
+nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
+                                     bool* aSnap) const {
+  *aSnap = false;
+  return mBounds;
+}
+
+void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
+  MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
+
+  /* Some transforms can get empty bounds in 2D, but might get transformed again
+   * and get non-empty bounds. A simple example of this would be a 180 degree
+   * rotation getting applied twice.
+   * We should not depend on transforming bounds level by level.
+   *
+   * This function collects the bounds of this transform and stores it in
+   * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse
+   * down and include the bounds of the child transforms.
+   * The bounds are transformed with the accumulated transformation matrix up to
+   * the 3D context root coordinate space.
+   */
+  nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
+  accTransform.Accumulate(GetTransform().GetMatrix());
+
+  // Do not dive into another 3D context.
+  if (!IsLeafOf3DContext()) {
+    for (nsDisplayItem* i = GetChildren()->GetBottom(); i; i = i->GetAbove()) {
+      i->DoUpdateBoundsPreserves3D(aBuilder);
+    }
+  }
+
+  /* The child transforms that extend 3D context further will have empty bounds,
+   * so the untransformed bounds here is the bounds of all the non-preserve-3d
+   * content under this transform.
+   */
+  const nsRect rect = TransformUntransformedBounds(
+      aBuilder, accTransform.GetCurrentTransform());
+  aBuilder->AccumulateRect(rect);
+}
+
 void nsDisplayTransform::DoUpdateBoundsPreserves3D(
     nsDisplayListBuilder* aBuilder) {
   MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
              IsTransformSeparator());
   // Updating is not going through to child 3D context.
   ComputeBounds(aBuilder);
 }
 
 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) {
-  mHasBounds = false;
-  UpdateUntransformedBounds(aBuilder, true);
-  UpdateBoundsFor3D(aBuilder);
+  UpdateUntransformedBounds(aBuilder);
+
+  if (IsTransformSeparator()) {
+    MOZ_ASSERT(GetTransform().IsIdentity());
+    mBounds = mChildBounds;
+    return;
+  }
+
+  if (!mFrame->Combines3DTransformWithAncestors()) {
+    if (mFrame->Extend3DContext()) {
+      // The transform establishes a 3D context. |UpdateBoundsFor3D()| will
+      // collect the bounds from the child transforms.
+      UpdateBoundsFor3D(aBuilder);
+    } else {
+      // A stand-alone transform.
+      mBounds = TransformUntransformedBounds(aBuilder, GetTransform());
+    }
+
+    return;
+  }
+
+  // With nested 3D transforms, the 2D bounds might not be useful.
+  MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors());
+  mBounds = nsRect();
 }
 
 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) {
-  if (!mFrame->Extend3DContext() ||
-      mFrame->Combines3DTransformWithAncestors() || IsTransformSeparator()) {
-    // Not an establisher of a 3D rendering context.
-    return;
-  }
+  MOZ_ASSERT(mFrame->Extend3DContext() &&
+             !mFrame->Combines3DTransformWithAncestors() &&
+             !IsTransformSeparator());
 
   // Always start updating from an establisher of a 3D rendering context.
   nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder);
   nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
   accTransform.StartRoot();
   ComputeBounds(aBuilder);
   mBounds = aBuilder->GetAccumulatedRect();
-  mHasBounds = true;
+}
+
+void nsDisplayTransform::UpdateUntransformedBounds(
+    nsDisplayListBuilder* aBuilder) {
+  mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR(
+      aBuilder, mActiveScrolledRoot);
 }
 
 #ifdef DEBUG_HIT
 #  include <time.h>
 #endif
 
 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder,
@@ -8313,77 +8387,16 @@ float nsDisplayTransform::GetHitDepthAtP
                                  NSAppUnitsToFloatPixels(aPoint.y, factor)));
 
   Point point2d = point.As2DPoint();
 
   Point3D transformed = matrix.TransformPoint(Point3D(point2d.x, point2d.y, 0));
   return transformed.z;
 }
 
-/* The bounding rectangle for the object is the overflow rectangle translated
- * by the reference point.
- */
-nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder,
-                                     bool* aSnap) const {
-  *aSnap = false;
-
-  if (mHasBounds) {
-    return mBounds;
-  }
-
-  if (mFrame->Extend3DContext() && !mIsTransformSeparator) {
-    return nsRect();
-  }
-
-  bool snap;
-  nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
-  // GetTransform always operates in dev pixels.
-  float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
-  mBounds = nsLayoutUtils::MatrixTransformRect(untransformedBounds,
-                                               GetTransform(), factor);
-  mHasBounds = true;
-  return mBounds;
-}
-
-void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) {
-  MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext());
-
-  /* For some cases, the transform would make an empty bounds, but it
-   * may be turned back again to get a non-empty bounds.  We should
-   * not depend on transforming bounds level by level.
-   *
-   * Here, it applies accumulated transforms on the leaf frames of the
-   * 3d rendering context, and track and accmulate bounds at
-   * nsDisplayListBuilder.
-   */
-  nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
-
-  accTransform.Accumulate(GetTransform().GetMatrix());
-
-  if (!IsLeafOf3DContext()) {
-    // Do not dive into another 3D context.
-    for (nsDisplayItem* i = GetChildren()->GetBottom(); i; i = i->GetAbove()) {
-      i->DoUpdateBoundsPreserves3D(aBuilder);
-    }
-  }
-
-  /* For Preserves3D, it is bounds of only children as leaf frames.
-   * For non-leaf frames, their bounds are accumulated and kept at
-   * nsDisplayListBuilder.
-   */
-  bool snap;
-  nsRect untransformedBounds = GetUntransformedBounds(aBuilder, &snap);
-  // GetTransform always operates in dev pixels.
-  float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
-  nsRect rect = nsLayoutUtils::MatrixTransformRect(
-      untransformedBounds, accTransform.GetCurrentTransform(), factor);
-
-  aBuilder->AccumulateRect(rect);
-}
-
 /* The transform is opaque iff the transform consists solely of scales and
  * translations and if the underlying content is opaque.  Thus if the transform
  * is of the form
  *
  * |a c e|
  * |b d f|
  * |0 0 1|
  *
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -6508,32 +6508,23 @@ class nsDisplayTransform : public nsDisp
 
   nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override;
 
   RetainedDisplayList* GetChildren() const override { return &mChildren; }
 
   nsRect GetUntransformedBounds(nsDisplayListBuilder* aBuilder,
                                 bool* aSnap) const override {
     *aSnap = false;
-    return *mChildBounds;
+    return mChildBounds;
   }
 
   const nsRect& GetUntransformedPaintRect() const override {
     return mChildrenBuildingRect;
   }
 
-  void UpdateUntransformedBounds(nsDisplayListBuilder* aBuilder,
-                                 bool aForce = false) {
-    if (!mChildBounds || aForce) {
-      mChildBounds =
-          mozilla::Some(GetChildren()->GetClippedBoundsWithRespectToASR(
-              aBuilder, mActiveScrolledRoot));
-    }
-  }
-
   bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
 
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override;
   nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override;
   nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   LayerState GetLayerState(
@@ -6763,17 +6754,17 @@ class nsDisplayTransform : public nsDisp
 
   void WriteDebugInfo(std::stringstream& aStream) override;
 
   /**
    * This item is an additional item as the boundary between parent
    * and child 3D rendering context.
    * \see nsIFrame::BuildDisplayListForStackingContext().
    */
-  bool IsTransformSeparator() { return mIsTransformSeparator; }
+  bool IsTransformSeparator() const { return mIsTransformSeparator; }
   /**
    * This item is the boundary between parent and child 3D rendering
    * context.
    */
   bool IsLeafOf3DContext() {
     return (IsTransformSeparator() ||
             (!mFrame->Extend3DContext() &&
              mFrame->Combines3DTransformWithAncestors()));
@@ -6784,42 +6775,41 @@ class nsDisplayTransform : public nsDisp
    */
   bool IsParticipating3DContext() {
     return mFrame->Extend3DContext() ||
            mFrame->Combines3DTransformWithAncestors();
   }
 
  private:
   void ComputeBounds(nsDisplayListBuilder* aBuilder);
+  nsRect TransformUntransformedBounds(nsDisplayListBuilder* aBuilder,
+                                      const Matrix4x4Flagged& aMatrix) const;
+  void UpdateUntransformedBounds(nsDisplayListBuilder* aBuilder);
+
   void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
   void Init(nsDisplayListBuilder* aBuilder, nsDisplayList* aChildren);
 
   static Matrix4x4 GetResultingTransformMatrixInternal(
       const FrameTransformProperties& aProperties, const nsPoint& aOrigin,
       float aAppUnitsPerPixel, uint32_t aFlags, const nsRect* aBoundsOverride);
 
   mutable mozilla::Maybe<Matrix4x4Flagged> mTransform;
   mutable mozilla::Maybe<Matrix4x4Flagged> mInverseTransform;
   // Accumulated transform of ancestors on the preserves-3d chain.
   mozilla::UniquePtr<Matrix4x4> mTransformPreserves3D;
   ComputeTransformFunction mTransformGetter;
   RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForChildren;
   RefPtr<AnimatedGeometryRoot> mAnimatedGeometryRootForScrollMetadata;
   nsRect mChildrenBuildingRect;
   uint32_t mIndex;
-
-  // The transformed bounds of this display item.
-  mutable nsRect mBounds;
-  // True for mBounds is valid.
-  mutable bool mHasBounds;
-
   mutable RetainedDisplayList mChildren;
   // The untransformed bounds of |mChildren|.
-  mutable mozilla::Maybe<nsRect> mChildBounds;
-
+  nsRect mChildBounds;
+  // The transformed bounds of this display item.
+  nsRect mBounds;
   // This item is a separator between 3D rendering contexts, and
   // mTransform have been presetted by the constructor.
   // This also forces us not to extend the 3D context.  Since we don't create a
   // transform item, a container layer, for every frame in a preserves3d
   // context, the transform items of a child preserves3d context may extend the
   // parent context unintendedly if the root of the child preserves3d context
   // doesn't create a transform item.
   bool mIsTransformSeparator;