Bug 1728258 - Compute AnimatedGeometryRoot frames in RetainedDisplayListBuilder without needing the existing object stored on nsDisplayItem. r=miko,mstange

Differential Revision: https://phabricator.services.mozilla.com/D124153
This commit is contained in:
Matt Woodrow 2021-09-06 23:12:04 +00:00
Родитель 9bdea2da6b
Коммит e060744ad1
3 изменённых файлов: 90 добавлений и 33 удалений

Просмотреть файл

@ -43,6 +43,29 @@
* we would have built both in the new list if they intersected. Given that, we
* can align items that appear in both lists, and any items that appear between
* matched items can be inserted into the merged list in any order.
*
* Frames that are a stacking context, containing blocks for position:fixed
* descendants, and don't have any continuations (see
* CanStoreDisplayListBuildingRect) trigger recursion into the algorithm with
* separate retaining decisions made.
*
* RDL defines the concept of an AnimatedGeometryRoot (AGR), the nearest
* ancestor frame which can be moved asynchronously on the compositor thread.
* These are currently nsDisplayItems which return true from CanMoveAsync
* (animated nsDisplayTransform and nsDisplayStickyPosition) and
* ActiveScrolledRoots.
*
* For each context that we run the retaining algorithm, there can only be
* mutations to one AnimatedGeometryRoot. This is because we are unable to
* reason about intersections of items that might then move relative to each
* other without RDL running again. If there are mutations to multiple
* AnimatedGeometryRoots, then we bail out and rebuild all the items in the
* context.
*
* Otherwise, when mutations are restricted to a single AGR, we pre-process the
* old display list and mark the frames for all existing (unmodified!) items
* that belong to a different AGR and ensure that we rebuild those items for
* correct sorting with the modified ones.
*/
using mozilla::dom::Document;
@ -101,8 +124,7 @@ static void MarkFramesWithItemsAndImagesModified(nsDisplayList* aList) {
}
}
static AnimatedGeometryRoot* SelectAGRForFrame(
nsIFrame* aFrame, AnimatedGeometryRoot* aParentAGR) {
static nsIFrame* SelectAGRForFrame(nsIFrame* aFrame, nsIFrame* aParentAGR) {
if (!aFrame->IsStackingContext() || !aFrame->IsFixedPosContainingBlock()) {
return aParentAGR;
}
@ -114,7 +136,7 @@ static AnimatedGeometryRoot* SelectAGRForFrame(
nsDisplayListBuilder::DisplayListBuildingData* data =
aFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
return data && data->mModifiedAGR ? data->mModifiedAGR.get() : nullptr;
return data && data->mModifiedAGR ? data->mModifiedAGR : nullptr;
}
void RetainedDisplayListBuilder::AddSizeOfIncludingThis(
@ -148,9 +170,10 @@ bool AnyContentAncestorModified(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
// to mark, as child stacking contexts might. It would be nice if we could
// jump into those immediately rather than walking the entire thing.
bool RetainedDisplayListBuilder::PreProcessDisplayList(
RetainedDisplayList* aList, AnimatedGeometryRoot* aAGR,
PartialUpdateResult& aUpdated, nsIFrame* aOuterFrame, uint32_t aCallerKey,
uint32_t aNestingDepth, bool aKeepLinked) {
RetainedDisplayList* aList, nsIFrame* aAGR, PartialUpdateResult& aUpdated,
nsIFrame* aAsyncAncestor, const ActiveScrolledRoot* aAsyncAncestorASR,
nsIFrame* aOuterFrame, uint32_t aCallerKey, uint32_t aNestingDepth,
bool aKeepLinked) {
// The DAG merging algorithm does not have strong mechanisms in place to keep
// the complexity of the resulting DAG under control. In some cases we can
// build up edges very quickly. Detect those cases and force a full display
@ -258,10 +281,21 @@ bool RetainedDisplayListBuilder::PreProcessDisplayList(
keepLinked = true;
}
if (!PreProcessDisplayList(item->GetChildren(),
SelectAGRForFrame(f, aAGR), aUpdated,
item->Frame(), item->GetPerFrameKey(),
aNestingDepth + 1, keepLinked)) {
// If this item's frame is an AGR (can be moved asynchronously by the
// compositor), then use that frame for descendants. Also pass the ASR
// for that item, so that descendants can compare to see if any new
// ASRs have been pushed since.
nsIFrame* asyncAncestor = aAsyncAncestor;
const ActiveScrolledRoot* asyncAncestorASR = aAsyncAncestorASR;
if (item->CanMoveAsync()) {
asyncAncestor = item->Frame();
asyncAncestorASR = item->GetActiveScrolledRoot();
}
if (!PreProcessDisplayList(
item->GetChildren(), SelectAGRForFrame(f, aAGR), aUpdated,
asyncAncestor, asyncAncestorASR, item->Frame(),
item->GetPerFrameKey(), aNestingDepth + 1, keepLinked)) {
MOZ_RELEASE_ASSERT(
!aKeepLinked,
"Can't early return since we need to move the out list back");
@ -276,7 +310,19 @@ bool RetainedDisplayListBuilder::PreProcessDisplayList(
// sibling of the changed thing to get correct ordering. The changed content
// is a frame though, and it's hard to map that to container items in this
// list.
if (aAGR && item->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
// If an ancestor display item is an AGR, and our ASR matches the ASR
// of that item, then there can't have been any new ASRs pushed since that
// item, so that item is our AGR. Otherwise, our AGR is our ASR.
nsIFrame* agrFrame = nullptr;
if (aAsyncAncestorASR == item->GetActiveScrolledRoot()) {
agrFrame = aAsyncAncestor;
} else {
MOZ_ASSERT(item->GetActiveScrolledRoot());
agrFrame =
item->GetActiveScrolledRoot()->mScrollableFrame->GetScrolledFrame();
}
if (aAGR && agrFrame != aAGR) {
mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
}
@ -934,7 +980,7 @@ static bool CanStoreDisplayListBuildingRect(nsDisplayListBuilder* aBuilder,
static bool ProcessFrameInternal(nsIFrame* aFrame,
nsDisplayListBuilder* aBuilder,
AnimatedGeometryRoot** aAGR, nsRect& aOverflow,
nsIFrame** aAGR, nsRect& aOverflow,
const nsIFrame* aStopAtFrame,
nsTArray<nsIFrame*>& aOutFramesWithProps,
const bool aStopAtStackingContext) {
@ -970,7 +1016,7 @@ static bool ProcessFrameInternal(nsIFrame* aFrame,
// Tracking AGRs for the placeholder processing is not necessary, as the
// goal is to only modify the DisplayListBuildingData rect.
AnimatedGeometryRoot* dummyAGR = nullptr;
nsIFrame* dummyAGR = nullptr;
// Find a common ancestor frame to handle frame continuations.
// TODO: It might be possible to write a more specific and efficient
@ -1112,7 +1158,7 @@ static bool ProcessFrameInternal(nsIFrame* aFrame,
bool RetainedDisplayListBuilder::ProcessFrame(
nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsIFrame* aStopAtFrame,
nsTArray<nsIFrame*>& aOutFramesWithProps, const bool aStopAtStackingContext,
nsRect* aOutDirty, AnimatedGeometryRoot** aOutModifiedAGR) {
nsRect* aOutDirty, nsIFrame** aOutModifiedAGR) {
if (aFrame->HasOverrideDirtyRegion()) {
aOutFramesWithProps.AppendElement(aFrame);
}
@ -1125,6 +1171,7 @@ bool RetainedDisplayListBuilder::ProcessFrame(
// combined with the ancestor walk for TransformFrameRectToAncestor.
AnimatedGeometryRoot* agr =
aBuilder->FindAnimatedGeometryRootFor(aFrame)->GetAsyncAGR();
nsIFrame* agrFrame = agr ? agr->mFrame : nullptr;
CRR_LOG("Processing frame %p with agr %p\n", aFrame, agr->mFrame);
@ -1146,7 +1193,7 @@ bool RetainedDisplayListBuilder::ProcessFrame(
overflow.UnionRect(overflow, aBuilder->GetCaretRect());
}
if (!ProcessFrameInternal(aFrame, aBuilder, &agr, overflow, aStopAtFrame,
if (!ProcessFrameInternal(aFrame, aBuilder, &agrFrame, overflow, aStopAtFrame,
aOutFramesWithProps, aStopAtStackingContext)) {
return false;
}
@ -1160,9 +1207,9 @@ bool RetainedDisplayListBuilder::ProcessFrame(
// really complex to track which items would need to be marked in
// MarkFramesForDifferentAGR.
if (!*aOutModifiedAGR) {
CRR_LOG("Setting %p as root stacking context AGR\n", agr);
*aOutModifiedAGR = agr;
} else if (agr && *aOutModifiedAGR != agr) {
CRR_LOG("Setting %p as root stacking context AGR\n", agrFrame);
*aOutModifiedAGR = agrFrame;
} else if (agrFrame && *aOutModifiedAGR != agrFrame) {
CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
return false;
}
@ -1236,8 +1283,7 @@ static void FindContainingBlocks(nsIFrame* aFrame,
*/
bool RetainedDisplayListBuilder::ComputeRebuildRegion(
nsTArray<nsIFrame*>& aModifiedFrames, nsRect* aOutDirty,
AnimatedGeometryRoot** aOutModifiedAGR,
nsTArray<nsIFrame*>& aOutFramesWithProps) {
nsIFrame** aOutModifiedAGR, nsTArray<nsIFrame*>& aOutFramesWithProps) {
CRR_LOG("Computing rebuild regions for %zu frames:\n",
aModifiedFrames.Length());
nsTArray<nsIFrame*> extraFrames;
@ -1399,12 +1445,13 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
bool shouldBuildPartial = ShouldBuildPartial(modifiedFrames.Frames());
nsRect modifiedDirty;
AnimatedGeometryRoot* modifiedAGR = nullptr;
nsIFrame* modifiedAGR = nullptr;
PartialUpdateResult result = PartialUpdateResult::NoChange;
if (!shouldBuildPartial ||
!ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
&modifiedAGR, framesWithProps.Frames()) ||
!PreProcessDisplayList(&mList, modifiedAGR, result)) {
!PreProcessDisplayList(&mList, modifiedAGR, result,
mBuilder.RootReferenceFrame(), nullptr)) {
mBuilder.SetPartialBuildFailed(true);
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), nullptr);
mList.DeleteAll(&mBuilder);

Просмотреть файл

@ -215,13 +215,11 @@ struct RetainedDisplayListBuilder {
* aKeepLinked=true internally for sub-lists that can't be changed to keep the
* original list structure linked for fast re-use.
*/
bool PreProcessDisplayList(RetainedDisplayList* aList,
AnimatedGeometryRoot* aAGR,
PartialUpdateResult& aUpdated,
nsIFrame* aOuterFrame = nullptr,
uint32_t aCallerKey = 0,
uint32_t aNestingDepth = 0,
bool aKeepLinked = false);
bool PreProcessDisplayList(
RetainedDisplayList* aList, nsIFrame* aAGR, PartialUpdateResult& aUpdated,
nsIFrame* aAsyncAncestor, const ActiveScrolledRoot* aAsyncAncestorASR,
nsIFrame* aOuterFrame = nullptr, uint32_t aCallerKey = 0,
uint32_t aNestingDepth = 0, bool aKeepLinked = false);
/**
* Merges items from aNewList into non-invalidated items from aOldList and
@ -241,15 +239,14 @@ struct RetainedDisplayListBuilder {
nsDisplayItem* aOuterItem = nullptr);
bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
nsRect* aOutDirty,
AnimatedGeometryRoot** aOutModifiedAGR,
nsRect* aOutDirty, nsIFrame** aOutModifiedAGR,
nsTArray<nsIFrame*>& aOutFramesWithProps);
bool ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
nsIFrame* aStopAtFrame,
nsTArray<nsIFrame*>& aOutFramesWithProps,
const bool aStopAtStackingContext, nsRect* aOutDirty,
AnimatedGeometryRoot** aOutModifiedAGR);
nsIFrame** aOutModifiedAGR);
friend class MergeState;

Просмотреть файл

@ -1501,7 +1501,7 @@ class nsDisplayListBuilder {
OutOfFlowDisplayData)
struct DisplayListBuildingData {
RefPtr<AnimatedGeometryRoot> mModifiedAGR = nullptr;
nsIFrame* mModifiedAGR = nullptr;
nsRect mDirtyRect;
};
NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayListBuildingRect,
@ -2364,6 +2364,12 @@ class nsDisplayItem : public nsDisplayItemLink {
virtual bool CreatesStackingContextHelper() { return false; }
/**
* Returns true if this item can be moved asynchronously on the compositor,
* see RetainedDisplayListBuilder.cpp comments.
*/
virtual bool CanMoveAsync() { return false; }
protected:
// This is never instantiated directly (it has pure virtual methods), so no
// need to count constructors and destructors.
@ -5615,6 +5621,8 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer {
bool CreatesStackingContextHelper() override { return true; }
bool CanMoveAsync() override { return true; }
private:
NS_DISPLAY_ALLOW_CLONING()
@ -6443,6 +6451,11 @@ class nsDisplayTransform : public nsPaintedDisplayItem {
void WriteDebugInfo(std::stringstream& aStream) override;
bool CanMoveAsync() override {
return EffectCompositor::HasAnimationsForCompositor(
mFrame, DisplayItemType::TYPE_TRANSFORM);
}
/**
* This item is an additional item as the boundary between parent
* and child 3D rendering context.