Bug 1472830: Reuse the work from ApplyOpacityToChildren when possible. r=mattwoodrow
MozReview-Commit-ID: 7SPMNBqVJ0Y
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -292,16 +292,17 @@ public:
MOZ_DIAGNOSTIC_ASSERT(!mOldItems[oldIndex.val].IsUsed());
if (aNewItem->GetChildren()) {
Maybe<const ActiveScrolledRoot*> containerASRForChildren;
if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
oldItem->GetChildren(),
aNewItem->GetChildren(),
containerASRForChildren,
aNewItem->GetPerFrameKey())) {
+ aNewItem->InvalidateCachedChildInfo();
mResultIsModified = true;
}
UpdateASR(aNewItem, containerASRForChildren);
aNewItem->UpdateBounds(mBuilder->Builder());
}
AutoTArray<MergedListIndex, 2> directPredecessors = ProcessPredecessorsOfOldNode(oldIndex);
@@ -396,16 +397,17 @@ public:
mOldItems[aNode.val].Discard(mBuilder, std::move(aDirectPredecessors));
mResultIsModified = true;
} else {
if (item->GetChildren()) {
Maybe<const ActiveScrolledRoot*> containerASRForChildren;
nsDisplayList empty;
if (mBuilder->MergeDisplayLists(&empty, item->GetChildren(), item->GetChildren(),
containerASRForChildren, item->GetPerFrameKey())) {
+ item->InvalidateCachedChildInfo();
mResultIsModified = true;
}
UpdateASR(item, containerASRForChildren);
item->UpdateBounds(mBuilder->Builder());
}
if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
mBuilder->IncrementSubDocPresShellPaintCount(item);
}
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -6236,17 +6236,17 @@ nsresult nsDisplayWrapper::WrapListsInPl
nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList,
const ActiveScrolledRoot* aActiveScrolledRoot,
bool aForEventsAndPluginsOnly)
: nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true)
, mOpacity(aFrame->StyleEffects()->mOpacity)
, mForEventsAndPluginsOnly(aForEventsAndPluginsOnly)
- , mOpacityAppliedToChildren(false)
+ , mChildOpacityState(ChildOpacityState::Unknown)
{
MOZ_COUNT_CTOR(nsDisplayOpacity);
mState.mOpacity = mOpacity;
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayOpacity::~nsDisplayOpacity() {
MOZ_COUNT_DTOR(nsDisplayOpacity);
@@ -6373,24 +6373,29 @@ CollectItemsWithOpacity(nsDisplayList* a
}
return true;
}
bool
nsDisplayOpacity::ApplyOpacityToChildren(nsDisplayListBuilder* aBuilder)
{
+ if (mChildOpacityState == ChildOpacityState::Deferred) {
+ return false;
+ }
+
// Only try folding our opacity down if we have at most kMaxChildCount
// children that don't overlap and can all apply the opacity to themselves.
static const size_t kMaxChildCount = 3;
// Iterate through the child display list and copy at most kMaxChildCount
// child display item pointers to a temporary list.
AutoTArray<nsDisplayItem*, kMaxChildCount> items;
if (!CollectItemsWithOpacity(&mList, items, kMaxChildCount)) {
+ mChildOpacityState = ChildOpacityState::Deferred;
return false;
}
struct {
nsDisplayItem* item;
nsRect bounds;
} children[kMaxChildCount];
@@ -6400,35 +6405,33 @@ nsDisplayOpacity::ApplyOpacityToChildren
children[childCount].item = item;
children[childCount].bounds = item->GetBounds(aBuilder, &snap);
childCount++;
}
for (size_t i = 0; i < childCount; i++) {
for (size_t j = i+1; j < childCount; j++) {
if (children[i].bounds.Intersects(children[j].bounds)) {
+ mChildOpacityState = ChildOpacityState::Deferred;
return false;
}
}
}
for (uint32_t i = 0; i < childCount; i++) {
children[i].item->ApplyOpacity(aBuilder, mOpacity, mClipChain);
}
- mOpacityAppliedToChildren = true;
+ mChildOpacityState = ChildOpacityState::Applied;
return true;
}
bool
nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
{
- // ShouldFlattenAway() should be called only once during painting.
- MOZ_ASSERT(!mOpacityAppliedToChildren);
-
if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
// If we've been split, then we might need to merge, so
// don't flatten us away.
return false;
}
if (NeedsActiveLayer(aBuilder, mFrame) || mOpacity == 0.0) {
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2368,16 +2368,22 @@ public:
aInvalidRegion->Or(aGeometry->mBounds, bounds);
} else {
aInvalidRegion->Xor(aGeometry->mBounds, bounds);
}
}
}
/**
+ * This function is called when an item's list of children has been omdified
+ * by RetaineDisplayListBuilder.
+ */
+ virtual void InvalidateCachedChildInfo() {}
+
+ /**
* @param aSnap set to true if the edges of the rectangles of the opaque
* region would be snapped to device pixels when drawing
* @return a region of the item that is opaque --- that is, every pixel
* that is visible is painted with an opaque
* color. This is useful for determining when one piece
* of content completely obscures another so that we can do occlusion
* culling.
* This does not take clipping into account.
@@ -5117,39 +5123,40 @@ public:
const ActiveScrolledRoot* aActiveScrolledRoot,
bool aForEventsAndPluginsOnly);
nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
const nsDisplayOpacity& aOther)
: nsDisplayWrapList(aBuilder, aOther)
, mOpacity(aOther.mOpacity)
, mForEventsAndPluginsOnly(aOther.mForEventsAndPluginsOnly)
- , mOpacityAppliedToChildren(false)
+ , mChildOpacityState(ChildOpacityState::Unknown)
{
// We should not try to merge flattened opacities.
- MOZ_ASSERT(!aOther.mOpacityAppliedToChildren);
+ MOZ_ASSERT(aOther.mChildOpacityState != ChildOpacityState::Applied);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayOpacity();
#endif
virtual void RestoreState() override
{
nsDisplayItem::RestoreState();
mOpacity = mState.mOpacity;
- mOpacityAppliedToChildren = false;
}
virtual nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override
{
MOZ_COUNT_CTOR(nsDisplayOpacity);
return MakeDisplayItem<nsDisplayOpacity>(aBuilder, *this);
}
+ virtual void InvalidateCachedChildInfo() override { mChildOpacityState = ChildOpacityState::Unknown; }
+
virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
bool* aSnap) const override;
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters) override;
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters) override;
@@ -5184,17 +5191,17 @@ public:
float aOpacity,
const DisplayItemClipChain* aClip) override;
virtual bool CanApplyOpacity() const override;
virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
/**
* Returns true if ShouldFlattenAway() applied opacity to children.
*/
- bool OpacityAppliedToChildren() const { return mOpacityAppliedToChildren; }
+ bool OpacityAppliedToChildren() const { return mChildOpacityState == ChildOpacityState::Applied; }
static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
virtual void WriteDebugInfo(std::stringstream& aStream) override;
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
@@ -5204,18 +5211,26 @@ public:
nsDisplayListBuilder* aDisplayListBuilder) override;
float GetOpacity() { return mOpacity; }
private:
bool ApplyOpacityToChildren(nsDisplayListBuilder* aBuilder);
float mOpacity;
- bool mForEventsAndPluginsOnly;
- bool mOpacityAppliedToChildren;
+ bool mForEventsAndPluginsOnly : 1;
+ enum class ChildOpacityState {
+ // Our child list has changed since the last time ApplyOpacityToChildren was called.
+ Unknown,
+ // Our children defer opacity handling to us.
+ Deferred,
+ // Opacity is applied to our children.
+ Applied
+ };
+ ChildOpacityState mChildOpacityState : 2;
struct {
float mOpacity;
} mState;
};
class nsDisplayBlendMode : public nsDisplayWrapList {
public: