Bug 1022612. Part 40: Restrict visible rect of 3D-transformed layers before converting to nsIntRect. r=mattwoodrow
☠☠ backed out by d3776838896d ☠ ☠
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 11 Jul 2014 13:17:47 +1200
changeset 215951 e6be65e455a2c99098c41b175c85c0ad4bb882fc
parent 215950 6f857407b64e6c6cf79fba0906d3d9d782af2327
child 215952 5c1f3340c45f018d6653edfe0ddd7195bad28da2
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1022612
milestone33.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 1022612. Part 40: Restrict visible rect of 3D-transformed layers before converting to nsIntRect. r=mattwoodrow
layout/base/FrameLayerBuilder.cpp
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1698,46 +1698,59 @@ AppUnitsPerDevPixel(nsDisplayItem* aItem
   }
   return aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
 }
 #endif
 
 /**
  * Set the visible region for aLayer.
  * aOuterVisibleRegion is the visible region relative to the parent layer.
+ * aLayerContentsVisibleRect, if non-null, is a rectangle in the layer's
+ * own coordinate system to which the layer's visible region is restricted.
  * Consumes *aOuterVisibleRegion.
  */
 static void
 SetOuterVisibleRegion(Layer* aLayer, nsIntRegion* aOuterVisibleRegion,
                       const nsIntRect* aLayerContentsVisibleRect = nullptr)
 {
   gfx3DMatrix transform;
   To3DMatrix(aLayer->GetTransform(), transform);
   gfxMatrix transform2D;
   if (transform.Is2D(&transform2D) && !transform2D.HasNonIntegerTranslation()) {
     aOuterVisibleRegion->MoveBy(-int(transform2D._31), -int(transform2D._32));
+    if (aLayerContentsVisibleRect) {
+      aOuterVisibleRegion->And(*aOuterVisibleRegion, *aLayerContentsVisibleRect);
+    }
   } else {
     nsIntRect outerRect = aOuterVisibleRegion->GetBounds();
     // if 'transform' is not invertible, then nothing will be displayed
     // for the layer, so it doesn't really matter what we do here
     gfxRect outerVisible(outerRect.x, outerRect.y, outerRect.width, outerRect.height);
     gfxRect layerVisible = transform.Inverse().ProjectRectBounds(outerVisible);
+    if (aLayerContentsVisibleRect) {
+      NS_ASSERTION(aLayerContentsVisibleRect->width >= 0 &&
+                   aLayerContentsVisibleRect->height >= 0,
+                   "Bad layer contents rectangle");
+      // restrict to aLayerContentsVisibleRect before call GfxRectToIntRect,
+      // in case layerVisible is extremely large (as it can be when
+      // projecting through the inverse of a 3D transform)
+      gfxRect layerContentsVisible(
+          aLayerContentsVisibleRect->x, aLayerContentsVisibleRect->y,
+          aLayerContentsVisibleRect->width, aLayerContentsVisibleRect->height);
+      layerVisible.IntersectRect(layerVisible, layerContentsVisible);
+    }
     layerVisible.RoundOut();
     nsIntRect visRect;
     if (gfxUtils::GfxRectToIntRect(layerVisible, &visRect)) {
       *aOuterVisibleRegion = visRect;
     } else  {
       aOuterVisibleRegion->SetEmpty();
     }
   }
 
-  if (aLayerContentsVisibleRect && aLayerContentsVisibleRect->width >= 0) {
-    aOuterVisibleRegion->And(*aOuterVisibleRegion, *aLayerContentsVisibleRect);
-  }
-
   aLayer->SetVisibleRegion(*aOuterVisibleRegion);
 }
 
 void
 ContainerState::SetOuterVisibleRegionForLayer(Layer* aLayer,
                                               const nsIntRegion& aOuterVisibleRegion,
                                               const nsIntRect* aLayerContentsVisibleRect) const
 {
@@ -2748,16 +2761,19 @@ ContainerState::ProcessDisplayItems(nsDi
       // way, since their ThebesLayer may decide it wants to draw them
       // into its buffer even if they're currently covered.
       if (itemVisibleRect.IsEmpty() &&
           !item->ShouldBuildLayerEvenIfInvisible(mBuilder)) {
         continue;
       }
 
       // Just use its layer.
+      // Set layerContentsVisibleRect.width/height to -1 to indicate we
+      // currently don't know. If BuildContainerLayerFor gets called by
+      // item->BuildLayer, this will be set to a proper rect.
       nsIntRect layerContentsVisibleRect(0, 0, -1, -1);
       mParameters.mLayerContentsVisibleRect = &layerContentsVisibleRect;
       nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
       if (!ownLayer) {
         continue;
       }
 
       NS_ASSERTION(!ownLayer->AsThebesLayer(),
@@ -2842,25 +2858,31 @@ ContainerState::ProcessDisplayItems(nsDi
       }
       NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, ownLayer) < 0,
                    "Layer already in list???");
 
       NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
       newLayerEntry->mLayer = ownLayer;
       newLayerEntry->mAnimatedGeometryRoot = animatedGeometryRoot;
       newLayerEntry->mFixedPosFrameForLayerData = fixedPosFrame;
+      // nsDisplayTransform::BuildLayer must set layerContentsVisibleRect.
+      // We rely on this to ensure 3D transforms compute a reasonable
+      // layer visible region.
+      NS_ASSERTION(itemType != nsDisplayItem::TYPE_TRANSFORM ||
+                   layerContentsVisibleRect.width >= 0,
+                   "Transform items must set layerContentsVisibleRect!");
       if (mLayerBuilder->IsBuildingRetainedLayers()) {
         newLayerEntry->mLayerContentsVisibleRect = layerContentsVisibleRect;
         newLayerEntry->mVisibleRegion = itemVisibleRect;
         newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
           animatedGeometryRoot, fixedPosFrame, itemClip, aList,
           &newLayerEntry->mHideAllLayersBelow);
       } else {
         SetOuterVisibleRegionForLayer(ownLayer, itemVisibleRect,
-                                      &layerContentsVisibleRect);
+            layerContentsVisibleRect.width >= 0 ? &layerContentsVisibleRect : nullptr);
       }
       if (itemType == nsDisplayItem::TYPE_SCROLL_LAYER) {
         nsDisplayScrollLayer* scrollItem = static_cast<nsDisplayScrollLayer*>(item);
         newLayerEntry->mOpaqueForAnimatedGeometryRootParent =
             scrollItem->IsDisplayPortOpaque();
       }
 
       /**