Commit b8e126f8 authored by Robert O'Callahan's avatar Robert O'Callahan
Browse files

Bug 507334, part 2: Blit multiple rectangles when scrolling rather than...

Bug 507334, part 2:  Blit multiple rectangles when scrolling rather than blitting only the largest single rectangle, and avoid repainting opaque content that covers the scrolling content.  r=dbaron
parent 4ddd47e3
Loading
Loading
Loading
Loading
+68 −12
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
    PRBool aIsForEvents, PRBool aBuildCaret)
    : mReferenceFrame(aReferenceFrame),
      mMovingFrame(nsnull),
      mSaveVisibleRegionOfMovingContent(nsnull),
      mIgnoreScrollFrame(nsnull),
      mCurrentTableItem(nsnull),
      mBuildCaret(aBuildCaret),
@@ -155,6 +156,16 @@ nsDisplayListBuilder::~nsDisplayListBuilder() {
  PL_FinishArenaPool(&mPool);
}

void
nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
                                                const nsRegion& aRegion)
{
  aVisibleRegion->Sub(*aVisibleRegion, aRegion);
  if (!GetAccurateVisibleRegions()) {
    aVisibleRegion->SimplifyOutward(15);
  }
}

PRBool
nsDisplayListBuilder::IsMovingFrame(nsIFrame* aFrame)
{
@@ -236,6 +247,26 @@ nsDisplayListBuilder::Allocate(size_t aSize) {
  return tmp;
}

void
nsDisplayListBuilder::AccumulateVisibleRegionOfMovingContent(const nsRegion& aMovingContent,
                                                             const nsRegion& aVisibleRegion)
{
  if (!mSaveVisibleRegionOfMovingContent)
    return;

  // Grab the union of aMovingContent (after the move) with
  // aMovingContent - mMoveDelta (before the move)
  nsRegion r = aMovingContent;
  r.MoveBy(-mMoveDelta);
  r.Or(r, aMovingContent);
  // Reduce to the part that's visible after the move
  r.And(r, aVisibleRegion);
  // Accumulate it into our result
  mSaveVisibleRegionOfMovingContent->Or(
      *mSaveVisibleRegionOfMovingContent, r);
  mSaveVisibleRegionOfMovingContent->SimplifyOutward(15);
}

void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const
{
  aDestination.BorderBackground()->AppendToTop(BorderBackground());
@@ -266,11 +297,7 @@ nsDisplayItem::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
      // before state and in the after state.
      opaqueArea.IntersectRect(bounds - aBuilder->GetMoveDelta(), bounds);
    }
    if (aBuilder->GetAccurateVisibleRegions()) {
      aVisibleRegion->Sub(*aVisibleRegion, opaqueArea);
    } else {
      aVisibleRegion->SimpleSubtract(opaqueArea);
    }
    aBuilder->SubtractFromVisibleRegion(aVisibleRegion, nsRegion(opaqueArea));
  }

  return PR_TRUE;
@@ -304,6 +331,12 @@ nsDisplayList::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
  nsAutoTArray<nsDisplayItem*, 512> elements;
  FlattenTo(&elements);

  // Accumulate the bounds of all moving content we find in this list
  nsRect movingContentAccumulatedBounds;
  // Store an overapproximation of the visible region for the moving
  // content in this list
  nsRegion movingContentVisibleRegion;

  for (PRInt32 i = elements.Length() - 1; i >= 0; --i) {
    nsDisplayItem* item = elements[i];
    nsDisplayItem* belowItem = i < 1 ? nsnull : elements[i - 1];
@@ -314,12 +347,25 @@ nsDisplayList::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
      continue;
    }

    nsIFrame* f = item->GetUnderlyingFrame();
    if (f && aBuilder->IsMovingFrame(f)) {
      if (movingContentAccumulatedBounds.IsEmpty()) {
        // *aVisibleRegion can only shrink during this loop, so storing
        // the first one we see is a sound overapproximation
        movingContentVisibleRegion = *aVisibleRegion;
      }
      movingContentAccumulatedBounds.UnionRect(movingContentAccumulatedBounds,
                                               item->GetBounds(aBuilder));
    }
    if (item->OptimizeVisibility(aBuilder, aVisibleRegion)) {
      AppendToBottom(item);
    } else {
      item->~nsDisplayItem();
    }
  }

  aBuilder->AccumulateVisibleRegionOfMovingContent(
      nsRegion(movingContentAccumulatedBounds), movingContentVisibleRegion);
}

void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
@@ -835,7 +881,7 @@ void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder,
static nsresult
WrapDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
  if (!aList->GetTop())
  if (!aList->GetTop() && !aBuilder->HasMovingFrames())
    return NS_OK;
  nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
  if (!item)
@@ -1024,15 +1070,25 @@ PRBool nsDisplayClip::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
                                         nsRegion* aVisibleRegion) {
  nsRegion clipped;
  clipped.And(*aVisibleRegion, mClip);

  if (aBuilder->HasMovingFrames() &&
      !aBuilder->IsMovingFrame(mClippingFrame)) {
    // There may be some clipped moving children that were visible before
    // but are clipped out now. Conservatively assume they were there
    // and add their possible area to the visible region of moving
    // content.
    // Compute the after-move region of moving content that could have been
    // totally clipped out.
    nsRegion r;
    r.Sub(mClip + aBuilder->GetMoveDelta(), mClip);
    aBuilder->AccumulateVisibleRegionOfMovingContent(r, *aVisibleRegion);
  }

  nsRegion rNew(clipped);
  PRBool anyVisible = nsDisplayWrapList::OptimizeVisibility(aBuilder, &rNew);
  nsRegion subtracted;
  subtracted.Sub(clipped, rNew);
  if (aBuilder->GetAccurateVisibleRegions()) {
    aVisibleRegion->Sub(*aVisibleRegion, subtracted);
  } else {
    aVisibleRegion->SimpleSubtract(subtracted);
  }
  aBuilder->SubtractFromVisibleRegion(aVisibleRegion, subtracted);
  return anyVisible;
}

+25 −1
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ class nsRegion;
class nsIRenderingContext;
class nsIDeviceContext;
class nsDisplayTableItem;
class nsDisplayItem;

/*
 * An nsIFrame can have many different visual parts. For example an image frame
@@ -160,10 +161,15 @@ public:
   * cover (during OptimizeVisibility) non-moving frames. E.g. when we're
   * constructing a display list to see what should be repainted during a
   * scroll operation, we specify the scrolled frame as the moving frame.
   * @param aSaveVisibleRegionOfMovingContent if non-null,
   *   this receives a bounding region for the visible moving content
   * (considering the moving content both before and after the move)
   */
  void SetMovingFrame(nsIFrame* aMovingFrame, const nsPoint& aMoveDelta) {
  void SetMovingFrame(nsIFrame* aMovingFrame, const nsPoint& aMoveDelta,
                      nsRegion* aSaveVisibleRegionOfMovingContent) {
    mMovingFrame = aMovingFrame;
    mMoveDelta = aMoveDelta;
    mSaveVisibleRegionOfMovingContent = aSaveVisibleRegionOfMovingContent;
  }

  /**
@@ -179,6 +185,14 @@ public:
   * Only valid when GetRootMovingFrame() returns non-null.
   */
  const nsPoint& GetMoveDelta() { return mMoveDelta; }
  /**
   * Given the bounds of some moving content, and a visible region,
   * intersect the bounds with the visible region and add it to the
   * recorded region of visible moving content.
   */
  void AccumulateVisibleRegionOfMovingContent(const nsRegion& aMovingContent,
                                              const nsRegion& aVisibleRegion);

  /**
   * @return PR_TRUE if aFrame is, or is a descendant of, the hypothetical
   * moving frame
@@ -276,6 +290,15 @@ public:
   */
  void SetInTransform(PRBool aInTransform) { mInTransform = aInTransform; }

  /**
   * Subtracts aRegion from *aVisibleRegion. We avoid letting
   * aVisibleRegion become overcomplex by simplifying it if necessary ---
   * unless mAccurateVisibleRegions is set, in which case we let it
   * get arbitrarily complex.
   */
  void SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
                                 const nsRegion& aRegion);

  /**
   * Mark the frames in aFrames to be displayed if they intersect aDirtyRect
   * (which is relative to aDirtyFrame). If the frames have placeholders
@@ -356,6 +379,7 @@ private:
  
  nsIFrame*                      mReferenceFrame;
  nsIFrame*                      mMovingFrame;
  nsRegion*                      mSaveVisibleRegionOfMovingContent;
  nsIFrame*                      mIgnoreScrollFrame;
  nsPoint                        mMoveDelta; // only valid when mMovingFrame is non-null
  PLArenaPool                    mPool;
+28 −17
Original line number Diff line number Diff line
@@ -1135,12 +1135,12 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra
}

static void
AccumulateItemInRegion(nsRegion* aRegion, const nsRect& aAreaRect,
AccumulateItemInRegion(nsRegion* aRegion, const nsRect& aUpdateRect,
                       const nsRect& aItemRect, const nsRect& aExclude,
                       nsDisplayItem* aItem)
{
  nsRect damageRect;
  if (damageRect.IntersectRect(aAreaRect, aItemRect)) {
  if (damageRect.IntersectRect(aUpdateRect, aItemRect)) {
    nsRegion r;
    r.Sub(damageRect, aExclude);
#ifdef DEBUG
@@ -1157,7 +1157,7 @@ AccumulateItemInRegion(nsRegion* aRegion, const nsRect& aAreaRect,

static void
AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
                 const nsRect& aRect, const nsRect& aClipRect, nsPoint aDelta,
                 const nsRect& aUpdateRect, const nsRect& aClipRect, nsPoint aDelta,
                 nsRegion* aRegion)
{
  for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
@@ -1186,19 +1186,19 @@ AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,

          // Invalidate the translation of the source area that was clipped out
          nsRegion clippedOutSource;
          clippedOutSource.Sub(aRect, clip);
          clippedOutSource.Sub(aUpdateRect - aDelta, clip);
          clippedOutSource.MoveBy(aDelta);
          aRegion->Or(*aRegion, clippedOutSource);

          // Invalidate the destination area that is clipped out
          nsRegion clippedOutDestination;
          clippedOutDestination.Sub(aRect + aDelta, clip);
          clippedOutDestination.Sub(aUpdateRect, clip);
          aRegion->Or(*aRegion, clippedOutDestination);
        }
        AddItemsToRegion(aBuilder, sublist, aRect, clip, aDelta, aRegion);
        AddItemsToRegion(aBuilder, sublist, aUpdateRect, clip, aDelta, aRegion);
      } else {
        // opacity, or a generic sublist
        AddItemsToRegion(aBuilder, sublist, aRect, aClipRect, aDelta, aRegion);
        AddItemsToRegion(aBuilder, sublist, aUpdateRect, aClipRect, aDelta, aRegion);
      }
    } else {
      nsRect r;
@@ -1210,7 +1210,7 @@ AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
          if (item->IsVaryingRelativeToMovingFrame(aBuilder)) {
            // something like background-attachment:fixed that varies
            // its drawing when it moves
            AccumulateItemInRegion(aRegion, aRect + aDelta, r, exclude, item);
            AccumulateItemInRegion(aRegion, aUpdateRect, r, exclude, item);
          }
        } else {
          // not moving.
@@ -1220,11 +1220,11 @@ AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
            exclude.IntersectRect(r, r + aDelta);
          }
          // area where a non-moving element is visible must be repainted
          AccumulateItemInRegion(aRegion, aRect + aDelta, r, exclude, item);
          AccumulateItemInRegion(aRegion, aUpdateRect, r, exclude, item);
          // we may have bitblitted an area that was painted by a non-moving
          // element. This bitblitted data is invalid and was copied to
          // "r + aDelta".
          AccumulateItemInRegion(aRegion, aRect + aDelta, r + aDelta,
          AccumulateItemInRegion(aRegion, aUpdateRect, r + aDelta,
                                 exclude, item);
        }
      }
@@ -1236,7 +1236,8 @@ nsresult
nsLayoutUtils::ComputeRepaintRegionForCopy(nsIFrame* aRootFrame,
                                           nsIFrame* aMovingFrame,
                                           nsPoint aDelta,
                                           const nsRect& aCopyRect,
                                           const nsRect& aUpdateRect,
                                           nsRegion* aBlitRegion,
                                           nsRegion* aRepaintRegion)
{
  NS_ASSERTION(aRootFrame != aMovingFrame,
@@ -1257,9 +1258,12 @@ nsLayoutUtils::ComputeRepaintRegionForCopy(nsIFrame* aRootFrame,
  // XXX but currently a non-moving clip item can incorrectly clip
  // moving items! See bug 428156.
  nsRect rect;
  rect.UnionRect(aCopyRect, aCopyRect + aDelta);
  rect.UnionRect(aUpdateRect, aUpdateRect - aDelta);
  nsDisplayListBuilder builder(aRootFrame, PR_FALSE, PR_TRUE);
  builder.SetMovingFrame(aMovingFrame, aDelta);
  // Retrieve the area of the moving content that's visible. This is the
  // only area that needs to be blitted or repainted.
  nsRegion visibleRegionOfMovingContent;
  builder.SetMovingFrame(aMovingFrame, aDelta, &visibleRegionOfMovingContent);
  nsDisplayList list;

  builder.EnterPresShell(aRootFrame, rect);
@@ -1281,8 +1285,8 @@ nsLayoutUtils::ComputeRepaintRegionForCopy(nsIFrame* aRootFrame,

  // Optimize for visibility, but frames under aMovingFrame will not be
  // considered opaque, so they don't cover non-moving frames.
  nsRegion visibleRegion(aCopyRect);
  visibleRegion.Or(visibleRegion, aCopyRect + aDelta);
  nsRegion visibleRegion(aUpdateRect);
  visibleRegion.Or(visibleRegion, aUpdateRect - aDelta);
  list.OptimizeVisibility(&builder, &visibleRegion);

#ifdef DEBUG
@@ -1297,14 +1301,21 @@ nsLayoutUtils::ComputeRepaintRegionForCopy(nsIFrame* aRootFrame,
  // a) at their current location and b) offset by -aPt (their position in
  // the 'before' display list) (unless they're uniform and we can exclude them).
  // Also, any visible position-varying display items get added to the
  // repaint region. All these areas are confined to aCopyRect+aDelta.
  // repaint region. All these areas are confined to aUpdateRect.
  // We could do more work here: e.g., do another optimize-visibility pass
  // with the moving items taken into account, either on the before-list
  // or the after-list, or even both if we cloned the display lists ... but
  // it's probably not worth it.
  AddItemsToRegion(&builder, &list, aCopyRect, rect, aDelta, aRepaintRegion);
  AddItemsToRegion(&builder, &list, aUpdateRect, rect, aDelta, aRepaintRegion);
  // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
  list.DeleteAll();

  // Finalize output regions. The region of moving content that's not
  // visible --- hidden by overlaid opaque non-moving content --- need not
  // be blitted or repainted.
  visibleRegionOfMovingContent.And(visibleRegionOfMovingContent, aUpdateRect);
  aRepaintRegion->And(*aRepaintRegion, visibleRegionOfMovingContent);
  aBlitRegion->Sub(visibleRegionOfMovingContent, *aRepaintRegion);
  return NS_OK;
}

+27 −20
Original line number Diff line number Diff line
@@ -497,20 +497,21 @@ public:
  /**
   * @param aRootFrame the root frame of the tree to be displayed
   * @param aMovingFrame a frame that has moved
   * @param aPt the amount by which aMovingFrame has moved and the rect will
   * be copied
   * @param aCopyRect a rectangle that will be copied, relative to aRootFrame
   * @param aRepaintRegion a subregion of aCopyRect+aDelta that must be repainted
   * after doing the bitblt
   * @param aPt the amount by which aMovingFrame has moved
   * @param aUpdateRect a rectangle that bounds the area to be updated,
   * relative to aRootFrame
   * @param aRepaintRegion output: a subregion of aUpdateRect that must be
   * repainted after doing the blit
   * @param aBlitRegion output: a subregion of aUpdateRect that should
   * be repainted by blitting
   * 
   * Ideally this function would actually have the rect-to-copy as an output
   * rather than an input, but for now, scroll bitblitting is limited to
   * the whole of a single widget, so we cannot choose the rect.
   * If the caller does a bitblt copy of aBlitRegion-aPt to aBlitRegion,
   * and then repaints aRepaintRegion, then the area aUpdateRect will be
   * correctly up to date. aBlitRegion and aRepaintRegion do not intersect
   * and are both contained within aUpdateRect.
   * 
   * This function assumes that the caller will do a bitblt copy of aCopyRect
   * to aCopyRect+aPt. It computes a region that must be repainted in order
   * for the resulting rendering to be correct. Frame geometry must have
   * already been adjusted for the scroll/copy operation.
   * Frame geometry must have already been adjusted for the scroll/copy
   * operation before this function is called.
   * 
   * Conceptually it works by computing a display list in the before-state
   * and a display list in the after-state and analyzing them to find the
@@ -519,25 +520,31 @@ public:
   * efficient), so we use some unfortunately tricky techniques to get by
   * with just the after-list.
   * 
   * The output region consists of:
   * We compute the "visible moving area": aUpdateRect minus any opaque
   * areas of non-moving content that are above all moving content in
   * z-order.
   *
   * The aRepaintRegion region consists of the visible moving area
   * intersected with the union of the following areas:
   * a) any visible background-attachment:fixed areas in the after-move display
   * list
   * b) any visible areas of the before-move display list corresponding to
   * frames that will not move (translated by aDelta)
   * c) any visible areas of the after-move display list corresponding to
   * frames that did not move
   * d) except that if the same display list element is visible in b) and c)
   * for a frame that did not move and paints a uniform color within its
   * bounds, then the intersection of its old and new bounds can be excluded
   * when it is processed by b) and c).
   * 
   * We may return a larger region if computing the above region precisely is
   * too expensive.
   * aBlitRegion is the visible moving area minus aRepaintRegion.
   * 
   * We may return a larger region for aRepaintRegion and/or aBlitRegion
   * if computing the above regions precisely is too expensive.  (However,
   * they will never intersect, since the regions that may be computed
   * imprecisely are really the "visible moving area" and aRepaintRegion.)
   */
  static nsresult ComputeRepaintRegionForCopy(nsIFrame* aRootFrame,
                                              nsIFrame* aMovingFrame,
                                              nsPoint aDelta,
                                              const nsRect& aCopyRect,
                                              const nsRect& aUpdateRect,
                                              nsRegion* aBlitRegion,
                                              nsRegion* aRepaintRegion);

  /**
+5 −3
Original line number Diff line number Diff line
@@ -913,7 +913,8 @@ public:
  NS_IMETHOD ComputeRepaintRegionForCopy(nsIView*      aRootView,
                                         nsIView*      aMovingView,
                                         nsPoint       aDelta,
                                         const nsRect& aCopyRect,
                                         const nsRect& aUpdateRect,
                                         nsRegion*     aBlitRegion,
                                         nsRegion*     aRepaintRegion);
  NS_IMETHOD HandleEvent(nsIView*        aView,
                         nsGUIEvent*     aEvent,
@@ -5224,13 +5225,14 @@ NS_IMETHODIMP
PresShell::ComputeRepaintRegionForCopy(nsIView*      aRootView,
                                       nsIView*      aMovingView,
                                       nsPoint       aDelta,
                                       const nsRect& aCopyRect,
                                       const nsRect& aUpdateRect,
                                       nsRegion*     aBlitRegion,
                                       nsRegion*     aRepaintRegion)
{
  return nsLayoutUtils::ComputeRepaintRegionForCopy(
      static_cast<nsIFrame*>(aRootView->GetClientData()),
      static_cast<nsIFrame*>(aMovingView->GetClientData()),
      aDelta, aCopyRect, aRepaintRegion);
      aDelta, aUpdateRect, aBlitRegion, aRepaintRegion);
}

NS_IMETHODIMP
Loading