Bug 1494062 - Part 2. Ensure blob image dirty rects are not clipped too small by containers. r=jrmuizel
authorAndrew Osmond <aosmond@mozilla.com>
Thu, 17 Jan 2019 07:56:12 -0500
changeset 511638 c82870227fa524ca423e781d27c98c683ebfe8f6
parent 511637 3167fee85c59af3d35d7917199d1277900e2a0ba
child 511639 5f4b3423f6fefaeffaf130898b2384756f22877f
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1494062
milestone66.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 1494062 - Part 2. Ensure blob image dirty rects are not clipped too small by containers. r=jrmuizel If an item in a blob image gets a new parent container which clips it to a smaller area than it was previously, we need to ensure the dirty rect does not take into account the new clip when determining the old intersection area. Instead now all old rects for an image are clipped to the image bounds, rather than the image bounds clipped to the aggregate of the ancestor containers. Differential Revision: https://phabricator.services.mozilla.com/D16855
gfx/layers/wr/WebRenderCommandBuilder.cpp
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -319,16 +319,19 @@ struct DIGroup {
   int32_t mAppUnitsPerDevPixel;
   gfx::Size mScale;
   ScrollableLayerGuid::ViewID mScrollId;
   LayerPoint mResidualOffset;
   LayerIntRect mLayerBounds;
   // The current bounds of the blob image, relative to
   // the top-left of the mLayerBounds.
   IntRect mImageBounds;
+  // mImageBounds clipped to the container/parent of the
+  // current item being processed.
+  IntRect mClippedImageBounds;
   Maybe<wr::BlobImageKey> mKey;
   std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
   std::vector<RefPtr<ScaledFont>> mFonts;
 
   DIGroup()
       : mAppUnitsPerDevPixel(0),
         mScrollId(ScrollableLayerGuid::NULL_SCROLL_ID) {}
 
@@ -386,18 +389,19 @@ struct DIGroup {
     int32_t appUnitsPerDevPixel =
         aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
     MOZ_RELEASE_ASSERT(mAppUnitsPerDevPixel == appUnitsPerDevPixel);
     LayoutDeviceRect bounds =
         LayoutDeviceRect::FromAppUnits(mGroupBounds, appUnitsPerDevPixel);
     LayoutDeviceIntPoint offset = RoundedToInt(bounds.TopLeft());
     GP("\n");
     GP("CGC offset %d %d\n", offset.x, offset.y);
-    GP("imageRect %d %d %d %d\n", mImageBounds.x, mImageBounds.y,
-       mImageBounds.width, mImageBounds.height);
+    GP("clippedImageRect %d %d %d %d\n", mClippedImageBounds.x,
+       mClippedImageBounds.y, mClippedImageBounds.width,
+       mClippedImageBounds.height);
     /*if (aItem->IsReused() && aData->mGeometry) {
       return;
     }*/
     aData->mInvalidRegion = false;
 
     GP("pre mInvalidRect: %s %p-%d - inv: %d %d %d %d\n", aItem->Name(),
        aItem->Frame(), aItem->GetPerFrameKey(), mInvalidRect.x, mInvalidRect.y,
        mInvalidRect.width, mInvalidRect.height);
@@ -407,17 +411,17 @@ struct DIGroup {
       UniquePtr<nsDisplayItemGeometry> geometry(
           aItem->AllocateGeometry(aBuilder));
       nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
           geometry->ComputeInvalidationRegion());
       aData->mGeometry = std::move(geometry);
 
       IntRect transformedRect = ToDeviceSpace(
           clippedBounds, aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
-      aData->mRect = transformedRect.Intersect(mImageBounds);
+      aData->mRect = transformedRect.Intersect(mClippedImageBounds);
       GP("CGC %s %d %d %d %d\n", aItem->Name(), clippedBounds.x,
          clippedBounds.y, clippedBounds.width, clippedBounds.height);
       GP("%d %d,  %f %f\n", mLayerBounds.TopLeft().x, mLayerBounds.TopLeft().y,
          aMatrix._11, aMatrix._22);
       GP("mRect %d %d %d %d\n", aData->mRect.x, aData->mRect.y,
          aData->mRect.width, aData->mRect.height);
       InvalidateRect(aData->mRect);
       aData->mInvalid = true;
@@ -437,17 +441,17 @@ struct DIGroup {
          aData->mRect.width, aData->mRect.height);
       InvalidateRect(aData->mRect.Intersect(mImageBounds));
       // We want to snap to outside pixels. When should we multiply by the
       // matrix?
       // XXX: TransformBounds is expensive. We should avoid doing it if we have
       // no transform
       IntRect transformedRect = ToDeviceSpace(
           clippedBounds, aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
-      aData->mRect = transformedRect.Intersect(mImageBounds);
+      aData->mRect = transformedRect.Intersect(mClippedImageBounds);
       InvalidateRect(aData->mRect);
       GP("new rect: %d %d %d %d\n", aData->mRect.x, aData->mRect.y,
          aData->mRect.width, aData->mRect.height);
       aData->mInvalid = true;
     } else {
       MOZ_RELEASE_ASSERT(mLayerBounds.TopLeft() == aData->mGroupOffset);
       GP("else invalidate: %s\n", aItem->Name());
       nsRegion combined;
@@ -466,17 +470,17 @@ struct DIGroup {
 
         aData->mGeometry = std::move(geometry);
 
         nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
             aData->mGeometry->ComputeInvalidationRegion());
         IntRect transformedRect =
             ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                           mLayerBounds.TopLeft());
-        aData->mRect = transformedRect.Intersect(mImageBounds);
+        aData->mRect = transformedRect.Intersect(mClippedImageBounds);
         InvalidateRect(aData->mRect);
 
         // CGC invariant broken
         if (!mInvalidRect.Contains(aData->mRect)) {
           gfxCriticalError()
               << "CGC-"
               << "-" << aData->mRect.x << "-" << aData->mRect.y << "-"
               << aData->mRect.width << "-" << aData->mRect.height << "-ib";
@@ -498,17 +502,17 @@ struct DIGroup {
             aData->mGeometry = std::move(geometry);
           }
           nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
               aData->mGeometry->ComputeInvalidationRegion());
           IntRect transformedRect =
               ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                             mLayerBounds.TopLeft());
           InvalidateRect(aData->mRect.Intersect(mImageBounds));
-          aData->mRect = transformedRect.Intersect(mImageBounds);
+          aData->mRect = transformedRect.Intersect(mClippedImageBounds);
           InvalidateRect(aData->mRect);
 
           GP("ClipChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x,
              aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
 
         } else if (!aMatrix.ExactlyEquals(aData->mMatrix)) {
           // We haven't detected any changes so far. Unfortunately we don't
           // currently have a good way of checking if the transform has changed
@@ -528,17 +532,17 @@ struct DIGroup {
             aData->mGeometry = std::move(geometry);
           }
           nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
               aData->mGeometry->ComputeInvalidationRegion());
           IntRect transformedRect =
               ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                             mLayerBounds.TopLeft());
           InvalidateRect(aData->mRect.Intersect(mImageBounds));
-          aData->mRect = transformedRect.Intersect(mImageBounds);
+          aData->mRect = transformedRect.Intersect(mClippedImageBounds);
           InvalidateRect(aData->mRect);
 
           GP("TransformChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x,
              aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
         } else if (IsContainerLayerItem(aItem)) {
           UniquePtr<nsDisplayItemGeometry> geometry(
               aItem->AllocateGeometry(aBuilder));
           // we need to catch bounds changes of containers so that we continue
@@ -547,80 +551,80 @@ struct DIGroup {
                                                             *geometry)) {
             nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
                 geometry->ComputeInvalidationRegion());
             aData->mGeometry = std::move(geometry);
             IntRect transformedRect =
                 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                               mLayerBounds.TopLeft());
             InvalidateRect(aData->mRect.Intersect(mImageBounds));
-            aData->mRect = transformedRect.Intersect(mImageBounds);
+            aData->mRect = transformedRect.Intersect(mClippedImageBounds);
             InvalidateRect(aData->mRect);
             GP("UpdateContainerLayerPropertiesAndDetectChange change\n");
-          } else if (!aData->mImageRect.IsEqualEdges(mImageBounds)) {
-            // Make sure we update mRect for mImageBounds changes
+          } else if (!aData->mImageRect.IsEqualEdges(mClippedImageBounds)) {
+            // Make sure we update mRect for mClippedImageBounds changes
             nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
                 geometry->ComputeInvalidationRegion());
             IntRect transformedRect =
                 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                               mLayerBounds.TopLeft());
             // The invalid rect should contain the old rect and the new rect
             // but may not because the parent may have been removed.
             InvalidateRect(aData->mRect);
-            aData->mRect = transformedRect.Intersect(mImageBounds);
+            aData->mRect = transformedRect.Intersect(mClippedImageBounds);
             InvalidateRect(aData->mRect);
             GP("ContainerLayer image rect bounds change\n");
           } else {
             // XXX: this code can eventually be deleted/made debug only
             nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
                 geometry->ComputeInvalidationRegion());
             IntRect transformedRect =
                 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                               mLayerBounds.TopLeft());
-            auto rect = transformedRect.Intersect(mImageBounds);
+            auto rect = transformedRect.Intersect(mClippedImageBounds);
             GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(),
                aData->mRect.x, aData->mRect.y, aData->mRect.XMost(),
                aData->mRect.YMost());
             MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
           }
-        } else if (!aData->mImageRect.IsEqualEdges(mImageBounds)) {
-          // Make sure we update mRect for mImageBounds changes
+        } else if (!aData->mImageRect.IsEqualEdges(mClippedImageBounds)) {
+          // Make sure we update mRect for mClippedImageBounds changes
           UniquePtr<nsDisplayItemGeometry> geometry(
               aItem->AllocateGeometry(aBuilder));
           nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
               geometry->ComputeInvalidationRegion());
           IntRect transformedRect =
               ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                             mLayerBounds.TopLeft());
           // The invalid rect should contain the old rect and the new rect
           // but may not because the parent may have been removed.
           InvalidateRect(aData->mRect);
-          aData->mRect = transformedRect.Intersect(mImageBounds);
+          aData->mRect = transformedRect.Intersect(mClippedImageBounds);
           InvalidateRect(aData->mRect);
           GP("image rect bounds change\n");
         } else {
           // XXX: this code can eventually be deleted/made debug only
           UniquePtr<nsDisplayItemGeometry> geometry(
               aItem->AllocateGeometry(aBuilder));
           nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
               geometry->ComputeInvalidationRegion());
           IntRect transformedRect =
               ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel,
                             mLayerBounds.TopLeft());
-          auto rect = transformedRect.Intersect(mImageBounds);
+          auto rect = transformedRect.Intersect(mClippedImageBounds);
           GP("NoChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x,
              aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
           MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
         }
       }
     }
     aData->mClip = clip;
     aData->mMatrix = aMatrix;
     aData->mGroupOffset = mLayerBounds.TopLeft();
-    aData->mImageRect = mImageBounds;
+    aData->mImageRect = mClippedImageBounds;
     GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y,
        mInvalidRect.width, mInvalidRect.height);
   }
 
   void EndGroup(WebRenderLayerManager* aWrManager,
                 nsDisplayListBuilder* aDisplayListBuilder,
                 wr::DisplayListBuilder& aBuilder,
                 wr::IpcResourceUpdateQueue& aResources, Grouper* aGrouper,
@@ -1157,16 +1161,18 @@ void Grouper::ConstructGroups(nsDisplayL
         groupData->mFollowingGroup.ClearImageKey(
             aCommandBuilder->mManager->GetRenderRootStateManager());
       }
       groupData->mFollowingGroup.mGroupBounds = currentGroup->mGroupBounds;
       groupData->mFollowingGroup.mAppUnitsPerDevPixel =
           currentGroup->mAppUnitsPerDevPixel;
       groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds;
       groupData->mFollowingGroup.mImageBounds = currentGroup->mImageBounds;
+      groupData->mFollowingGroup.mClippedImageBounds =
+          currentGroup->mClippedImageBounds;
       groupData->mFollowingGroup.mScale = currentGroup->mScale;
       groupData->mFollowingGroup.mResidualOffset =
           currentGroup->mResidualOffset;
       groupData->mFollowingGroup.mPaintRect = currentGroup->mPaintRect;
 
       currentGroup = &groupData->mFollowingGroup;
 
       startOfCurrentGroup = item->GetAbove();
@@ -1214,18 +1220,19 @@ void Grouper::ConstructItemInsideInactiv
   data->mInvalid = false;
 
   // we compute the geometry change here because we have the transform around
   // still
   aGroup->ComputeGeometryChange(aItem, data, mTransform, mDisplayListBuilder);
 
   // Temporarily restrict the image bounds to the bounds of the container so
   // that clipped children within the container know about the clip.
-  IntRect oldImageBounds = aGroup->mImageBounds;
-  aGroup->mImageBounds = aGroup->mImageBounds.Intersect(data->mRect);
+  IntRect oldClippedImageBounds = aGroup->mClippedImageBounds;
+  aGroup->mClippedImageBounds =
+      aGroup->mClippedImageBounds.Intersect(data->mRect);
 
   if (aItem->GetType() == DisplayItemType::TYPE_FILTER) {
     gfx::Size scale(1, 1);
     // If ComputeDifferences finds any change, we invalidate the entire
     // container item. This is needed because blob merging requires the entire
     // item to be within the invalid region.
     if (BuildLayer(aItem, data, mDisplayListBuilder, scale)) {
       data->mInvalid = true;
@@ -1260,17 +1267,17 @@ void Grouper::ConstructItemInsideInactiv
   } else if (children) {
     sIndent++;
     ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, aGroup,
                                  children, aSc);
     sIndent--;
   }
 
   GP("Including %s of %d\n", aItem->Name(), aGroup->mDisplayItems.Count());
-  aGroup->mImageBounds = oldImageBounds;
+  aGroup->mClippedImageBounds = oldClippedImageBounds;
 }
 
 /* This is just a copy of nsRect::ScaleToOutsidePixels with an offset added in.
  * The offset is applied just before the rounding. It's in the scaled space. */
 static mozilla::gfx::IntRect ScaleToOutsidePixelsOffset(
     nsRect aRect, float aXScale, float aYScale, nscoord aAppUnitsPerPixel,
     LayerPoint aOffset) {
   mozilla::gfx::IntRect rect;
@@ -1368,16 +1375,17 @@ void WebRenderCommandBuilder::DoGrouping
   group.mResidualOffset = residualOffset;
   group.mGroupBounds = groupBounds;
   group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   group.mLayerBounds = LayerIntRect::FromUnknownRect(
       ScaleToOutsidePixelsOffset(group.mGroupBounds, scale.width, scale.height,
                                  group.mAppUnitsPerDevPixel, residualOffset));
   group.mImageBounds =
       IntRect(0, 0, group.mLayerBounds.width, group.mLayerBounds.height);
+  group.mClippedImageBounds = group.mImageBounds;
   group.mPaintRect =
       LayerIntRect::FromUnknownRect(
           ScaleToOutsidePixelsOffset(aWrappingItem->GetPaintRect(), scale.width,
                                      scale.height, group.mAppUnitsPerDevPixel,
                                      residualOffset))
           .Intersect(group.mLayerBounds);
   // XXX: Make the paint rect relative to the layer bounds. After we include
   // mLayerBounds.TopLeft() in the blob image we want to stop doing this