Bug 1460491 - Part 2: Only recompute visibility for items if they are newly added to this layer, or intersect one that changed. r?jnicol draft
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 16 May 2018 16:02:37 +1200
changeset 795564 ab9f7030233e00f1b6ddec4064ed8712bd18d853
parent 795563 24dea044d82b6d216c2c079d7c157a7877f626f1
push id110015
push usermwoodrow@mozilla.com
push dateWed, 16 May 2018 04:15:10 +0000
reviewersjnicol
bugs1460491
milestone62.0a1
Bug 1460491 - Part 2: Only recompute visibility for items if they are newly added to this layer, or intersect one that changed. r?jnicol MozReview-Commit-ID: EAgvVQGsSPE
gfx/layers/wr/WebRenderCommandBuilder.cpp
layout/painting/FrameLayerBuilder.cpp
layout/painting/FrameLayerBuilder.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1320,16 +1320,17 @@ WebRenderCommandBuilder::CreateWebRender
       }/* else if (itemType == DisplayItemType::TYPE_FOREIGN_OBJECT) {
         // We do not want to apply grouping inside <foreignObject>.
         // TODO: TYPE_FOREIGN_OBJECT does not exist yet, make it exist
         mDoGrouping = false;
       }*/
 
       // Note: this call to CreateWebRenderCommands can recurse back into
       // this function if the |item| is a wrapper for a sublist.
+      item->SetPaintRect(item->GetBuildingRect());
       bool createdWRCommands =
         item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
                                       aDisplayListBuilder);
       if (!createdWRCommands) {
         PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
       }
     }
 
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -285,20 +285,21 @@ DisplayItemData::EndUpdate(nsAutoPtr<nsD
   mChangedFrameInvalidations.SetEmpty();
 
   mItem = nullptr;
   EndUpdate();
 }
 
 void
 DisplayItemData::BeginUpdate(Layer* aLayer, LayerState aState,
+                             bool aFirstUpdate,
                              nsDisplayItem* aItem /* = nullptr */)
 {
   BeginUpdate(aLayer, aState, aItem,
-              aItem ? aItem->IsReused() : false,
+              (aItem && !aFirstUpdate) ? aItem->IsReused() : false,
               aItem ? aItem->HasMergedFrames() : false);
 }
 
 void
 DisplayItemData::BeginUpdate(Layer* aLayer, LayerState aState,
                              nsDisplayItem* aItem, bool aIsReused,
                              bool aIsMerged)
 {
@@ -569,16 +570,17 @@ public:
    * need to update our regions.
    * @param aVisibleRect the area of the item that's visible
    * @param aSolidColor if non-null, the visible area of the item is
    * a constant color given by *aSolidColor
    */
   void Accumulate(ContainerState* aState,
                   nsDisplayItem* aItem,
                   const nsIntRect& aVisibleRect,
+                  const nsRect& aContentRect,
                   const DisplayItemClip& aClip,
                   LayerState aLayerState,
                   nsDisplayList *aList,
                   DisplayItemEntryType aType);
   AnimatedGeometryRoot* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; }
 
   /**
    * A region including the horizontal pan, vertical pan, and no action regions.
@@ -1691,16 +1693,20 @@ public:
 
   // The region for which display item visibility for this layer has already
   // been calculated. Used to reduce the number of calls to
   // RecomputeVisibilityForItems if it is known in advance that a larger
   // region will be painted during a transaction than in a single call to
   // DrawPaintedLayer, for example when progressive paint is enabled.
   nsIntRegion mVisibilityComputedRegion;
 
+  // The area for which we called RecomputeVisibilityForItems on the
+  // previous paint.
+  nsRect mPreviousRecomputeVisibilityRect;
+
   // The number of items assigned to this layer on the previous paint.
   size_t mLastItemCount;
 
   // The translation set on this PaintedLayer before we started updating the
   // layer tree.
   nsIntPoint mLastPaintOffset;
 
   // Temporary state only valid during the FrameLayerBuilder's lifetime.
@@ -3607,16 +3613,17 @@ IsItemAreaInWindowOpaqueRegion(nsDisplay
   }
   return aBuilder->GetWindowOpaqueRegion().Contains(aComponentAlphaBounds);
 }
 
 void
 PaintedLayerData::Accumulate(ContainerState* aState,
                              nsDisplayItem* aItem,
                              const nsIntRect& aVisibleRect,
+                             const nsRect& aContentRect,
                              const DisplayItemClip& aClip,
                              LayerState aLayerState,
                              nsDisplayList* aList,
                              DisplayItemEntryType aType)
 {
   FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating dp=%s(%p), f=%p against pld=%p\n", aItem->Name(), aItem, aItem->Frame(), this);
 
   const bool hasOpacity = mOpacityIndices.Length() > 0;
@@ -3624,17 +3631,17 @@ PaintedLayerData::Accumulate(ContainerSt
   const DisplayItemClip* oldClip = mItemClip;
   mItemClip = &aClip;
 
   if (aType == DisplayItemEntryType::POP_OPACITY) {
     MOZ_ASSERT(!mOpacityIndices.IsEmpty());
     mOpacityIndices.RemoveLastElement();
 
     AssignedDisplayItem item(aItem, aLayerState,
-                             nullptr, aType, hasOpacity);
+                             nullptr, aContentRect, aType, hasOpacity);
     mAssignedDisplayItems.AppendElement(Move(item));
     return;
   }
 
   if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
     mForceTransparentSurface = true;
   }
 
@@ -3663,17 +3670,17 @@ PaintedLayerData::Accumulate(ContainerSt
   DisplayItemData* currentData =
     aItem->HasMergedFrames() ? nullptr : aItem->GetDisplayItemData();
 
   DisplayItemData* oldData =
     aState->mLayerBuilder->GetOldLayerForFrame(aItem->Frame(),
                                                aItem->GetPerFrameKey(),
                                                currentData);
   AssignedDisplayItem item(aItem, aLayerState,
-                           oldData, aType, hasOpacity);
+                           oldData, aContentRect, aType, hasOpacity);
   mAssignedDisplayItems.AppendElement(Move(item));
 
   if (aType == DisplayItemEntryType::PUSH_OPACITY) {
     mOpacityIndices.AppendElement(mAssignedDisplayItems.Length() - 1);
   }
 
   if (aItem->MustPaintOnContentSide()) {
      mShouldPaintOnContentSide = true;
@@ -4837,17 +4844,17 @@ ContainerState::ProcessDisplayItems(nsDi
         nsDisplayLayerEventRegions* eventRegions =
           static_cast<nsDisplayLayerEventRegions*>(item);
         paintedLayerData->AccumulateEventRegions(this, eventRegions);
       } else if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
         nsDisplayCompositorHitTestInfo* hitTestInfo =
           static_cast<nsDisplayCompositorHitTestInfo*>(item);
         paintedLayerData->AccumulateHitTestInfo(this, hitTestInfo);
       } else {
-        paintedLayerData->Accumulate(this, item, itemVisibleRect, itemClip,
+        paintedLayerData->Accumulate(this, item, itemVisibleRect, itemContent, itemClip,
                                      layerState, aList, marker);
 
         if (!paintedLayerData->mLayer) {
           // Try to recycle the old layer of this display item.
           RefPtr<PaintedLayer> layer =
             AttemptToRecyclePaintedLayer(animatedGeometryRoot, item,
                                          topLeft, referenceFrame);
           if (layer) {
@@ -5163,72 +5170,75 @@ FrameLayerBuilder::AddPaintedDisplayItem
 }
 
 DisplayItemData*
 FrameLayerBuilder::StoreDataForFrame(nsDisplayItem* aItem, Layer* aLayer,
                                      LayerState aState, DisplayItemData* aData)
 {
   if (aData) {
     if (!aData->mUsed) {
-      aData->BeginUpdate(aLayer, aState, aItem);
+      aData->BeginUpdate(aLayer, aState, false, aItem);
     }
     return aData;
   }
 
   LayerManagerData* lmd = static_cast<LayerManagerData*>
     (mRetainingManager->GetUserData(&gLayerManagerUserData));
 
   RefPtr<DisplayItemData> data =
     new (aItem->Frame()->PresContext()) DisplayItemData(lmd, aItem->GetPerFrameKey(), aLayer);
 
   if (!data->HasMergedFrames()) {
     aItem->SetDisplayItemData(data);
   }
 
-  data->BeginUpdate(aLayer, aState, aItem);
+  data->BeginUpdate(aLayer, aState, true, aItem);
 
   lmd->mDisplayItems.PutEntry(data);
   return data;
 }
 
 void
 FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame,
                                      uint32_t aDisplayItemKey,
                                      Layer* aLayer,
                                      LayerState aState)
 {
   DisplayItemData* oldData = GetDisplayItemData(aFrame, aDisplayItemKey);
   if (oldData && oldData->mFrameList.Length() == 1) {
-    oldData->BeginUpdate(aLayer, aState);
+    oldData->BeginUpdate(aLayer, aState, false);
     return;
   }
 
   LayerManagerData* lmd = static_cast<LayerManagerData*>
     (mRetainingManager->GetUserData(&gLayerManagerUserData));
 
   RefPtr<DisplayItemData> data =
     new (aFrame->PresContext()) DisplayItemData(lmd, aDisplayItemKey, aLayer, aFrame);
 
-  data->BeginUpdate(aLayer, aState);
+  data->BeginUpdate(aLayer, aState, true);
 
   lmd->mDisplayItems.PutEntry(data);
 }
 
 AssignedDisplayItem::AssignedDisplayItem(nsDisplayItem* aItem,
                                          LayerState aLayerState,
                                          DisplayItemData* aData,
+                                         const nsRect& aContentRect,
                                          DisplayItemEntryType aType,
                                          const bool aHasOpacity)
   : mItem(aItem)
   , mLayerState(aLayerState)
   , mDisplayItemData(aData)
+  , mContentRect(aContentRect)
+  , mType(aType)
   , mReused(aItem->IsReused())
   , mMerged(aItem->HasMergedFrames())
-  , mType(aType)
   , mHasOpacity(aHasOpacity)
+  , mHasPaintRect(aItem->HasPaintRect())
 {}
 
 AssignedDisplayItem::~AssignedDisplayItem()
 {
   if (mInactiveLayerManager) {
     mInactiveLayerManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
   }
 }
@@ -6152,16 +6162,17 @@ static void DebugPaintItem(DrawTarget& a
   aItem->SetPainted();
 }
 #endif
 
 /* static */ void
 FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray<AssignedDisplayItem>& aItems,
                                                nsDisplayListBuilder *aBuilder,
                                                const nsIntRegion& aRegionToDraw,
+                                               nsRect& aPreviousRectToDraw,
                                                const nsIntPoint& aOffset,
                                                int32_t aAppUnitsPerDevPixel,
                                                float aXScale,
                                                float aYScale)
 {
   uint32_t i;
   // Update visible regions. We perform visibility analysis to take account
   // of occlusion culling.
@@ -6171,16 +6182,22 @@ FrameLayerBuilder::RecomputeVisibilityFo
   visible.ScaleInverseRoundOut(aXScale, aYScale);
 
   for (i = aItems.Length(); i > 0; --i) {
     AssignedDisplayItem* cdi = &aItems[i - 1];
     if (!cdi->mItem) {
       continue;
     }
 
+    if (cdi->mHasPaintRect &&
+        !cdi->mContentRect.Intersects(visible.GetBounds()) &&
+        !cdi->mContentRect.Intersects(aPreviousRectToDraw)) {
+      continue;
+    }
+
     if (cdi->mType == DisplayItemEntryType::POP_OPACITY ||
         (cdi->mType == DisplayItemEntryType::ITEM && cdi->mHasOpacity)) {
       // The visibility calculations are skipped when the item is an effect end
       // marker, or when the display item is within a flattened opacity group.
       // This is because RecomputeVisibility has already been called for the
       // group item, and all the children.
       continue;
     }
@@ -6212,16 +6229,18 @@ FrameLayerBuilder::RecomputeVisibilityFo
       nsRegion newVisible;
       newVisible.Sub(visible, removed);
       // Don't let the visible region get too complex.
       if (newVisible.GetNumRects() <= 15) {
         visible = Move(newVisible);
       }
     }
   }
+
+  aPreviousRectToDraw = visible.GetBounds();
 }
 
 /**
  * Sets the clip chain and starts a new opacity group.
  */
 static void
 PushOpacity(gfxContext* aContext,
             const nsRect& aPaintRect,
@@ -6527,16 +6546,17 @@ FrameLayerBuilder::DrawPaintedLayer(Pain
       !layerBuilder->GetContainingPaintedLayerData()) {
     // Recompute visibility of items in our PaintedLayer, if required. Note
     // that this recomputes visibility for all descendants of our display
     // items too, so there's no need to do this for the items in inactive
     // PaintedLayers. If aDirtyRegion has not changed since the previous call
     // then we can skip this.
     int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
     RecomputeVisibilityForItems(userData->mItems, builder, aDirtyRegion,
+                                userData->mPreviousRecomputeVisibilityRect,
                                 offset, appUnitsPerDevPixel,
                                 userData->mXScale, userData->mYScale);
     userData->mVisibilityComputedRegion = aDirtyRegion;
   }
 
   if (shouldDrawRectsSeparately) {
     for (auto iter = aRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
       const nsIntRect& iterRect = iter.Get();
--- a/layout/painting/FrameLayerBuilder.h
+++ b/layout/painting/FrameLayerBuilder.h
@@ -157,16 +157,17 @@ private:
     * Updates the contents of this item to a new set of data, instead of allocating a new
     * object.
     * Set the passed in parameters, and clears the opt layer and inactive manager.
     * Parent, and display item key are assumed to be the same.
     *
     * EndUpdate must be called before the end of the transaction to complete the update.
     */
   void BeginUpdate(layers::Layer* aLayer, LayerState aState,
+                   bool aFirstUpdate,
                    nsDisplayItem* aItem = nullptr);
   void BeginUpdate(layers::Layer* aLayer, LayerState aState,
                    nsDisplayItem* aItem, bool aIsReused, bool aIsMerged);
 
   /**
     * Completes the update of this, and removes any references to data that won't live
     * longer than the transaction.
     *
@@ -217,36 +218,38 @@ public:
   bool mIsInfinite;
 };
 
 struct AssignedDisplayItem
 {
   AssignedDisplayItem(nsDisplayItem* aItem,
                       LayerState aLayerState,
                       DisplayItemData* aData,
+                      const nsRect& aContentRect,
                       DisplayItemEntryType aType,
                       const bool aHasOpacity);
   ~AssignedDisplayItem();
 
   nsDisplayItem* mItem;
   LayerState mLayerState;
   DisplayItemData* mDisplayItemData;
+  nsRect mContentRect;
 
   /**
    * If the display item is being rendered as an inactive
    * layer, then this stores the layer manager being
    * used for the inactive transaction.
    */
   RefPtr<layers::LayerManager> mInactiveLayerManager;
 
+  DisplayItemEntryType mType;
   bool mReused;
   bool mMerged;
-
-  DisplayItemEntryType mType;
   bool mHasOpacity;
+  bool mHasPaintRect;
 };
 
 
 struct ContainerLayerParameters {
   ContainerLayerParameters()
     : mXScale(1)
     , mYScale(1)
     , mLayerContentsVisibleRect(nullptr)
@@ -660,16 +663,17 @@ protected:
    * These are only stored during the paint process, so that the
    * DrawPaintedLayer callback can figure out which items to draw for the
    * PaintedLayer.
    */
 
   static void RecomputeVisibilityForItems(nsTArray<AssignedDisplayItem>& aItems,
                                           nsDisplayListBuilder* aBuilder,
                                           const nsIntRegion& aRegionToDraw,
+                                          nsRect& aPreviousRectToDraw,
                                           const nsIntPoint& aOffset,
                                           int32_t aAppUnitsPerDevPixel,
                                           float aXScale,
                                           float aYScale);
 
   void PaintItems(nsTArray<AssignedDisplayItem>& aItems,
                   const nsIntRect& aRect,
                   gfxContext* aContext,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -3091,16 +3091,17 @@ nsDisplayItem::nsDisplayItem(nsDisplayLi
                              const ActiveScrolledRoot* aActiveScrolledRoot)
   : mFrame(aFrame)
   , mActiveScrolledRoot(aActiveScrolledRoot)
   , mAnimatedGeometryRoot(nullptr)
   , mForceNotVisible(aBuilder->IsBuildingInvisibleItems())
   , mDisableSubpixelAA(false)
   , mReusedItem(false)
   , mBackfaceHidden(mFrame->In3DContextAndBackfaceIsHidden())
+  , mPaintRectValid(false)
 #ifdef MOZ_DUMP_PAINTING
   , mPainted(false)
 #endif
 {
   MOZ_COUNT_CTOR(nsDisplayItem);
   if (aBuilder->IsRetainingDisplayList()) {
     mFrame->AddDisplayItem(this);
   }
@@ -9590,17 +9591,17 @@ nsDisplayMask::PaintMask(nsDisplayListBu
 {
   MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
 
   imgDrawingParams imgParmas(aBuilder->ShouldSyncDecodeImages()
                              ? imgIContainer::FLAG_SYNC_DECODE
                              : imgIContainer::FLAG_SYNC_DECODE_IF_FAST);
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   nsSVGIntegrationUtils::PaintFramesParams params(*aMaskContext,
-                                                  mFrame,  GetPaintRect(),
+                                                  mFrame,  GetBuildingRect(),
                                                   borderArea, aBuilder,
                                                   nullptr,
                                                   mHandleOpacity, imgParmas);
   ComputeMaskGeometry(params);
   nsSVGIntegrationUtils::PaintMask(params);
 
   nsDisplayMaskGeometry::UpdateDrawResult(this, imgParmas.result);
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2095,16 +2095,17 @@ public:
     , mClip(nullptr)
     , mActiveScrolledRoot(nullptr)
     , mReferenceFrame(nullptr)
     , mAnimatedGeometryRoot(nullptr)
     , mForceNotVisible(false)
     , mDisableSubpixelAA(false)
     , mReusedItem(false)
     , mBackfaceHidden(mFrame->In3DContextAndBackfaceIsHidden())
+    , mPaintRectValid(false)
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
   {
     MOZ_COUNT_CTOR(nsDisplayItem);
   }
 
 protected:
@@ -2168,16 +2169,17 @@ public:
     , mAnimatedGeometryRoot(aOther.mAnimatedGeometryRoot)
     , mToReferenceFrame(aOther.mToReferenceFrame)
     , mBuildingRect(aOther.mBuildingRect)
     , mPaintRect(aOther.mPaintRect)
     , mForceNotVisible(aOther.mForceNotVisible)
     , mDisableSubpixelAA(aOther.mDisableSubpixelAA)
     , mReusedItem(false)
     , mBackfaceHidden(mFrame->In3DContextAndBackfaceIsHidden())
+    , mPaintRectValid(false)
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
   {
     MOZ_COUNT_CTOR(nsDisplayItem);
   }
 
 
@@ -2655,17 +2657,19 @@ public:
 
   void SetBuildingRect(const nsRect& aBuildingRect)
   {
     mPaintRect = mBuildingRect = aBuildingRect;
   }
 
   void SetPaintRect(const nsRect& aPaintRect) {
     mPaintRect = aPaintRect;
-  }
+    mPaintRectValid = true;
+  }
+  bool HasPaintRect() const { return mPaintRectValid; }
 
   /**
    * Returns the building rect for the children, relative to their
    * reference frame. Can be different from mBuildingRect for nsDisplayTransform,
    * since the reference frame for the children is different from the reference
    * frame for the item itself.
    */
   virtual const nsRect& GetBuildingRectForChildren() const { return mBuildingRect; }
@@ -2880,16 +2884,17 @@ protected:
 
   mozilla::DebugOnly<uintptr_t> mOldList;
   OldListIndex mOldListIndex;
 
   bool      mForceNotVisible;
   bool      mDisableSubpixelAA;
   bool      mReusedItem;
   bool      mBackfaceHidden;
+  bool      mPaintRectValid;
 #ifdef MOZ_DUMP_PAINTING
   // True if this frame has been painted.
   bool      mPainted;
 #endif
 
   struct {
     RefPtr<const DisplayItemClipChain> mClipChain;
     const DisplayItemClip* mClip;