Bug 1440177 - Part 2: Combine PaintedLayerItemsEntry and PaintedDisplayItemLayerUserData into a single struct. r?jnicol draft
authorMatt Woodrow <mwoodrow@mozilla.com>
Tue, 13 Feb 2018 15:56:43 +1300
changeset 762221 72b4a74dc82abc29eb94bdc464498f22d99707bc
parent 761373 0e1ca79b33cf7de2562308d7dab90f481f8247c1
child 762222 a136e473da15901a9683d5ccf145ade9dc987eba
push id101106
push usermwoodrow@mozilla.com
push dateThu, 01 Mar 2018 22:19:48 +0000
reviewersjnicol
bugs1440177
milestone60.0a1
Bug 1440177 - Part 2: Combine PaintedLayerItemsEntry and PaintedDisplayItemLayerUserData into a single struct. r?jnicol These two structs store very similar state (including duplicating the mask layer common clip count), and the former uses an expensive hashtable for lookups. This patch combines the two, and uses a vector of entries instead of the hashtable so we can do the cleanup pass. * * * [mq]: fix MozReview-Commit-ID: KamhbGAIqpD
gfx/layers/Layers.h
layout/painting/FrameLayerBuilder.cpp
layout/painting/FrameLayerBuilder.h
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1483,19 +1483,19 @@ public:
   // computation. A layer may be non-opaque for visibility even if it
   // is not transparent, for example, if it has a mix-blend-mode.
   bool IsOpaqueForVisibility();
 
   /**
    * This setter can be used anytime. The user data for all keys is
    * initially null. Ownership pases to the layer manager.
    */
-  void SetUserData(void* aKey, LayerUserData* aData)
+  void SetUserData(void* aKey, LayerUserData* aData, void (*aDestroy)(void*) = LayerManager::LayerUserDataDestroy)
   {
-    mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerManager::LayerUserDataDestroy);
+    mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, aDestroy);
   }
   /**
    * This can be used anytime. Ownership passes to the caller!
    */
   UniquePtr<LayerUserData> RemoveUserData(void* aKey);
   /**
    * This getter can be used anytime.
    */
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -112,33 +112,16 @@ static inline MaskLayerImageCache* GetMa
 {
   if (!gMaskLayerImageCache) {
     gMaskLayerImageCache = new MaskLayerImageCache();
   }
 
   return gMaskLayerImageCache;
 }
 
-FrameLayerBuilder::FrameLayerBuilder()
-  : mRetainingManager(nullptr)
-  , mContainingPaintedLayer(nullptr)
-  , mInactiveLayerClip(nullptr)
-  , mInvalidateAllLayers(false)
-  , mInLayerTreeCompressionMode(false)
-  , mIsInactiveLayerManager(false)
-{
-  MOZ_COUNT_CTOR(FrameLayerBuilder);
-}
-
-FrameLayerBuilder::~FrameLayerBuilder()
-{
-  GetMaskLayerImageCache()->Sweep();
-  MOZ_COUNT_DTOR(FrameLayerBuilder);
-}
-
 DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey,
                                  Layer* aLayer, nsIFrame* aFrame)
 
   : mRefCnt(0)
   , mParent(aParent)
   , mLayer(aLayer)
   , mDisplayItemKey(aKey)
   , mItem(nullptr)
@@ -1384,19 +1367,20 @@ protected:
    * there is no clipping specified or a mask layer cannot be built.
    * Builds an ImageLayer for the appropriate backend; the mask is relative to
    * aLayer's visible region.
    * aLayer is the layer to be clipped.
    * relative to the container reference frame
    * aRoundedRectClipCount is used when building mask layers for PaintedLayers,
    * SetupMaskLayer will build a mask layer for only the first
    * aRoundedRectClipCount rounded rects in aClip
+   * Returns the number of rounded rects included in the mask layer.
    */
-  void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
-                      uint32_t aRoundedRectClipCount = UINT32_MAX);
+  uint32_t SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
+                          uint32_t aRoundedRectClipCount = UINT32_MAX);
 
   /**
    * If |aClip| has rounded corners, create a mask layer for them, and
    * add it to |aLayer|'s ancestor mask layers, returning an index into
    * the array of ancestor mask layers. Returns an empty Maybe if
    * |aClip| does not have rounded corners, or if no mask layer could
    * be created.
    */
@@ -1495,31 +1479,42 @@ protected:
   nsRect mLastDisplayPortRect;
 };
 
 class PaintedDisplayItemLayerUserData : public LayerUserData
 {
 public:
   PaintedDisplayItemLayerUserData() :
     mMaskClipCount(0),
+    mLastCommonClipCount(0),
     mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
     mXScale(1.f), mYScale(1.f),
     mAppUnitsPerDevPixel(0),
     mTranslation(0, 0),
     mAnimatedGeometryRootPosition(0, 0),
-    mLastItemCount(0) {}
+    mLastItemCount(0),
+    mContainerLayerFrame(nullptr),
+    mHasExplicitLastPaintOffset(false) {}
+
+  NS_INLINE_DECL_REFCOUNTING(PaintedDisplayItemLayerUserData);
 
   /**
    * Record the number of clips in the PaintedLayer's mask layer.
    * Should not be reset when the layer is recycled since it is used to track
    * changes in the use of mask layers.
    */
   uint32_t mMaskClipCount;
 
   /**
+   * Records the number of clips in the PaintedLayer's mask layer during
+   * the previous paint. Used for invalidation.
+   */
+  uint32_t mLastCommonClipCount;
+
+  /**
    * A color that should be painted over the bounds of the layer's visible
    * region before any other content is painted.
    */
   nscolor mForcedBackgroundColor;
 
   /**
    * The resolution scale used.
    */
@@ -1566,22 +1561,64 @@ public:
   // 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 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.
+  // FLB's mPaintedLayerItems is responsible for cleaning these up when
+  // we finish painting to avoid dangling pointers.
+  nsTArray<AssignedDisplayItem> mItems;
+  nsIFrame* mContainerLayerFrame;
+
+  bool mHasExplicitLastPaintOffset;
+
   /**
    * This is set when the painted layer has no component alpha.
    */
   bool mDisabledAlpha;
+
+protected:
+  ~PaintedDisplayItemLayerUserData() = default;
 };
 
+FrameLayerBuilder::FrameLayerBuilder()
+  : mRetainingManager(nullptr)
+  , mContainingPaintedLayer(nullptr)
+  , mInactiveLayerClip(nullptr)
+  , mInvalidateAllLayers(false)
+  , mInLayerTreeCompressionMode(false)
+  , mIsInactiveLayerManager(false)
+{
+  MOZ_COUNT_CTOR(FrameLayerBuilder);
+}
+
+FrameLayerBuilder::~FrameLayerBuilder()
+{
+  GetMaskLayerImageCache()->Sweep();
+  for (PaintedDisplayItemLayerUserData* userData : mPaintedLayerItems) {
+    userData->mItems.Clear();
+    userData->mContainerLayerFrame = nullptr;
+  }
+  MOZ_COUNT_DTOR(FrameLayerBuilder);
+}
+
+void
+FrameLayerBuilder::AddPaintedLayerItemsEntry(PaintedDisplayItemLayerUserData* aData)
+{
+  mPaintedLayerItems.AppendElement(aData);
+}
+
 /*
  * User data for layers which will be used as masks.
  */
 struct MaskLayerUserData : public LayerUserData
 {
   MaskLayerUserData()
     : mScaleX(-1.0f)
     , mScaleY(-1.0f)
@@ -2336,33 +2373,41 @@ ContainerState::AttemptToRecyclePaintedL
                         didResetScrollPositionForLayerPixelAlignment);
   PreparePaintedLayerForUse(layer, data, aAnimatedGeometryRoot,
                             aReferenceFrame, aTopLeft,
                             didResetScrollPositionForLayerPixelAlignment);
 
   return layer.forget();
 }
 
+void ReleaseLayerUserData(void* aData)
+{
+  PaintedDisplayItemLayerUserData* userData =
+    static_cast<PaintedDisplayItemLayerUserData*>(aData);
+  userData->Release();
+}
+
 already_AddRefed<PaintedLayer>
 ContainerState::CreatePaintedLayer(PaintedLayerData* aData)
 {
   LayerManager::PaintedLayerCreationHint creationHint =
     GetLayerCreationHint(aData->mAnimatedGeometryRoot);
 
   // Create a new painted layer
   RefPtr<PaintedLayer> layer = mManager->CreatePaintedLayerWithHint(creationHint);
   if (!layer) {
     return nullptr;
   }
 
   // Mark this layer as being used for painting display items
-  PaintedDisplayItemLayerUserData* userData = new PaintedDisplayItemLayerUserData();
+  RefPtr<PaintedDisplayItemLayerUserData> userData = new PaintedDisplayItemLayerUserData();
   userData->mDisabledAlpha =
     mParameters.mDisableSubpixelAntialiasingInDescendants;
-  layer->SetUserData(&gPaintedDisplayItemLayerUserData, userData);
+  userData.get()->AddRef();
+  layer->SetUserData(&gPaintedDisplayItemLayerUserData, userData, ReleaseLayerUserData);
   ResetScrollPositionForLayerPixelAlignment(aData->mAnimatedGeometryRoot);
 
   PreparePaintedLayerForUse(layer, userData, aData->mAnimatedGeometryRoot,
                             aData->mReferenceFrame,
                             aData->mAnimatedGeometryRootOffset, true);
 
   return layer.forget();
 }
@@ -2430,17 +2475,19 @@ ContainerState::PreparePaintedLayerForUs
 {
   aData->mXScale = mParameters.mXScale;
   aData->mYScale = mParameters.mYScale;
   aData->mLastAnimatedGeometryRootOrigin = aData->mAnimatedGeometryRootOrigin;
   aData->mAnimatedGeometryRootOrigin = aTopLeft;
   aData->mAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
   aLayer->SetAllowResidualTranslation(mParameters.AllowResidualTranslation());
 
-  mLayerBuilder->SavePreviousDataForLayer(aLayer, aData->mMaskClipCount);
+  aData->mLastPaintOffset = GetTranslationForPaintedLayer(aLayer);
+  aData->mHasExplicitLastPaintOffset = true;
+  aData->mLastCommonClipCount = aData->mMaskClipCount;
 
   // Set up transform so that 0,0 in the PaintedLayer corresponds to the
   // (pixel-snapped) top-left of the aAnimatedGeometryRoot.
   nsPoint offset = (*aAnimatedGeometryRoot)->GetOffsetToCrossDoc(aReferenceFrame);
   nscoord appUnitsPerDevPixel = (*aAnimatedGeometryRoot)->PresContext()->AppUnitsPerDevPixel();
   gfxPoint scaledOffset(
       NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale,
       NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale);
@@ -3174,16 +3221,17 @@ void ContainerState::FinishPaintedLayerD
     InvalidateForLayerChange(item.mItem, data->mLayer, oldData);
     mLayerBuilder->AddPaintedDisplayItem(data, item.mItem, item.mClip,
                                          *this, item.mLayerState,
                                          data->mAnimatedGeometryRootOffset,
                                          oldData, item);
   }
 
   PaintedDisplayItemLayerUserData* userData = GetPaintedDisplayItemLayerUserData(data->mLayer);
+  NS_ASSERTION(userData, "where did our user data go?");
   userData->mLastItemCount = data->mAssignedDisplayItems.Length();
 
   NewLayerEntry* newLayerEntry = &mNewChildLayers[data->mNewChildLayersIndex];
 
   RefPtr<Layer> layer;
   bool canOptimizeToImageLayer = data->CanOptimizeToImageLayer(mBuilder);
 
   FLB_LOG_PAINTED_LAYER_DECISION(data, "Selecting layer for pld=%p\n", data);
@@ -3249,19 +3297,17 @@ void ContainerState::FinishPaintedLayerD
     if (PaintedLayerData* containingPld = mLayerBuilder->GetContainingPaintedLayerData()) {
       containingPld->mLayer->AddExtraDumpInfo(nsCString(data->mLog));
     } else {
       layer->AddExtraDumpInfo(nsCString(data->mLog));
     }
   }
 #endif
 
-  FrameLayerBuilder::PaintedLayerItemsEntry* entry = mLayerBuilder->
-    AddPaintedLayerItemsEntry(static_cast<PaintedLayer*>(layer.get()));
-  MOZ_ASSERT(entry);
+  mLayerBuilder->AddPaintedLayerItemsEntry(userData);
 
   nsIntRegion transparentRegion;
   transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
   bool isOpaque = transparentRegion.IsEmpty();
   // For translucent PaintedLayers, try to find an opaque background
   // color that covers the entire area beneath it so we can pull that
   // color into this layer to make it opaque.
   if (layer == data->mLayer) {
@@ -3269,19 +3315,16 @@ void ContainerState::FinishPaintedLayerD
     if (!isOpaque) {
       backgroundColor = aFindOpaqueBackgroundColor();
       if (NS_GET_A(backgroundColor) == 255) {
         isOpaque = true;
       }
     }
 
     // Store the background color
-    PaintedDisplayItemLayerUserData* userData =
-      GetPaintedDisplayItemLayerUserData(data->mLayer);
-    NS_ASSERTION(userData, "where did our user data go?");
     if (userData->mForcedBackgroundColor != backgroundColor) {
       // Invalidate the entire target PaintedLayer since we're changing
       // the background color
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf_stderr("Forced background color has changed from #%08X to #%08X on layer %p\n",
                       userData->mForcedBackgroundColor, backgroundColor, data->mLayer);
         nsAutoCString str;
@@ -3291,21 +3334,28 @@ void ContainerState::FinishPaintedLayerD
 #endif
       data->mLayer->InvalidateWholeLayer();
     }
     userData->mForcedBackgroundColor = backgroundColor;
 
     // use a mask layer for rounded rect clipping.
     // data->mCommonClipCount may be -1 if we haven't put any actual
     // drawable items in this layer (i.e. it's only catching events).
-    int32_t commonClipCount;
+    uint32_t commonClipCount;
     commonClipCount = std::max(0, data->mCommonClipCount);
-    SetupMaskLayer(layer, data->mItemClip, commonClipCount);
-    // copy commonClipCount to the entry
-    entry->mCommonClipCount = commonClipCount;
+
+    // if the number of clips we are going to mask has decreased, then aLayer might have
+    // cached graphics which assume the existence of a soon-to-be non-existent mask layer
+    // in that case, invalidate the whole layer.
+    if (commonClipCount < userData->mMaskClipCount) {
+      PaintedLayer* painted = layer->AsPaintedLayer();
+      painted->InvalidateWholeLayer();
+    }
+
+    userData->mMaskClipCount = SetupMaskLayer(layer, data->mItemClip, commonClipCount);
   } else {
     // mask layer for image and color layers
     SetupMaskLayer(layer, data->mItemClip);
   }
 
   uint32_t flags = 0;
   nsIWidget* widget = mContainerReferenceFrame->PresContext()->GetRootWidget();
   // See bug 941095. Not quite ready to disable this.
@@ -3318,18 +3368,18 @@ void ContainerState::FinishPaintedLayerD
   } else if (data->mNeedComponentAlpha && !hidpi) {
     flags |= Layer::CONTENT_COMPONENT_ALPHA;
   }
   if (data->mDisableFlattening) {
     flags |= Layer::CONTENT_DISABLE_FLATTENING;
   }
   layer->SetContentFlags(flags);
 
-  entry->mItems = Move(data->mAssignedDisplayItems);
-  entry->mContainerLayerFrame = GetContainerFrame();
+  userData->mItems = Move(data->mAssignedDisplayItems);
+  userData->mContainerLayerFrame = GetContainerFrame();
 
   PaintedLayerData* containingPaintedLayerData =
      mLayerBuilder->GetContainingPaintedLayerData();
   // If we're building layers for an inactive layer, the event regions are
   // clipped to the inactive layer's clip prior to being combined into the
   // event regions of the containing PLD.
   // For the dispatch-to-content and maybe-hit regions, rounded corners on
   // the clip are ignored, since these are approximate regions. For the
@@ -4729,20 +4779,19 @@ FrameLayerBuilder::ComputeGeometryChange
 
     // Only allocate a new geometry object if something actually changed, otherwise the existing
     // one should be fine. We always reallocate for inactive layers, since these types don't
     // implement ComputeInvalidateRegion (and rely on the ComputeDifferences call in
     // AddPaintedDisplayItem instead).
     if (!combined.IsEmpty() || aData->mLayerState == LAYER_INACTIVE) {
       geometry = item->AllocateGeometry(mDisplayListBuilder);
     }
-    PaintedLayerItemsEntry* entry = mPaintedLayerItems.GetEntry(paintedLayer);
-    aData->mClip.AddOffsetAndComputeDifference(entry->mCommonClipCount,
+    aData->mClip.AddOffsetAndComputeDifference(layerData->mMaskClipCount,
                                                shift, aData->mGeometry->ComputeInvalidationRegion(),
-                                               clip, entry->mLastCommonClipCount,
+                                               clip, layerData->mLastCommonClipCount,
                                                geometry ? geometry->ComputeInvalidationRegion() :
                                                           aData->mGeometry->ComputeInvalidationRegion(),
                                                &combined);
 
     // Add in any rect that the frame specified
     combined.Or(combined, invalid);
     combined.Or(combined, changedFrameInvalidations);
 
@@ -4959,36 +5008,16 @@ FrameLayerBuilder::StoreDataForFrame(nsI
 
 AssignedDisplayItem::~AssignedDisplayItem()
 {
   if (mInactiveLayerManager) {
     mInactiveLayerManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
   }
 }
 
-FrameLayerBuilder::PaintedLayerItemsEntry::PaintedLayerItemsEntry(const PaintedLayer *aKey)
-  : nsPtrHashKey<PaintedLayer>(aKey)
-  , mContainerLayerFrame(nullptr)
-  , mLastCommonClipCount(0)
-  , mHasExplicitLastPaintOffset(false)
-  , mCommonClipCount(0)
-{
-}
-
-FrameLayerBuilder::PaintedLayerItemsEntry::PaintedLayerItemsEntry(const PaintedLayerItemsEntry& aOther)
-  : nsPtrHashKey<PaintedLayer>(aOther.mKey)
-  , mItems(aOther.mItems)
-{
-  NS_ERROR("Should never be called, since we ALLOW_MEMMOVE");
-}
-
-FrameLayerBuilder::PaintedLayerItemsEntry::~PaintedLayerItemsEntry()
-{
-}
-
 void
 FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
                                        nsDisplayItem* aItem,
                                        LayerState aLayerState,
                                        BasicLayerManager* aManager,
                                        DisplayItemData* aData)
 {
   if (aLayer->Manager() != mRetainingManager)
@@ -4996,35 +5025,24 @@ FrameLayerBuilder::AddLayerDisplayItem(L
 
   DisplayItemData *data = StoreDataForFrame(aItem, aLayer, aLayerState, aData);
   data->mInactiveManager = aManager;
 }
 
 nsIntPoint
 FrameLayerBuilder::GetLastPaintOffset(PaintedLayer* aLayer)
 {
-  PaintedLayerItemsEntry* entry = mPaintedLayerItems.PutEntry(aLayer);
-  if (entry) {
-    if (entry->mHasExplicitLastPaintOffset)
-      return entry->mLastPaintOffset;
+  PaintedDisplayItemLayerUserData* layerData = GetPaintedDisplayItemLayerUserData(aLayer);
+  MOZ_ASSERT(layerData);
+  if (layerData->mHasExplicitLastPaintOffset) {
+    return layerData->mLastPaintOffset;
   }
   return GetTranslationForPaintedLayer(aLayer);
 }
 
-void
-FrameLayerBuilder::SavePreviousDataForLayer(PaintedLayer* aLayer, uint32_t aClipCount)
-{
-  PaintedLayerItemsEntry* entry = mPaintedLayerItems.PutEntry(aLayer);
-  if (entry) {
-    entry->mLastPaintOffset = GetTranslationForPaintedLayer(aLayer);
-    entry->mHasExplicitLastPaintOffset = true;
-    entry->mLastCommonClipCount = aClipCount;
-  }
-}
-
 bool
 FrameLayerBuilder::CheckInLayerTreeCompressionMode()
 {
   if (mInLayerTreeCompressionMode) {
     return true;
   }
 
   // If we wanted to be in layer tree compression mode, but weren't, then scheduled
@@ -6130,27 +6148,23 @@ FrameLayerBuilder::DrawPaintedLayer(Pain
   AUTO_PROFILER_LABEL("FrameLayerBuilder::DrawPaintedLayer", GRAPHICS);
 
   nsDisplayListBuilder* builder = static_cast<nsDisplayListBuilder*>
     (aCallbackData);
 
   FrameLayerBuilder *layerBuilder = aLayer->Manager()->GetLayerBuilder();
   NS_ASSERTION(layerBuilder, "Unexpectedly null layer builder!");
 
-  PaintedLayerItemsEntry* entry = layerBuilder->mPaintedLayerItems.GetEntry(aLayer);
-  NS_ASSERTION(entry, "We shouldn't be drawing into a layer with no items!");
-  if (!entry->mContainerLayerFrame) {
-    return;
-  }
-
-
   PaintedDisplayItemLayerUserData* userData =
     static_cast<PaintedDisplayItemLayerUserData*>
       (aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
   NS_ASSERTION(userData, "where did our user data go?");
+  if (!userData->mContainerLayerFrame) {
+    return;
+  }
 
   bool shouldDrawRectsSeparately =
     ShouldDrawRectsSeparately(&aDrawTarget, aClip);
 
   if (!shouldDrawRectsSeparately) {
     if (aClip == DrawRegionClip::DRAW) {
       gfxUtils::ClipToRegion(aContext, aRegionToDraw);
     }
@@ -6158,27 +6172,27 @@ FrameLayerBuilder::DrawPaintedLayer(Pain
     DrawForcedBackgroundColor(aDrawTarget, aRegionToDraw.GetBounds(),
                               userData->mForcedBackgroundColor);
   }
 
   // make the origin of the context coincide with the origin of the
   // PaintedLayer
   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
   nsIntPoint offset = GetTranslationForPaintedLayer(aLayer);
-  nsPresContext* presContext = entry->mContainerLayerFrame->PresContext();
+  nsPresContext* presContext = userData->mContainerLayerFrame->PresContext();
 
   if (!userData->mVisibilityComputedRegion.Contains(aDirtyRegion) &&
       !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(entry->mItems, builder, aDirtyRegion,
+    RecomputeVisibilityForItems(userData->mItems, builder, aDirtyRegion,
                                 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();
@@ -6192,36 +6206,36 @@ FrameLayerBuilder::DrawPaintedLayer(Pain
 
       // Apply the residual transform if it has been enabled, to ensure that
       // snapping when we draw into aContext exactly matches the ideal transform.
       // See above for why this is OK.
       aContext->SetMatrixDouble(
         aContext->CurrentMatrixDouble().PreTranslate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)).
                                         PreScale(userData->mXScale, userData->mYScale));
 
-      layerBuilder->PaintItems(entry->mItems, iterRect, aContext,
+      layerBuilder->PaintItems(userData->mItems, iterRect, aContext,
                                builder, presContext,
                                offset, userData->mXScale, userData->mYScale,
-                               entry->mCommonClipCount);
+                               userData->mMaskClipCount);
       if (gfxPrefs::GfxLoggingPaintedPixelCountEnabled()) {
         aLayer->Manager()->AddPaintedPixelCount(iterRect.Area());
       }
     }
   } else {
     // Apply the residual transform if it has been enabled, to ensure that
     // snapping when we draw into aContext exactly matches the ideal transform.
     // See above for why this is OK.
     aContext->SetMatrixDouble(
       aContext->CurrentMatrixDouble().PreTranslate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)).
                                       PreScale(userData->mXScale,userData->mYScale));
 
-    layerBuilder->PaintItems(entry->mItems, aRegionToDraw.GetBounds(), aContext,
+    layerBuilder->PaintItems(userData->mItems, aRegionToDraw.GetBounds(), aContext,
                              builder, presContext,
                              offset, userData->mXScale, userData->mYScale,
-                             entry->mCommonClipCount);
+                             userData->mMaskClipCount);
     if (gfxPrefs::GfxLoggingPaintedPixelCountEnabled()) {
       aLayer->Manager()->AddPaintedPixelCount(
         aRegionToDraw.GetBounds().Area());
     }
   }
 
   bool isActiveLayerManager = !aLayer->Manager()->IsInactiveLayerManager();
 
@@ -6288,57 +6302,36 @@ CalculateBounds(const nsTArray<DisplayIt
   nsRect bounds = aRects[0].mRect;
   for (uint32_t i = 1; i < aRects.Length(); ++i) {
     bounds.UnionRect(bounds, aRects[i].mRect);
    }
 
   return gfx::Rect(bounds.ToNearestPixels(aAppUnitsPerDevPixel));
 }
 
-static void
-SetClipCount(PaintedDisplayItemLayerUserData* apaintedData,
-             uint32_t aClipCount)
-{
-  if (apaintedData) {
-    apaintedData->mMaskClipCount = aClipCount;
-  }
-}
-
-void
+uint32_t
 ContainerState::SetupMaskLayer(Layer *aLayer,
                                const DisplayItemClip& aClip,
                                uint32_t aRoundedRectClipCount)
 {
-  // if the number of clips we are going to mask has decreased, then aLayer might have
-  // cached graphics which assume the existence of a soon-to-be non-existent mask layer
-  // in that case, invalidate the whole layer.
-  PaintedDisplayItemLayerUserData* paintedData = GetPaintedDisplayItemLayerUserData(aLayer);
-  if (paintedData &&
-      aRoundedRectClipCount < paintedData->mMaskClipCount) {
-    PaintedLayer* painted = aLayer->AsPaintedLayer();
-    painted->InvalidateWholeLayer();
-  }
-
   // don't build an unnecessary mask
   if (aClip.GetRoundedRectCount() == 0 ||
       aRoundedRectClipCount == 0) {
-    SetClipCount(paintedData, 0);
-    return;
+    return 0;
   }
 
   RefPtr<Layer> maskLayer =
     CreateMaskLayer(aLayer, aClip, Nothing(), aRoundedRectClipCount);
 
   if (!maskLayer) {
-    SetClipCount(paintedData, 0);
-    return;
+    return 0;
   }
 
   aLayer->SetMaskLayer(maskLayer);
-  SetClipCount(paintedData, aRoundedRectClipCount);
+  return aRoundedRectClipCount;
 }
 
 MaskLayerUserData*
 GetMaskLayerUserData(Layer* aMaskLayer)
 {
   if (!aMaskLayer) {
     return nullptr;
   }
--- a/layout/painting/FrameLayerBuilder.h
+++ b/layout/painting/FrameLayerBuilder.h
@@ -38,16 +38,17 @@ class BasicLayerManager;
 class PaintedLayer;
 class ImageLayer;
 } // namespace layers
 
 class FrameLayerBuilder;
 class LayerManagerData;
 class PaintedLayerData;
 class ContainerState;
+class PaintedDisplayItemLayerUserData;
 
 /**
   * Retained data storage:
   *
   * Each layer manager (widget, and inactive) stores a LayerManagerData object
   * that keeps a hash-set of DisplayItemData items that were drawn into it.
   * Each frame also keeps a list of DisplayItemData pointers that were
   * created for that frame. DisplayItemData objects manage these lists automatically.
@@ -585,22 +586,16 @@ public:
    * Returns true if the given display item was rendered during the previous
    * paint. Returns false otherwise.
    */
   static bool HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey);
 
   typedef void (*DisplayItemDataCallback)(nsIFrame *aFrame, DisplayItemData* aItem);
 
   /**
-   * Save transform that was in aLayer when we last painted, and the position
-   * of the active scrolled root frame. It must be an integer
-   * translation.
-   */
-  void SavePreviousDataForLayer(PaintedLayer* aLayer, uint32_t aClipCount);
-  /**
    * Get the translation transform that was in aLayer when we last painted. It's either
    * the transform saved by SaveLastPaintTransform, or else the transform
    * that's currently in the layer (which must be an integer translation).
    */
   nsIntPoint GetLastPaintOffset(PaintedLayer* aLayer);
 
   /**
    * Return the resolution at which we expect to render aFrame's contents,
@@ -699,47 +694,21 @@ protected:
                   float aXScale, float aYScale,
                   int32_t aCommonClipCount);
 
   /**
    * We accumulate ClippedDisplayItem elements in a hashtable during
    * the paint process. This is the hashentry for that hashtable.
    */
 public:
-  class PaintedLayerItemsEntry : public nsPtrHashKey<PaintedLayer> {
-  public:
-    explicit PaintedLayerItemsEntry(const PaintedLayer *key);
-    PaintedLayerItemsEntry(const PaintedLayerItemsEntry&);
-    ~PaintedLayerItemsEntry();
-
-    nsTArray<AssignedDisplayItem> mItems;
-    nsIFrame* mContainerLayerFrame;
-    // The translation set on this PaintedLayer before we started updating the
-    // layer tree.
-    nsIntPoint mLastPaintOffset;
-    uint32_t mLastCommonClipCount;
-
-    bool mHasExplicitLastPaintOffset;
-    /**
-      * The first mCommonClipCount rounded rectangle clips are identical for
-      * all items in the layer. Computed in PaintedLayerData.
-      */
-    uint32_t mCommonClipCount;
-
-    enum { ALLOW_MEMMOVE = true };
-  };
-
   /**
-   * Get the PaintedLayerItemsEntry object associated with aLayer in this
-   * FrameLayerBuilder
+   * Add the PaintedDisplayItemLayerUserData object as being used in this
+   * transaction so that we clean it up afterwards.
    */
-  PaintedLayerItemsEntry* AddPaintedLayerItemsEntry(PaintedLayer* aLayer)
-  {
-    return mPaintedLayerItems.PutEntry(aLayer);
-  }
+  void AddPaintedLayerItemsEntry(PaintedDisplayItemLayerUserData* aData);
 
   PaintedLayerData* GetContainingPaintedLayerData()
   {
     return mContainingPaintedLayer;
   }
 
   const DisplayItemClip* GetInactiveLayerClip() const
   {
@@ -771,20 +740,22 @@ protected:
    */
   RefPtr<nsRootPresContext>         mRootPresContext;
 
   /**
    * The display list builder being used.
    */
   nsDisplayListBuilder*               mDisplayListBuilder;
   /**
-   * A map from PaintedLayers to the list of display items (plus
-   * clipping data) to be rendered in the layer.
+   * An array of PaintedLayer user data objects containing the
+   * list of display items (plus clipping data) to be rendered in the
+   * layer. We clean these up at the end of the transaction to
+   * remove references to display items.
    */
-  nsTHashtable<PaintedLayerItemsEntry> mPaintedLayerItems;
+  AutoTArray<RefPtr<PaintedDisplayItemLayerUserData>, 5> mPaintedLayerItems;
 
   /**
    * When building layers for an inactive layer, this is where the
    * inactive layer will be placed.
    */
   PaintedLayerData*                   mContainingPaintedLayer;
 
   /**