Bug 1022612. Part 40: Restrict visible rect of 3D-transformed layers before converting to nsIntRect. r=mattwoodrow
☠☠ backed out by 2bcded4e3b4a ☠ ☠
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 11 Jul 2014 13:17:47 +1200
changeset 216636 fac64141a00aaac6f42b0b23c0ad847277ae43bf
parent 216635 bf0df1c9d68ba8b6521a9989aad1fc52444161ab
child 216637 0c57c620c8989bea318b1d0a51f8edb7393e5269
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();
       }
 
       /**