Bug 1404181 - Part 17: Track will-change contributions per-frame so that can remove contributions from invalidated frames. r?mstange
MozReview-Commit-ID: 54LwXKkjfFX
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3105,16 +3105,18 @@ nsIFrame::BuildDisplayListForChild(nsDis
return;
}
}
nsIFrame* child = aChild;
if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
return;
+ aBuilder->ClearWillChangeBudget(child);
+
const bool doingShortcut =
(child->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST) &&
aBuilder->IsPaintingToWindow() &&
// This would be changed by the change of preference.
aBuilder->IsBuildingLayerEventRegions() &&
// Animations may change the value of |HasOpacity()|.
!(child->GetContent() &&
child->GetContent()->MayHaveAnimations());
@@ -3182,16 +3184,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
nsRect dirty = aBuilder->GetDirtyRect() - offset;
nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
bool isPlaceholder = false;
if (child->IsPlaceholderFrame()) {
isPlaceholder = true;
nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child);
child = placeholder->GetOutOfFlowFrame();
+ aBuilder->ClearWillChangeBudget(child);
NS_ASSERTION(child, "No out of flow frame?");
// If 'child' is a pushed float then it's owned by a block that's not an
// ancestor of the placeholder, and it will be painted by that block and
// should not be painted through the placeholder.
if (!child || nsLayoutUtils::IsPopup(child) ||
(child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT))
return;
MOZ_ASSERT(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -622,16 +622,17 @@ public:
, mHasImageRequest(false)
, mHasFirstLetterChild(false)
, mParentIsWrapperAnonBox(false)
, mIsWrapperBoxNeedingRestyle(false)
, mReflowRequestedForCharDataChange(false)
, mForceDescendIntoIfVisible(false)
, mBuiltDisplayList(false)
, mFrameIsModified(false)
+ , mMayHaveWillChangeBudget(false)
, mIsPrimaryFrame(false)
{
mozilla::PodZero(&mOverflow);
}
nsPresContext* PresContext() const {
return StyleContext()->PresContext();
}
@@ -4101,16 +4102,19 @@ public:
void SetForceDescendIntoIfVisible(bool aForce) { mForceDescendIntoIfVisible = aForce; }
bool BuiltDisplayList() { return mBuiltDisplayList; }
void SetBuiltDisplayList(bool aBuilt) { mBuiltDisplayList = aBuilt; }
bool IsFrameModified() { return mFrameIsModified; }
void SetFrameIsModified(bool aFrameIsModified) { mFrameIsModified = aFrameIsModified; }
+ bool MayHaveWillChangeBudget() { return mMayHaveWillChangeBudget; }
+ void SetMayHaveWillChangeBudget(bool aHasBudget) { mMayHaveWillChangeBudget = aHasBudget; }
+
protected:
/**
* Reparent this frame's view if it has one.
*/
void ReparentFrameViewTo(nsViewManager* aViewManager,
nsView* aNewParentView,
nsView* aOldParentView);
@@ -4279,25 +4283,31 @@ protected:
* the last call to CheckAndClearDisplayListState, false
* otherwise. Used for the reftest harness to verify minimal
* display list building.
*/
bool mBuiltDisplayList : 1;
bool mFrameIsModified : 1;
+ /**
+ * True if frame has will-change, and currently has display
+ * items consuming some of the will-change budget.
+ */
+ bool mMayHaveWillChangeBudget : 1;
+
private:
/**
* True if this is the primary frame for mContent.
*/
bool mIsPrimaryFrame : 1;
protected:
- // There is a 7-bit gap left here.
+ // There is a 5-bit gap left here.
// Helpers
/**
* Can we stop inside this frame when we're skipping non-rendered whitespace?
* @param aForward [in] Are we moving forward (or backward) in content order.
* @param aOffset [in/out] At what offset into the frame to start looking.
* on output - what offset was reached (whether or not we found a place to stop).
* @return STOP: An appropriate offset was found within this frame,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1917,17 +1917,17 @@ static uint32_t GetLayerizationCost(cons
nsPresContext::AppUnitsToIntCSSPixels(aSize.height));
return budgetCost;
}
bool
nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame,
const nsSize& aSize) {
- if (mWillChangeBudgetSet.Contains(aFrame)) {
+ if (mWillChangeBudgetSet.Get(aFrame, nullptr)) {
return true; // Already accounted
}
nsPresContext* key = aFrame->PresContext();
DocumentWillChangeBudget budget;
auto willChangeBudgetEntry = mWillChangeBudget.LookupForAdd(key);
if (willChangeBudgetEntry) {
// We have an existing entry.
@@ -1943,17 +1943,18 @@ nsDisplayListBuilder::AddToWillChangeBud
uint32_t cost = GetLayerizationCost(aSize);
bool onBudget = (budget.mBudget + cost) /
gWillChangeAreaMultiplier < budgetLimit;
if (onBudget) {
budget.mBudget += cost;
willChangeBudgetEntry.Data() = budget;
- mWillChangeBudgetSet.PutEntry(aFrame);
+ mWillChangeBudgetSet.Put(aFrame, cost);
+ aFrame->SetMayHaveWillChangeBudget(true);
}
return onBudget;
}
bool
nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame,
const nsSize& aSize) {
@@ -1975,16 +1976,36 @@ nsDisplayListBuilder::IsInWillChangeBudg
const char16_t* params[] = { multiplierStr.get(), limitStr.get() };
aFrame->PresContext()->Document()->WarnOnceAbout(
nsIDocument::eIgnoringWillChangeOverBudget, false,
params, ArrayLength(params));
}
return onBudget;
}
+void
+nsDisplayListBuilder::ClearWillChangeBudget(nsIFrame* aFrame)
+{
+ if (!aFrame->MayHaveWillChangeBudget()) {
+ return;
+ }
+ aFrame->SetMayHaveWillChangeBudget(false);
+
+ uint32_t cost = 0;
+ if (!mWillChangeBudgetSet.Get(aFrame, &cost)) {
+ return;
+ }
+ mWillChangeBudgetSet.Remove(aFrame);
+
+ DocumentWillChangeBudget& budget =
+ mWillChangeBudget.GetOrInsert(aFrame->PresContext());
+ MOZ_ASSERT(budget.mBudget >= cost);
+ budget.mBudget -= cost;
+}
+
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
const float gAGRBudgetAreaMultiplier = 0.3;
#else
const float gAGRBudgetAreaMultiplier = 3.0;
#endif
bool
nsDisplayListBuilder::AddToAGRBudget(nsIFrame* aFrame)
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1524,16 +1524,18 @@ public:
/**
* This will add the current frame to the will-change budget the first
* time it is seen. On subsequent calls this will return the same
* answer. This effectively implements a first-come, first-served
* allocation of the will-change budget.
*/
bool IsInWillChangeBudget(nsIFrame* aFrame, const nsSize& aSize);
+ void ClearWillChangeBudget(nsIFrame* aFrame);
+
void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage);
void ExitSVGEffectsContents();
/**
* Note: if changing the conditions under which scroll info layers
* are created, make a corresponding change to
* ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp.
*/
@@ -1666,16 +1668,26 @@ private:
struct DocumentWillChangeBudget {
DocumentWillChangeBudget()
: mBudget(0)
{}
uint32_t mBudget;
};
+ struct FrameWillChangeBudget {
+ FrameWillChangeBudget(nsIFrame* aFrame, uint32_t aUsage)
+ : mFrame(aFrame)
+ , mUsage(aUsage)
+ {}
+
+ nsIFrame* mFrame;
+ uint32_t mUsage;
+ };
+
nsIFrame* const mReferenceFrame;
nsIFrame* mIgnoreScrollFrame;
nsDisplayLayerEventRegions* mLayerEventRegions;
static const size_t kArenaAlignment =
mozilla::tl::Max<NS_ALIGNMENT_OF(void*), NS_ALIGNMENT_OF(double)>::value;
mozilla::ArenaAllocator<4096, kArenaAlignment> mPool;
@@ -1699,17 +1711,17 @@ private:
RefPtr<AnimatedGeometryRoot> mCurrentAGR;
// will-change budget tracker
nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
mWillChangeBudget;
// Any frame listed in this set is already counted in the budget
// and thus is in-budget.
- nsTHashtable<nsPtrHashKey<nsIFrame> > mWillChangeBudgetSet;
+ nsDataHashtable<nsPtrHashKey<nsIFrame>, uint32_t> mWillChangeBudgetSet;
// Area of animated geometry root budget already allocated
uint32_t mUsedAGRBudget;
// Set of frames already counted in budget
nsTHashtable<nsPtrHashKey<nsIFrame> > mAGRBudgetSet;
nsTArray<nsIFrame*> mModifiedFramesDuringBuilding;