Commit 1862089a authored by Miko Mynttinen's avatar Miko Mynttinen
Browse files

Bug 1428993 - Part 1: Split RetainedDisplayListBuilder::ComputeRebuildRegion()...

Bug 1428993 - Part 1: Split RetainedDisplayListBuilder::ComputeRebuildRegion() and PreProcessDisplayList() into multiple functions r=mattwoodrow

MozReview-Commit-ID: 77DntJk53q2
parent 7e8cf134
Loading
Loading
Loading
Loading
+142 −114
Original line number Original line Diff line number Diff line
@@ -77,6 +77,24 @@ bool IsAnyAncestorModified(nsIFrame* aFrame)
  return false;
  return false;
}
}


static AnimatedGeometryRoot*
SelectAGRForFrame(nsIFrame* aFrame, AnimatedGeometryRoot* aParentAGR)
{
  if (!aFrame->IsStackingContext()) {
    return aParentAGR;
  }

  if (!aFrame->HasOverrideDirtyRegion()) {
    return nullptr;
  }

  nsDisplayListBuilder::DisplayListBuildingData* data =
    aFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());

  return data && data->mModifiedAGR ? data->mModifiedAGR.get()
                                    : nullptr;
}

// Removes any display items that belonged to a frame that was deleted,
// Removes any display items that belonged to a frame that was deleted,
// and mark frames that belong to a different AGR so that get their
// and mark frames that belong to a different AGR so that get their
// items built again.
// items built again.
@@ -97,19 +115,7 @@ RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
    nsIFrame* f = i->Frame();
    nsIFrame* f = i->Frame();


    if (i->GetChildren()) {
    if (i->GetChildren()) {
      AnimatedGeometryRoot *childAGR = aAGR;
      PreProcessDisplayList(i->GetChildren(), SelectAGRForFrame(f, aAGR));
      if (f->IsStackingContext()) {
        if (f->HasOverrideDirtyRegion()) {
          nsDisplayListBuilder::DisplayListBuildingData* data =
            f->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
          if (data) {
            childAGR = data->mModifiedAGR;
          }
        } else {
          childAGR = nullptr;
        }
      }
      PreProcessDisplayList(i->GetChildren(), childAGR);
    }
    }


    // TODO: We should be able to check the clipped bounds relative
    // TODO: We should be able to check the clipped bounds relative
@@ -594,87 +600,41 @@ GetModifiedFrames(nsDisplayListBuilder* aBuilder)
#  define CRR_LOG(...)
#  define CRR_LOG(...)
#endif
#endif


/**
static nsIFrame*
 * Given a list of frames that has been modified, computes the region that we need to
HandlePreserve3D(nsIFrame* aFrame, nsRect& aOverflow)
 * do display list building for in order to build all modified display items.
 *
 * When a modified frame is within a stacking context (with an existing display item),
 * then we only contribute to the build area within the stacking context, as well as forcing
 * display list building to descend to the stacking context. We don't need to add build
 * area outside of the stacking context (and force items above/below the stacking context
 * container item to be built), since just matching the position of the stacking context
 * container item is sufficient to ensure correct ordering during merging.
 *
 * We need to rebuild all items that might intersect with the modified frame, both now
 * and during async changes on the compositor. We do this by rebuilding the area covered
 * by the changed frame, as well as rebuilding all items that have a different (async)
 * AGR to the changed frame. If we have changes to multiple AGRs (within a stacking
 * context), then we rebuild that stacking context entirely.
 *
 * @param aModifiedFrames The list of modified frames.
 * @param aOutDirty The result region to use for display list building.
 * @param aOutModifiedAGR The modified AGR for the root stacking context.
 * @param aOutFramesWithProps The list of frames to which we attached partial build
 * data so that it can be cleaned up.
 *
 * @return true if we succesfully computed a partial rebuild region, false if a full
 * build is required.
 */
bool
RetainedDisplayListBuilder::ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                                                 nsRect* aOutDirty,
                                                 AnimatedGeometryRoot** aOutModifiedAGR,
                                                 nsTArray<nsIFrame*>* aOutFramesWithProps)
{
{
  CRR_LOG("Computing rebuild regions for %d frames:\n", aModifiedFrames.size());
  for (nsIFrame* f : aModifiedFrames) {
    MOZ_ASSERT(f);

    if (f->HasOverrideDirtyRegion()) {
      aOutFramesWithProps->AppendElement(f);
    }

    if (f->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
      continue;
    }

    // TODO: There is almost certainly a faster way of doing this, probably can be combined with the ancestor
    // walk for TransformFrameRectToAncestor.
    AnimatedGeometryRoot* agr = mBuilder.FindAnimatedGeometryRootFor(f)->GetAsyncAGR();

    CRR_LOG("Processing frame %p with agr %p\n", f, agr->mFrame);


    // Convert the frame's overflow rect into the coordinate space
    // of the nearest stacking context that has an existing display item.
    // We store that as a dirty rect on that stacking context so that we build
    // all items that intersect the changed frame within the stacking context,
    // and then we use MarkFrameForDisplayIfVisible to make sure the stacking
    // context itself gets built. We don't need to build items that intersect outside
    // of the stacking context, since we know the stacking context item exists in
    // the old list, so we can trivially merge without needing other items.
    nsRect overflow = f->GetVisualOverflowRectRelativeToSelf();
    nsIFrame* currentFrame = f;

    while (currentFrame != mBuilder.RootReferenceFrame()) {

  // Preserve-3d frames don't have valid overflow areas, and they might
  // Preserve-3d frames don't have valid overflow areas, and they might
  // have singular transforms (despite still being visible when combined
  // have singular transforms (despite still being visible when combined
  // with their ancestors). If we're at one, jump up to the root of the
  // with their ancestors). If we're at one, jump up to the root of the
  // preserve-3d context and use the whole overflow area.
  // preserve-3d context and use the whole overflow area.
      nsIFrame* last = currentFrame;
  nsIFrame* last = aFrame;
      while (currentFrame->Extend3DContext() ||
  while (aFrame->Extend3DContext() ||
             currentFrame->Combines3DTransformWithAncestors()) {
         aFrame->Combines3DTransformWithAncestors()) {
        last = currentFrame;
    last = aFrame;
        currentFrame = currentFrame->GetParent();
    aFrame = aFrame->GetParent();
  }
  }
      if (last != currentFrame) {
  if (last != aFrame) {
        overflow = last->GetVisualOverflowRectRelativeToParent();
    aOverflow = last->GetVisualOverflowRectRelativeToParent();
  }

  return aFrame;
}
}


      // Convert 'overflow' into the coordinate space of the nearest stacking context
static void
ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder& aBuilder,
             AnimatedGeometryRoot** aAGR, nsRect& aOverflow,
             nsIFrame* aStopAtFrame, nsTArray<nsIFrame*>& aOutFramesWithProps,
             const bool /* aStopAtStackingContext */)
{
  nsIFrame* currentFrame = aFrame;

  while (currentFrame != aStopAtFrame) {
    currentFrame = HandlePreserve3D(currentFrame, aOverflow);

    // Convert 'aOverflow' into the coordinate space of the nearest stacking context
    // or display port ancestor and update 'currentFrame' to point to that frame.
    // or display port ancestor and update 'currentFrame' to point to that frame.
      overflow = nsLayoutUtils::TransformFrameRectToAncestor(currentFrame, overflow, mBuilder.RootReferenceFrame(),
    aOverflow = nsLayoutUtils::TransformFrameRectToAncestor(currentFrame, aOverflow,
                                                            aStopAtFrame,
                                                            nullptr, nullptr,
                                                            nullptr, nullptr,
                                                            /* aStopAtStackingContextAndDisplayPort = */ true,
                                                            /* aStopAtStackingContextAndDisplayPort = */ true,
                                                            &currentFrame);
                                                            &currentFrame);
@@ -686,10 +646,11 @@ RetainedDisplayListBuilder::ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedF
      MOZ_ASSERT(sf);
      MOZ_ASSERT(sf);
      nsRect displayPort;
      nsRect displayPort;
      DebugOnly<bool> hasDisplayPort =
      DebugOnly<bool> hasDisplayPort =
          nsLayoutUtils::GetDisplayPort(currentFrame->GetContent(), &displayPort, RelativeTo::ScrollPort);
        nsLayoutUtils::GetDisplayPort(currentFrame->GetContent(), &displayPort,
                                      RelativeTo::ScrollPort);
      MOZ_ASSERT(hasDisplayPort);
      MOZ_ASSERT(hasDisplayPort);
      // get it relative to the scrollport (from the scrollframe)
      // get it relative to the scrollport (from the scrollframe)
        nsRect r = overflow - sf->GetScrollPortRect().TopLeft();
      nsRect r = aOverflow - sf->GetScrollPortRect().TopLeft();
      r.IntersectRect(r, displayPort);
      r.IntersectRect(r, displayPort);
      if (!r.IsEmpty()) {
      if (!r.IsEmpty()) {
        nsRect* rect =
        nsRect* rect =
@@ -698,18 +659,18 @@ RetainedDisplayListBuilder::ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedF
          rect = new nsRect();
          rect = new nsRect();
          currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
          currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
          currentFrame->SetHasOverrideDirtyRegion(true);
          currentFrame->SetHasOverrideDirtyRegion(true);
          aOutFramesWithProps.AppendElement(currentFrame);
        }
        }
        rect->UnionRect(*rect, r);
        rect->UnionRect(*rect, r);
          aOutFramesWithProps->AppendElement(currentFrame);
        CRR_LOG("Adding area to displayport draw area: %d %d %d %d\n", r.x, r.y, r.width, r.height);
        CRR_LOG("Adding area to displayport draw area: %d %d %d %d\n", r.x, r.y, r.width, r.height);


        // TODO: Can we just use MarkFrameForDisplayIfVisible, plus MarkFramesForDifferentAGR to
        // TODO: Can we just use MarkFrameForDisplayIfVisible, plus MarkFramesForDifferentAGR to
        // ensure that this displayport, plus any items that move relative to it get rebuilt,
        // ensure that this displayport, plus any items that move relative to it get rebuilt,
        // and then not contribute to the root dirty area?
        // and then not contribute to the root dirty area?
          overflow = sf->GetScrollPortRect();
        aOverflow = sf->GetScrollPortRect();
      } else {
      } else {
        // Don't contribute to the root dirty area at all.
        // Don't contribute to the root dirty area at all.
          overflow.SetEmpty();
        aOverflow.SetEmpty();
        break;
        break;
      }
      }
    }
    }
@@ -719,9 +680,10 @@ RetainedDisplayListBuilder::ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedF
      // If we found an intermediate stacking context with an existing display item
      // If we found an intermediate stacking context with an existing display item
      // then we can store the dirty rect there and stop. If we couldn't find one then
      // then we can store the dirty rect there and stop. If we couldn't find one then
      // we need to keep bubbling up to the next stacking context.
      // we need to keep bubbling up to the next stacking context.
        if (currentFrame != mBuilder.RootReferenceFrame() &&
      if (currentFrame != aBuilder.RootReferenceFrame() &&
          currentFrame->HasDisplayItems()) {
          currentFrame->HasDisplayItems()) {
          mBuilder.MarkFrameForDisplayIfVisible(currentFrame, mBuilder.RootReferenceFrame());
        aBuilder.MarkFrameForDisplayIfVisible(currentFrame,
                                              aBuilder.RootReferenceFrame());


        // Store the stacking context relative dirty area such
        // Store the stacking context relative dirty area such
        // that display list building will pick it up when it
        // that display list building will pick it up when it
@@ -732,31 +694,97 @@ RetainedDisplayListBuilder::ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedF
          data = new nsDisplayListBuilder::DisplayListBuildingData;
          data = new nsDisplayListBuilder::DisplayListBuildingData;
          currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingRect(), data);
          currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingRect(), data);
          currentFrame->SetHasOverrideDirtyRegion(true);
          currentFrame->SetHasOverrideDirtyRegion(true);
            aOutFramesWithProps->AppendElement(currentFrame);
          aOutFramesWithProps.AppendElement(currentFrame);
        }
        }
          data->mDirtyRect.UnionRect(data->mDirtyRect, overflow);
        data->mDirtyRect.UnionRect(data->mDirtyRect, aOverflow);
        CRR_LOG("Adding area to stacking context draw area: %d %d %d %d\n",
        CRR_LOG("Adding area to stacking context draw area: %d %d %d %d\n",
                  overflow.x, overflow.y, overflow.width, overflow.height);
                aOverflow.x, aOverflow.y, aOverflow.width, aOverflow.height);
        if (!data->mModifiedAGR) {
        if (!data->mModifiedAGR) {
            data->mModifiedAGR = agr;
          data->mModifiedAGR = *aAGR;
          } else if (data->mModifiedAGR != agr) {
        } else if (data->mModifiedAGR != *aAGR) {
          data->mDirtyRect = currentFrame->GetVisualOverflowRectRelativeToSelf();
          data->mDirtyRect = currentFrame->GetVisualOverflowRectRelativeToSelf();
          CRR_LOG("Found multiple modified AGRs within this stacking context, giving up\n");
          CRR_LOG("Found multiple modified AGRs within this stacking context, giving up\n");
        }
        }


        // Don't contribute to the root dirty area at all.
        // Don't contribute to the root dirty area at all.
          agr = nullptr;
        *aAGR = nullptr;
          overflow.SetEmpty();
        aOverflow.SetEmpty();
        break;
        break;
      }
      }
    }
    }
  }
  }
}
/**
 * Given a list of frames that has been modified, computes the region that we need to
 * do display list building for in order to build all modified display items.
 *
 * When a modified frame is within a stacking context (with an existing display item),
 * then we only contribute to the build area within the stacking context, as well as forcing
 * display list building to descend to the stacking context. We don't need to add build
 * area outside of the stacking context (and force items above/below the stacking context
 * container item to be built), since just matching the position of the stacking context
 * container item is sufficient to ensure correct ordering during merging.
 *
 * We need to rebuild all items that might intersect with the modified frame, both now
 * and during async changes on the compositor. We do this by rebuilding the area covered
 * by the changed frame, as well as rebuilding all items that have a different (async)
 * AGR to the changed frame. If we have changes to multiple AGRs (within a stacking
 * context), then we rebuild that stacking context entirely.
 *
 * @param aModifiedFrames The list of modified frames.
 * @param aOutDirty The result region to use for display list building.
 * @param aOutModifiedAGR The modified AGR for the root stacking context.
 * @param aOutFramesWithProps The list of frames to which we attached partial build
 * data so that it can be cleaned up.
 *
 * @return true if we succesfully computed a partial rebuild region, false if a full
 * build is required.
 */
bool
RetainedDisplayListBuilder::ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                                                 nsRect* aOutDirty,
                                                 AnimatedGeometryRoot** aOutModifiedAGR,
                                                 nsTArray<nsIFrame*>& aOutFramesWithProps)
{
  CRR_LOG("Computing rebuild regions for %zu frames:\n", aModifiedFrames.Length());
  for (nsIFrame* f : aModifiedFrames) {
    MOZ_ASSERT(f);

    if (f->HasOverrideDirtyRegion()) {
      aOutFramesWithProps.AppendElement(f);
    }

    if (f->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
      continue;
    }

    // TODO: There is almost certainly a faster way of doing this, probably can be combined with the ancestor
    // walk for TransformFrameRectToAncestor.
    AnimatedGeometryRoot* agr = mBuilder.FindAnimatedGeometryRootFor(f)->GetAsyncAGR();

    CRR_LOG("Processing frame %p with agr %p\n", f, agr->mFrame);

    // Convert the frame's overflow rect into the coordinate space
    // of the nearest stacking context that has an existing display item.
    // We store that as a dirty rect on that stacking context so that we build
    // all items that intersect the changed frame within the stacking context,
    // and then we use MarkFrameForDisplayIfVisible to make sure the stacking
    // context itself gets built. We don't need to build items that intersect outside
    // of the stacking context, since we know the stacking context item exists in
    // the old list, so we can trivially merge without needing other items.
    nsRect overflow = f->GetVisualOverflowRectRelativeToSelf();

    ProcessFrame(f, mBuilder, &agr, overflow, mBuilder.RootReferenceFrame(),
                 aOutFramesWithProps, true);

    aOutDirty->UnionRect(*aOutDirty, overflow);
    aOutDirty->UnionRect(*aOutDirty, overflow);
    CRR_LOG("Adding area to root draw area: %d %d %d %d\n", overflow.x, overflow.y, overflow.width, overflow.height);
    CRR_LOG("Adding area to root draw area: %d %d %d %d\n",
            overflow.x, overflow.y, overflow.width, overflow.height);


    // If we get changed frames from multiple AGRS, then just give up as it gets really complex to
    // If we get changed frames from multiple AGRS, then just give up as it gets really complex to
    // track which items would need to be marked in MarkFramesForDifferentAGR.
    // track which items would need to be marked in MarkFramesForDifferentAGR.
    if (!*aOutModifiedAGR) {
    if (!*aOutModifiedAGR) {
      CRR_LOG("Setting %p as root stacking context AGR\n", agr);
      *aOutModifiedAGR = agr;
      *aOutModifiedAGR = agr;
    } else if (agr && *aOutModifiedAGR != agr) {
    } else if (agr && *aOutModifiedAGR != agr) {
      CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
      CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
@@ -861,7 +889,7 @@ RetainedDisplayListBuilder::AttemptPartialUpdate(nscolor aBackstop)
  bool merged = false;
  bool merged = false;
  if (shouldBuildPartial &&
  if (shouldBuildPartial &&
      ComputeRebuildRegion(modifiedFrames, &modifiedDirty,
      ComputeRebuildRegion(modifiedFrames, &modifiedDirty,
                           &modifiedAGR, &framesWithProps)) {
                           &modifiedAGR, framesWithProps)) {
    modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());
    modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());


    PreProcessDisplayList(&mList, modifiedAGR);
    PreProcessDisplayList(&mList, modifiedAGR);
+1 −1
Original line number Original line Diff line number Diff line
@@ -48,7 +48,7 @@ private:
  bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
  bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                            nsRect* aOutDirty,
                            nsRect* aOutDirty,
                            AnimatedGeometryRoot** aOutModifiedAGR,
                            AnimatedGeometryRoot** aOutModifiedAGR,
                            nsTArray<nsIFrame*>* aOutFramesWithProps);
                            nsTArray<nsIFrame*>& aOutFramesWithProps);


  void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
  void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);