Bug 579349. Track the 'drawn region' of content in layers, whhich ignores visibility, and use it to determine which layer a display item should be placed in. r=tnikkel
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 02 Aug 2010 15:06:58 +1200
changeset 48689 491b28e3c1080528031de7c2efb5194c9fcc3bf6
parent 48688 488ea306526be3c11c343f097b2990d3061f3365
child 48690 6acec0cadc4c3f587125f8969ed264800c7b2c77
push id14768
push userrocallahan@mozilla.com
push dateMon, 02 Aug 2010 03:08:16 +0000
treeherdermozilla-central@b1d534754b62 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs579349
milestone2.0b3pre
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 579349. Track the 'drawn region' of content in layers, whhich ignores visibility, and use it to determine which layer a display item should be placed in. r=tnikkel
gfx/layers/Layers.cpp
layout/base/FrameLayerBuilder.cpp
layout/reftests/bugs/579349-1-ref.html
layout/reftests/bugs/579349-1.html
layout/reftests/bugs/reftest.list
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -89,18 +89,18 @@ AppendToString(nsACString& s, const gfx3
 {
   s += pfx;
   if (m.IsIdentity())
     s += "[ I ]";
   else {
     gfxMatrix matrix;
     if (m.Is2D(&matrix)) {
       s += nsPrintfCString(
-        "[ %g %g; %g %g; %g %g; ]",
-        96, matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0);
+        96, "[ %g %g; %g %g; %g %g; ]",
+        matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0);
     } else {
       s += nsPrintfCString(
         256, "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]",
         m._11, m._12, m._13, m._14,
         m._21, m._22, m._23, m._24,
         m._31, m._32, m._33, m._34,
         m._41, m._42, m._43, m._44);
     }
@@ -213,20 +213,23 @@ Layer::LogSelf(const char* aPrefix)
 }
 
 nsACString&
 Layer::PrintInfo(nsACString& aTo, const char* aPrefix)
 {
   aTo += aPrefix;
   aTo += nsPrintfCString(64, "%s%s (0x%p)", mManager->Name(), Name(), this);
 
+  if (mUseClipRect) {
+    AppendToString(aTo, mClipRect, " [clip=", "]");
+  }
+  if (!mTransform.IsIdentity())
+    AppendToString(aTo, mTransform, " [transform=", "]");
   if (!mVisibleRegion.IsEmpty())
     AppendToString(aTo, mVisibleRegion, " [visible=", "]");
-  if (!mTransform.IsIdentity())
-    AppendToString(aTo, mTransform, " [transform=", "]");
   if (1.0 != mOpacity)
     aTo.AppendPrintf(" [opacity=%g]", mOpacity);
   if (IsOpaqueContent())
     aTo += " [opaqueContent]";
 
   return aTo;
 }
 
@@ -245,25 +248,29 @@ ColorLayer::PrintInfo(nsACString& aTo, c
   AppendToString(aTo, mColor, " [color=", "]");
   return aTo;
 }
 
 nsACString&
 CanvasLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
 {
   Layer::PrintInfo(aTo, aPrefix);
-  AppendToString(aTo, mFilter, " [filter=", "]");
+  if (mFilter != gfxPattern::FILTER_GOOD) {
+    AppendToString(aTo, mFilter, " [filter=", "]");
+  }
   return aTo;
 }
 
 nsACString&
 ImageLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
 {
   Layer::PrintInfo(aTo, aPrefix);
-  AppendToString(aTo, mFilter, " [filter=", "]");
+  if (mFilter != gfxPattern::FILTER_GOOD) {
+    AppendToString(aTo, mFilter, " [filter=", "]");
+  }
   return aTo;
 }
 
 //--------------------------------------------------
 // LayerManager
 
 void
 LayerManager::Dump(FILE* aFile, const char* aPrefix)
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -147,24 +147,27 @@ protected:
   public:
     ThebesLayerData() :
       mActiveScrolledRoot(nsnull), mLayer(nsnull),
       mIsSolidColorInVisibleRegion(PR_FALSE) {}
     /**
      * Record that an item has been added to the ThebesLayer, so we
      * need to update our regions.
      * @param aVisibleRect the area of the item that's visible
+     * @param aDrawRect the area of the item that would be drawn if it
+     * was completely visible
      * @param aOpaqueRect if non-null, the area of the item that's opaque.
      * We pass in a separate opaque rect because the opaque rect can be
      * bigger than the visible rect, and we want to have the biggest
      * opaque rect that we can.
      * @param aSolidColor if non-null, the visible area of the item is
      * a constant color given by *aSolidColor
      */
     void Accumulate(const nsIntRect& aVisibleRect,
+                    const nsIntRect& aDrawRect,
                     const nsIntRect* aOpaqueRect,
                     nscolor* aSolidColor);
     nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; }
 
     /**
      * The region of visible content in the layer, relative to the
      * container layer (which is at the snapped top-left of the display
      * list reference frame).
@@ -173,16 +176,31 @@ protected:
     /**
      * The region of visible content above the layer and below the
      * next ThebesLayerData currently in the stack, if any. Note that not
      * all ThebesLayers for the container are in the ThebesLayerData stack.
      * Same coordinate system as mVisibleRegion.
      */
     nsIntRegion  mVisibleAboveRegion;
     /**
+     * The region containing the bounds of all display items in the layer,
+     * regardless of visbility.
+     * Same coordinate system as mVisibleRegion.
+     */
+    nsIntRegion  mDrawRegion;
+    /**
+     * The region containing the bounds of all display items (regardless
+     * of visibility) in the layer and below the next ThebesLayerData
+     * currently in the stack, if any.
+     * Note that not all ThebesLayers for the container are in the
+     * ThebesLayerData stack.
+     * Same coordinate system as mVisibleRegion.
+     */
+    nsIntRegion  mDrawAboveRegion;
+    /**
      * The region of visible content in the layer that is opaque.
      * Same coordinate system as mVisibleRegion.
      */
     nsIntRegion  mOpaqueRegion;
     /**
      * The "active scrolled root" for all content in the layer. Must
      * be non-null; all content in a ThebesLayer must have the same
      * active scrolled root.
@@ -226,28 +244,33 @@ protected:
   /**
    * Indicate that we are done adding items to the ThebesLayer at the top of
    * mThebesLayerDataStack. Set the final visible region and opaque-content
    * flag, and pop it off the stack.
    */
   void PopThebesLayerData();
   /**
    * Find the ThebesLayer to which we should assign the next display item.
+   * We scan the ThebesLayerData stack to find the topmost ThebesLayer
+   * that is compatible with the display item (i.e., has the same
+   * active scrolled root), and that has no content from other layers above
+   * it and intersecting the aVisibleRect.
    * Returns the layer, and also updates the ThebesLayerData. Will
-   * push a new ThebesLayerData onto the stack if necessary. If we choose
-   * a ThebesLayer that's already on the ThebesLayerData stack,
-   * later elements on the stack will be popped off.
+   * push a new ThebesLayerData onto the stack if no suitable existing
+   * layer is found. If we choose a ThebesLayer that's already on the
+   * ThebesLayerData stack, later elements on the stack will be popped off.
    * @param aVisibleRect the area of the next display item that's visible
    * @param aActiveScrolledRoot the active scrolled root for the next
    * display item
    * @param aOpaqueRect if non-null, a region of the display item that is opaque
    * @param aSolidColor if non-null, indicates that every pixel in aVisibleRect
    * will be painted with aSolidColor by the item
    */
   already_AddRefed<ThebesLayer> FindThebesLayerFor(const nsIntRect& aVisibleRect,
+                                                   const nsIntRect& aDrawRect,
                                                    nsIFrame* aActiveScrolledRoot,
                                                    const nsIntRect* aOpaqueRect,
                                                    nscolor* aSolidColor);
   ThebesLayerData* GetTopThebesLayerData()
   {
     return mThebesLayerDataStack.IsEmpty() ? nsnull
         : mThebesLayerDataStack[mThebesLayerDataStack.Length() - 1].get();
   }
@@ -669,16 +692,20 @@ ContainerState::PopThebesLayerData()
     // Since we're going to pop off the last ThebesLayerData, the
     // mVisibleAboveRegion of the second-to-last item will need to include
     // the regions of the last item.
     ThebesLayerData* nextData = mThebesLayerDataStack[lastIndex - 1];
     nextData->mVisibleAboveRegion.Or(nextData->mVisibleAboveRegion,
                                      data->mVisibleAboveRegion);
     nextData->mVisibleAboveRegion.Or(nextData->mVisibleAboveRegion,
                                      data->mVisibleRegion);
+    nextData->mDrawAboveRegion.Or(nextData->mDrawAboveRegion,
+                                     data->mDrawAboveRegion);
+    nextData->mDrawAboveRegion.Or(nextData->mDrawAboveRegion,
+                                     data->mDrawRegion);
   }
 
   Layer* layer;
   if (data->mIsSolidColorInVisibleRegion) {
     nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer();
     colorLayer->SetColor(data->mSolidColor);
 
     NS_ASSERTION(!mNewChildLayers.Contains(colorLayer), "Layer already in list???");
@@ -723,74 +750,78 @@ ContainerState::PopThebesLayerData()
   nsIntRegion transparentRegion;
   transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
   layer->SetIsOpaqueContent(transparentRegion.IsEmpty());
 
   mThebesLayerDataStack.RemoveElementAt(lastIndex);
 }
 
 void
-ContainerState::ThebesLayerData::Accumulate(const nsIntRect& aRect,
+ContainerState::ThebesLayerData::Accumulate(const nsIntRect& aVisibleRect,
+                                            const nsIntRect& aDrawRect,
                                             const nsIntRect* aOpaqueRect,
                                             nscolor* aSolidColor)
 {
   if (aSolidColor) {
     if (mVisibleRegion.IsEmpty()) {
       // This color is all we have
       mSolidColor = *aSolidColor;
       mIsSolidColorInVisibleRegion = PR_TRUE;
     } else if (mIsSolidColorInVisibleRegion &&
-               mVisibleRegion.IsEqual(nsIntRegion(aRect))) {
+               mVisibleRegion.IsEqual(nsIntRegion(aVisibleRect))) {
       // we can just blend the colors together
       mSolidColor = NS_ComposeColors(mSolidColor, *aSolidColor);
     } else {
       mIsSolidColorInVisibleRegion = PR_FALSE;
     }
   } else {
     mIsSolidColorInVisibleRegion = PR_FALSE;
   }
 
-  mVisibleRegion.Or(mVisibleRegion, aRect);
+  mVisibleRegion.Or(mVisibleRegion, aVisibleRect);
   mVisibleRegion.SimplifyOutward(4);
+  mDrawRegion.Or(mDrawRegion, aDrawRect);
+  mDrawRegion.SimplifyOutward(4);
   if (aOpaqueRect) {
     // We don't use SimplifyInward here since it's not defined exactly
     // what it will discard. For our purposes the most important case
     // is a large opaque background at the bottom of z-order (e.g.,
     // a canvas background), so we need to make sure that the first rect
     // we see doesn't get discarded.
     nsIntRegion tmp;
     tmp.Or(mOpaqueRegion, *aOpaqueRect);
     if (tmp.GetNumRects() <= 4) {
       mOpaqueRegion = tmp;
     }
   }
 }
 
 already_AddRefed<ThebesLayer>
 ContainerState::FindThebesLayerFor(const nsIntRect& aVisibleRect,
+                                   const nsIntRect& aDrawRect,
                                    nsIFrame* aActiveScrolledRoot,
                                    const nsIntRect* aOpaqueRect,
                                    nscolor* aSolidColor)
 {
   PRInt32 i;
   PRInt32 lowestUsableLayerWithScrolledRoot = -1;
   PRInt32 topmostLayerWithScrolledRoot = -1;
   for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) {
     ThebesLayerData* data = mThebesLayerDataStack[i];
-    if (data->mVisibleAboveRegion.Intersects(aVisibleRect)) {
+    if (data->mDrawAboveRegion.Intersects(aVisibleRect)) {
       ++i;
       break;
     }
     if (data->mActiveScrolledRoot == aActiveScrolledRoot) {
       lowestUsableLayerWithScrolledRoot = i;
       if (topmostLayerWithScrolledRoot < 0) {
         topmostLayerWithScrolledRoot = i;
       }
     }
-    if (data->mVisibleRegion.Intersects(aVisibleRect))
+    if (data->mDrawRegion.Intersects(aVisibleRect))
       break;
   }
   if (topmostLayerWithScrolledRoot < 0) {
     --i;
     for (; i >= 0; --i) {
       ThebesLayerData* data = mThebesLayerDataStack[i];
       if (data->mActiveScrolledRoot == aActiveScrolledRoot) {
         topmostLayerWithScrolledRoot = i;
@@ -817,17 +848,17 @@ ContainerState::FindThebesLayerFor(const
     mThebesLayerDataStack.AppendElement(thebesLayerData);
     thebesLayerData->mLayer = layer;
     thebesLayerData->mActiveScrolledRoot = aActiveScrolledRoot;
   } else {
     thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot];
     layer = thebesLayerData->mLayer;
   }
 
-  thebesLayerData->Accumulate(aVisibleRect, aOpaqueRect, aSolidColor);
+  thebesLayerData->Accumulate(aVisibleRect, aDrawRect, aOpaqueRect, aSolidColor);
   return layer.forget();
 }
 
 /*
  * Iterate through the non-clip items in aList and its descendants.
  * For each item we compute the effective clip rect. Each item is assigned
  * to a layer. We invalidate the areas in ThebesLayers where an item
  * has moved from one ThebesLayer to another. Also,
@@ -852,16 +883,21 @@ ContainerState::ProcessDisplayItems(cons
       }
       ProcessDisplayItems(*clipItem->GetList(), &clip);
       continue;
     }
 
     PRInt32 appUnitsPerDevPixel = AppUnitsPerDevPixel(item);
     nsIntRect itemVisibleRect =
       item->GetVisibleRect().ToNearestPixels(appUnitsPerDevPixel);
+    nsRect itemContent = item->GetBounds(mBuilder);
+    if (aClipRect) {
+      itemContent.IntersectRect(*aClipRect, itemContent);
+    }
+    nsIntRect itemDrawRect = itemContent.ToNearestPixels(appUnitsPerDevPixel);
     nsDisplayItem::LayerState layerState =
       item->GetLayerState(mBuilder, mManager);
 
     // Assign the item to a layer
     if (layerState == LAYER_ACTIVE) {
       // If the item would have its own layer but is invisible, just hide it.
       // Note that items without their own layers can't be skipped this
       // way, since their ThebesLayer may decide it wants to draw them
@@ -885,16 +921,21 @@ ContainerState::ProcessDisplayItems(cons
       // It has its own layer. Update that layer's clip and visible rects.
       if (aClipRect) {
         ownLayer->IntersectClipRect(
             aClipRect->ToNearestPixels(appUnitsPerDevPixel));
       }
       ThebesLayerData* data = GetTopThebesLayerData();
       if (data) {
         data->mVisibleAboveRegion.Or(data->mVisibleAboveRegion, itemVisibleRect);
+        // Add the entire bounds rect to the mDrawAboveRegion.
+        // The visible region may be excluding opaque content above the
+        // item, and we need to ensure that that content is not placed
+        // in a ThebesLayer below the item!
+        data->mDrawAboveRegion.Or(data->mDrawAboveRegion, itemDrawRect);
       }
       SetVisibleRectForLayer(ownLayer, itemVisibleRect);
       ContainerLayer* oldContainer = ownLayer->GetParent();
       if (oldContainer && oldContainer != mContainerLayer) {
         oldContainer->RemoveChild(ownLayer);
       }
       NS_ASSERTION(!mNewChildLayers.Contains(ownLayer),
                    "Layer already in list???");
@@ -927,17 +968,17 @@ ContainerState::ProcessDisplayItems(cons
       nscolor uniformColor;
       PRBool isUniform = item->IsUniform(mBuilder, &uniformColor);
       PRBool isOpaque = item->IsOpaque(mBuilder);
       nsIntRect opaqueRect;
       if (isOpaque) {
         opaqueRect = item->GetBounds(mBuilder).ToNearestPixels(appUnitsPerDevPixel);
       }
       nsRefPtr<ThebesLayer> thebesLayer =
-        FindThebesLayerFor(itemVisibleRect, activeScrolledRoot,
+        FindThebesLayerFor(itemVisibleRect, itemDrawRect, activeScrolledRoot,
                            isOpaque ? &opaqueRect : nsnull,
                            isUniform ? &uniformColor : nsnull);
 
       InvalidateForLayerChange(item, thebesLayer);
 
       mBuilder->LayerBuilder()->
         AddThebesDisplayItem(thebesLayer, mBuilder,
                              item, aClipRect, mContainerFrame,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/579349-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="width:500px; height:500px; background:blue;"></div>
+<div style="position:absolute; left:0; top:0; height:100px; width:100%; background:yellow"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/579349-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<body>
+<canvas id="c" width="500" height="200"></canvas>
+<div style="position:absolute; left:0; top:0; height:100px; width:100%; background:yellow"></div>
+<script>
+var c = document.getElementById("c");
+var ctx = c.getContext("2d");
+ctx.fillStyle = "blue;";
+ctx.fillRect(0, 0, c.width, c.height);
+</script>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1454,8 +1454,9 @@ random-if(!haveTestPlugin) == 546071-1.h
 == 571347-1b.html 571347-1-ref.html
 == 571347-2a.html 571347-2-ref.html
 == 571347-2b.html 571347-2-ref.html
 == 571347-2c.html 571347-2-ref.html
 == 571347-2d.html 571347-2-ref.html
 == 571347-3.html 571347-3-ref.html
 == 572598-1.html 572598-ref.html
 == 574898-1.html 574898-ref.html
+== 579349-1.html 579349-1-ref.html