Bug 1697979 - Part 1: Reuse previously built stacking context display items without merging r=mstange

Differential Revision: https://phabricator.services.mozilla.com/D128413
This commit is contained in:
Miko Mynttinen 2022-01-20 18:38:59 +00:00
Родитель a60cfaa300
Коммит 3882f995d9
17 изменённых файлов: 595 добавлений и 109 удалений

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

@ -586,6 +586,12 @@ void DisplayPortUtils::InvalidateForDisplayPortChange(
return;
}
if (StaticPrefs::layout_display_list_retain_sc()) {
// DisplayListBuildingDisplayPortRect property is not used when retain sc
// mode is enabled.
return;
}
bool found;
nsRect* rect = frame->GetProperty(
nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), &found);

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

@ -116,6 +116,24 @@ static void PrintDisplayItemTo(nsDisplayListBuilder* aBuilder,
area.width, area.height);
}
auto ReuseStateToString = [](nsDisplayItem::ReuseState aState) {
switch (aState) {
case nsDisplayItem::ReuseState::None:
return "None";
case nsDisplayItem::ReuseState::Reusable:
return "Reusable";
case nsDisplayItem::ReuseState::PreProcessed:
return "PreProcessed";
case nsDisplayItem::ReuseState::Reused:
return "Reused";
}
MOZ_ASSERT_UNREACHABLE();
};
aStream << nsPrintfCString(" reuse-state(%s)",
ReuseStateToString(aItem->GetReuseState()));
// Display item specific debug info
aItem->WriteDebugInfo(aStream);

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

@ -3130,15 +3130,6 @@ void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
MOZ_ASSERT(builder && list && metrics);
// Retained builder exists, but display list retaining is disabled.
if (!useRetainedBuilder && retainedBuilder) {
// Clear the modified frames lists and frame properties.
retainedBuilder->ClearFramesWithProps();
// Clear the retained display list.
retainedBuilder->List()->DeleteAll(retainedBuilder->Builder());
}
metrics->Reset();
metrics->StartBuild();
@ -3320,15 +3311,11 @@ void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
// If a pref is toggled that adds or removes display list items,
// we need to rebuild the display list. The pref may be toggled
// manually by the user, or during test setup.
bool shouldAttemptPartialUpdate = useRetainedBuilder;
if (builder->ShouldRebuildDisplayListDueToPrefChange()) {
shouldAttemptPartialUpdate = false;
}
// Attempt to do a partial build and merge into the existing list.
// This calls BuildDisplayListForStacking context on a subset of the
// viewport.
if (shouldAttemptPartialUpdate) {
if (useRetainedBuilder &&
!builder->ShouldRebuildDisplayListDueToPrefChange()) {
// Attempt to do a partial build and merge into the existing list.
// This calls BuildDisplayListForStacking context on a subset of the
// viewport.
updateState = retainedBuilder->AttemptPartialUpdate(aBackstop);
metrics->EndPartialBuild(updateState);
} else {
@ -3349,16 +3336,25 @@ void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
}
if (doFullRebuild) {
DL_LOGI("Starting full display list build, root frame: %p",
builder->RootReferenceFrame());
list->DeleteAll(builder);
list->RestoreState();
if (useRetainedBuilder) {
retainedBuilder->ClearFramesWithProps();
mozilla::RDLUtils::AssertFrameSubtreeUnmodified(
builder->RootReferenceFrame());
MOZ_ASSERT(retainedBuilder->List()->IsEmpty());
}
builder->ClearRetainedWindowRegions();
builder->ClearWillChangeBudgets();
builder->EnterPresShell(aFrame);
builder->SetDirtyRect(visibleRect);
DL_LOGI("Starting full display list build, root frame: %p",
builder->RootReferenceFrame());
aFrame->BuildDisplayListForStackingContext(builder, list);
AddExtraBackgroundItems(builder, list, aFrame, canvasArea,
visibleRegion, aBackstop);

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

@ -4488,23 +4488,25 @@ bool ScrollFrameHelper::DecideScrollableLayer(
content, &displayPort,
DisplayPortOptions().With(DisplayportRelativeTo::ScrollFrame));
auto OverrideDirtyRect = [&](const nsRect& aRect) {
*aDirtyRect = aRect;
if (aDirtyRectHasBeenOverriden) {
*aDirtyRectHasBeenOverriden = true;
}
};
if (usingDisplayPort) {
// Override the dirty rectangle if the displayport has been set.
*aVisibleRect = displayPort;
if (!aBuilder->IsPartialUpdate() || aBuilder->InInvalidSubtree() ||
if (aBuilder->IsReusingStackingContextItems() ||
!aBuilder->IsPartialUpdate() || aBuilder->InInvalidSubtree() ||
mOuter->IsFrameModified()) {
*aDirtyRect = displayPort;
if (aDirtyRectHasBeenOverriden) {
*aDirtyRectHasBeenOverriden = true;
}
OverrideDirtyRect(displayPort);
} else if (mOuter->HasOverrideDirtyRegion()) {
nsRect* rect = mOuter->GetProperty(
nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
if (rect) {
*aDirtyRect = *rect;
if (aDirtyRectHasBeenOverriden) {
*aDirtyRectHasBeenOverriden = true;
}
OverrideDirtyRect(*rect);
}
}
} else if (mIsRoot) {

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

@ -1043,7 +1043,7 @@ void nsIFrame::RemoveDisplayItemDataForDeletion() {
GetFrameName(name);
}
#endif
DL_LOGD("Removing display item data for frame %p (%s)", this,
DL_LOGV("Removing display item data for frame %p (%s)", this,
NS_ConvertUTF16toUTF8(name).get());
// Destroying a WebRenderUserDataTable can cause destruction of other objects
@ -1126,15 +1126,6 @@ void nsIFrame::MarkNeedsDisplayItemRebuild() {
return;
}
nsAutoString name;
#ifdef DEBUG_FRAME_DUMP
if (DL_LOG_TEST(LogLevel::Debug)) {
GetFrameName(name);
}
#endif
DL_LOGD("RDL - Rebuilding display items for frame %p (%s)", this,
NS_ConvertUTF16toUTF8(name).get());
nsIFrame* rootFrame = PresShell()->GetRootFrame();
MOZ_ASSERT(rootFrame);
@ -1142,8 +1133,17 @@ void nsIFrame::MarkNeedsDisplayItemRebuild() {
return;
}
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
nsAutoString name;
#ifdef DEBUG_FRAME_DUMP
if (DL_LOG_TEST(LogLevel::Debug)) {
GetFrameName(name);
}
#endif
DL_LOGV("RDL - Rebuilding display items for frame %p (%s)", this,
NS_ConvertUTF16toUTF8(name).get());
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
if (data->ModifiedFramesCount() >
StaticPrefs::layout_display_list_rebuild_frame_limit()) {
// If the modified frames count is above the rebuild limit, mark the root
@ -3089,6 +3089,41 @@ struct ContainerTracker {
bool mCreatedContainer = false;
};
/**
* Tries to reuse a top-level stacking context item from the previous paint.
* Returns true if an item was reused, otherwise false.
*/
bool TryToReuseStackingContextItem(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList, nsIFrame* aFrame) {
if (!aBuilder->IsForPainting() || !aBuilder->IsPartialUpdate() ||
aBuilder->InInvalidSubtree()) {
return false;
}
if (aFrame->IsFrameModified() || aFrame->HasModifiedDescendants()) {
return false;
}
auto& items = aFrame->DisplayItems();
auto* res = std::find_if(
items.begin(), items.end(),
[](nsDisplayItem* aItem) { return aItem->IsPreProcessed(); });
if (res == items.end()) {
return false;
}
nsDisplayItem* container = *res;
MOZ_ASSERT(!container->GetAbove());
MOZ_ASSERT(container->Frame() == aFrame);
DL_LOGD("RDL - Found SC item %p (%s) (frame: %p)", container,
container->Name(), container->Frame());
aList->AppendToTop(container);
aBuilder->ReuseDisplayItem(container);
return true;
}
void nsIFrame::BuildDisplayListForStackingContext(
nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
bool* aCreatedContainerItem) {
@ -3103,7 +3138,18 @@ void nsIFrame::BuildDisplayListForStackingContext(
});
AutoCheckBuilder check(aBuilder);
if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE)) return;
if (aBuilder->IsReusingStackingContextItems() &&
TryToReuseStackingContextItem(aBuilder, aList, this)) {
if (aCreatedContainerItem) {
*aCreatedContainerItem = true;
}
return;
}
if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE)) {
return;
}
const nsStyleDisplay* disp = StyleDisplay();
const nsStyleEffects* effects = StyleEffects();
@ -3258,7 +3304,8 @@ void nsIFrame::BuildDisplayListForStackingContext(
//
// These conditions should match |CanStoreDisplayListBuildingRect()| in
// RetainedDisplayListBuilder.cpp
if (aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
if (!aBuilder->IsReusingStackingContextItems() &&
aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
!IsFrameModified() && IsFixedPosContainingBlock() &&
!GetPrevContinuation() && !GetNextContinuation()) {
dirtyRect = nsRect();
@ -3439,6 +3486,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
MarkAbsoluteFramesForDisplayList(aBuilder);
aBuilder->Check();
BuildDisplayList(aBuilder, set);
SetBuiltDisplayList(true);
aBuilder->Check();
aBuilder->DisplayCaret(this, set.Outlines());
@ -3804,15 +3852,39 @@ void nsIFrame::BuildDisplayListForStackingContext(
CreateOwnLayerIfNeeded(aBuilder, &resultList,
nsDisplayOwnLayer::OwnLayerForStackingContext,
&createdOwnLayer);
if (createdOwnLayer) {
ct.TrackContainer(resultList.GetTop());
}
if (aBuilder->IsReusingStackingContextItems()) {
if (resultList.IsEmpty()) {
return;
}
nsDisplayItem* container = resultList.GetBottom();
if (resultList.Count() > 1 || container->Frame() != this) {
container = MakeDisplayItem<nsDisplayContainer>(
aBuilder, this, containerItemASR, &resultList);
} else {
container = resultList.RemoveBottom();
}
// Mark the outermost display item as reusable. These display items and
// their chidren can be reused during the next paint if no ancestor or
// descendant frames have been modified.
if (!container->IsReusedItem()) {
container->SetReusable();
}
aList->AppendToTop(container);
ct.TrackContainer(container);
} else {
aList->AppendToTop(&resultList);
}
if (aCreatedContainerItem) {
*aCreatedContainerItem = ct.mCreatedContainer;
}
aList->AppendToTop(&resultList);
}
static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
@ -3973,6 +4045,7 @@ void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
aBuilder->AdjustWindowDraggingRegion(aChild);
aBuilder->Check();
aChild->BuildDisplayList(aBuilder, aLists);
aChild->SetBuiltDisplayList(true);
aBuilder->Check();
aBuilder->DisplayCaret(aChild, aLists.Outlines());
#ifdef DEBUG
@ -4181,8 +4254,6 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
awayFromCommonPath = true;
}
child->SetBuiltDisplayList(true);
// Child is composited if it's transformed, partially transparent, or has
// SVG effects or a blend mode..
const nsStyleDisplay* disp = child->StyleDisplay();
@ -4264,7 +4335,8 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
child->BuildDisplayListForStackingContext(aBuilder, &list,
&builtContainerItem);
wrapListASR = contASRTracker.GetContainerASR();
if (aBuilder->GetCaretFrame() == child) {
if (!aBuilder->IsReusingStackingContextItems() &&
aBuilder->GetCaretFrame() == child) {
builtContainerItem = false;
}
} else {
@ -4279,6 +4351,7 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
}
child->MarkAbsoluteFramesForDisplayList(aBuilder);
child->SetBuiltDisplayList(true);
if (!awayFromCommonPath &&
// Some SVG frames might change opacity without invalidating the frame,
@ -4332,10 +4405,10 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
buildingForChild.RestoreBuildingInvisibleItemsValue();
if (isPositioned || isStackingContext) {
// Genuine stacking contexts, and positioned pseudo-stacking-contexts,
// go in this level.
if (!list.IsEmpty()) {
if (!list.IsEmpty()) {
if (isPositioned || isStackingContext) {
// Genuine stacking contexts, and positioned pseudo-stacking-contexts,
// go in this level.
nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
builtContainerItem);
if (isSVG) {
@ -4343,14 +4416,12 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
} else {
aLists.PositionedDescendants()->AppendToTop(item);
}
}
} else if (!isSVG && disp->IsFloating(child)) {
if (!list.IsEmpty()) {
} else if (!isSVG && disp->IsFloating(child)) {
aLists.Floats()->AppendToTop(
WrapInWrapList(aBuilder, child, &list, wrapListASR));
} else {
aLists.Content()->AppendToTop(&list);
}
} else {
aLists.Content()->AppendToTop(&list);
}
// We delay placing the positioned descendants of positioned frames to here,
// because in the absence of z-index this is the correct order for them.
@ -7985,6 +8056,14 @@ void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
aTo += ToString(pseudoType).c_str();
}
aTo += "]";
if (IsFrameModified()) {
aTo += nsPrintfCString(" modified");
}
if (HasModifiedDescendants()) {
aTo += nsPrintfCString(" has-modified-descendants");
}
}
void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {

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

@ -599,6 +599,7 @@ class nsIFrame : public nsQueryFrame {
mForceDescendIntoIfVisible(false),
mBuiltDisplayList(false),
mFrameIsModified(false),
mHasModifiedDescendants(false),
mHasOverrideDirtyRegion(false),
mMayHaveWillChangeBudget(false),
mIsPrimaryFrame(false),
@ -4893,6 +4894,11 @@ class nsIFrame : public nsQueryFrame {
mFrameIsModified = aFrameIsModified;
}
bool HasModifiedDescendants() const { return mHasModifiedDescendants; }
void SetHasModifiedDescendants(const bool aHasModifiedDescendants) {
mHasModifiedDescendants = aHasModifiedDescendants;
}
bool HasOverrideDirtyRegion() const { return mHasOverrideDirtyRegion; }
void SetHasOverrideDirtyRegion(const bool aHasDirtyRegion) {
mHasOverrideDirtyRegion = aHasDirtyRegion;
@ -5128,8 +5134,22 @@ class nsIFrame : public nsQueryFrame {
*/
bool mBuiltDisplayList : 1;
/**
* True if the frame has been marked modified by
* |MarkNeedsDisplayItemRebuild()|, usually due to a style change or reflow.
*/
bool mFrameIsModified : 1;
/**
* True if the frame has modified descendants. Set before display list
* preprocessing and only used during partial display list builds.
*/
bool mHasModifiedDescendants : 1;
/**
* Used by merging based retained display lists to restrict the dirty area
* during partial display list builds.
*/
bool mHasOverrideDirtyRegion : 1;
/**

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

@ -10293,8 +10293,9 @@ void nsTextFrame::ToCString(nsCString& aBuf) const {
return;
}
const uint32_t contentLength = AssertedCast<uint32_t>(GetContentLength());
if (0 == contentLength) {
const int32_t length = GetContentEnd() - mContentOffset;
if (length <= 0) {
// Negative lengths are possible during invalidation.
return;
}

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

@ -7,6 +7,7 @@
#include "RetainedDisplayListBuilder.h"
#include "mozilla/Attributes.h"
#include "mozilla/StaticPrefs_layout.h"
#include "nsIFrame.h"
#include "nsIFrameInlines.h"
@ -112,6 +113,7 @@ static void MarkFramesWithItemsAndImagesModified(nsDisplayList* aList) {
}
if (invalidate) {
DL_LOGV("Invalidating item %p (%s)", i, i->Name());
i->FrameForInvalidation()->MarkNeedsDisplayItemRebuild();
if (i->GetDependentFrame()) {
i->GetDependentFrame()->MarkNeedsDisplayItemRebuild();
@ -355,8 +357,8 @@ bool RetainedDisplayListBuilder::PreProcessDisplayList(
return true;
}
void RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(
nsDisplayItem* aItem) {
void IncrementPresShellPaintCount(nsDisplayListBuilder* aBuilder,
nsDisplayItem* aItem) {
MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
nsSubDocumentFrame* subDocFrame =
@ -366,7 +368,12 @@ void RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(
PresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
MOZ_ASSERT(presShell);
mBuilder.IncrementPresShellPaintCount(presShell);
aBuilder->IncrementPresShellPaintCount(presShell);
}
void RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(
nsDisplayItem* aItem) {
IncrementPresShellPaintCount(&mBuilder, aItem);
}
static Maybe<const ActiveScrolledRoot*> SelectContainerASR(
@ -1401,6 +1408,7 @@ static void ClearFrameProps(nsTArray<nsIFrame*>& aFrames) {
}
f->SetFrameIsModified(false);
f->SetHasModifiedDescendants(false);
}
}
@ -1424,6 +1432,232 @@ void RetainedDisplayListBuilder::ClearFramesWithProps() {
&framesWithProps.Frames());
}
namespace RDLUtils {
MOZ_NEVER_INLINE_DEBUG void AssertFrameSubtreeUnmodified(
const nsIFrame* aFrame) {
for (const auto& childList : aFrame->ChildLists()) {
for (nsIFrame* child : childList.mList) {
MOZ_ASSERT(!aFrame->IsFrameModified());
MOZ_ASSERT(!aFrame->HasModifiedDescendants());
AssertFrameSubtreeUnmodified(child);
}
}
}
MOZ_NEVER_INLINE_DEBUG void AssertDisplayListUnmodified(nsDisplayList* aList) {
for (nsDisplayItem* item : *aList) {
AssertDisplayItemUnmodified(item);
}
}
MOZ_NEVER_INLINE_DEBUG void AssertDisplayItemUnmodified(nsDisplayItem* aItem) {
MOZ_ASSERT(!aItem->HasDeletedFrame());
MOZ_ASSERT(!AnyContentAncestorModified(aItem->FrameForInvalidation()));
if (aItem->GetChildren()) {
AssertDisplayListUnmodified(aItem->GetChildren());
}
}
} // namespace RDLUtils
namespace RDL {
void MarkAncestorFrames(nsIFrame* aFrame,
nsTArray<nsIFrame*>& aOutFramesWithProps) {
nsIFrame* frame = nsLayoutUtils::GetDisplayListParent(aFrame);
while (frame && !frame->HasModifiedDescendants()) {
aOutFramesWithProps.AppendElement(frame);
frame->SetHasModifiedDescendants(true);
frame = nsLayoutUtils::GetDisplayListParent(frame);
}
}
/**
* Iterates over the modified frames array and updates the frame tree flags
* so that container frames know whether they have modified descendant frames.
* Frames that were marked modified are added to |aOutFramesWithProps|, so that
* the modified status can be cleared after the display list build.
*/
void MarkAllAncestorFrames(const nsTArray<nsIFrame*>& aModifiedFrames,
nsTArray<nsIFrame*>& aOutFramesWithProps) {
nsAutoString frameName;
DL_LOGI("RDL - Modified frames: %zu", aModifiedFrames.Length());
for (nsIFrame* frame : aModifiedFrames) {
#ifdef DEBUG
frame->GetFrameName(frameName);
#endif
DL_LOGV("RDL - Processing modified frame: %p (%s)", frame,
NS_ConvertUTF16toUTF8(frameName).get());
MarkAncestorFrames(frame, aOutFramesWithProps);
}
}
/**
* Marks the given display item |aItem| as reuseable container, and updates the
* bounds in case some child items were destroyed.
*/
MOZ_NEVER_INLINE_DEBUG void ReuseStackingContextItem(
nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
aItem->SetPreProcessed();
if (aItem->HasChildren()) {
aItem->UpdateBounds(aBuilder);
}
DL_LOGD("Retaining display item %p", aItem);
}
bool IsSupportedFrameType(const nsIFrame* aFrame) {
// The way table backgrounds are handled makes these frames incompatible with
// this retained display list approach.
if (aFrame->IsTableColFrame()) {
return false;
}
if (aFrame->IsTableColGroupFrame()) {
return false;
}
if (aFrame->IsTableRowFrame()) {
return false;
}
if (aFrame->IsTableRowGroupFrame()) {
return false;
}
if (aFrame->IsTableCellFrame()) {
return false;
}
// Everything else should work.
return true;
}
bool IsReuseableStackingContextItem(nsDisplayItem* aItem) {
if (!IsSupportedFrameType(aItem->Frame())) {
return false;
}
if (!aItem->IsReusable()) {
return false;
}
const nsIFrame* frame = aItem->FrameForInvalidation();
return !frame->HasModifiedDescendants() && !frame->GetPrevContinuation() &&
!frame->GetNextContinuation();
}
/**
* Recursively visits every display item of the display list and destroys all
* display items that depend on deleted or modified frames.
* The stacking context display items for unmodified frame subtrees are kept
* linked and collected in given |aOutItems| array.
*/
void CollectStackingContextItems(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
nsTArray<nsDisplayItem*>& aOutItems,
nsIFrame* aOuterFrame, int aDepth = 0,
bool aParentReused = false) {
nsDisplayList out;
while (nsDisplayItem* item = aList->RemoveBottom()) {
if (DL_LOG_TEST(LogLevel::Debug)) {
DL_LOGD(
"%*s Preprocessing item %p (%s) (frame: %p) "
"(children: %d) (depth: %d) (parentReused: %d)",
aDepth, "", item, item->Name(),
item->HasDeletedFrame() ? nullptr : item->Frame(),
item->GetChildren() ? item->GetChildren()->Count() : 0, aDepth,
aParentReused);
}
if (!item->CanBeReused() || item->HasDeletedFrame() ||
AnyContentAncestorModified(item->FrameForInvalidation(), aOuterFrame)) {
DL_LOGD("%*s Deleted modified or temporary item %p", aDepth, "", item);
item->Destroy(aBuilder);
continue;
}
MOZ_ASSERT(!AnyContentAncestorModified(item->FrameForInvalidation()));
MOZ_ASSERT(!item->IsPreProcessed());
item->InvalidateCachedChildInfo(aBuilder);
item->SetMergedPreProcessed(false, true);
item->SetReused(true);
const bool isStackingContextItem = IsReuseableStackingContextItem(item);
if (item->GetChildren()) {
CollectStackingContextItems(aBuilder, item->GetChildren(), aOutItems,
item->Frame(), aDepth + 1,
aParentReused || isStackingContextItem);
}
if (aParentReused) {
// Keep the contents of the current container item linked.
RDLUtils::AssertDisplayItemUnmodified(item);
out.AppendToTop(item);
} else if (isStackingContextItem) {
// |item| is a stacking context item that can be reused.
aOutItems.AppendElement(item);
ReuseStackingContextItem(aBuilder, item);
} else {
// |item| is inside a container item that will be destroyed later.
DL_LOGD("%*s Deleted unused item %p", aDepth, "", item);
item->Destroy(aBuilder);
continue;
}
if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
IncrementPresShellPaintCount(aBuilder, item);
}
}
aList->AppendToTop(&out);
aList->RestoreState();
}
/**
* Destroys the retained stacking context items that have not been reused and
* clears the array.
*/
void ClearPreviousItems(nsDisplayListBuilder* aBuilder,
nsTArray<nsDisplayItem*>& aItems) {
const size_t total = aItems.Length();
size_t reused = 0;
for (auto* item : aItems) {
if (item->IsReusedItem()) {
reused++;
item->SetReusable();
} else {
item->Destroy(aBuilder);
}
}
DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
aItems.Clear();
}
} // namespace RDL
bool RetainedDisplayListBuilder::TrySimpleUpdate(
const nsTArray<nsIFrame*>& aModifiedFrames,
nsTArray<nsIFrame*>& aOutFramesWithProps) {
if (!mBuilder.IsReusingStackingContextItems()) {
return false;
}
MOZ_ASSERT(mPreviousItems.IsEmpty());
RDL::MarkAllAncestorFrames(aModifiedFrames, aOutFramesWithProps);
RDL::CollectStackingContextItems(&mBuilder, &mList, mPreviousItems,
RootReferenceFrame());
return true;
}
PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
nscolor aBackstop) {
DL_LOGI("RDL - AttemptPartialUpdate, root frame: %p", RootReferenceFrame());
@ -1437,32 +1671,43 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
InvalidateCaretFramesIfNeeded();
mBuilder.EnterPresShell(mBuilder.RootReferenceFrame());
// We set the override dirty regions during ComputeRebuildRegion or in
// DisplayPortUtils::InvalidateForDisplayPortChange. The display port change
// also marks the frame modified, so those regions are cleared here as well.
AutoClearFramePropsArray modifiedFrames(64);
AutoClearFramePropsArray framesWithProps;
AutoClearFramePropsArray framesWithProps(64);
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(),
&framesWithProps.Frames());
// Do not allow partial builds if the |ShouldBuildPartial()| heuristic fails.
bool shouldBuildPartial = ShouldBuildPartial(modifiedFrames.Frames());
if (!ShouldBuildPartial(modifiedFrames.Frames())) {
// Do not allow partial builds if the |ShouldBuildPartial()| heuristic
// fails.
mBuilder.SetPartialBuildFailed(true);
return PartialUpdateResult::Failed;
}
nsRect modifiedDirty;
nsDisplayList modifiedDL;
nsIFrame* modifiedAGR = nullptr;
PartialUpdateResult result = PartialUpdateResult::NoChange;
if (!shouldBuildPartial ||
!ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
&modifiedAGR, framesWithProps.Frames()) ||
!PreProcessDisplayList(&mList, modifiedAGR, result,
mBuilder.RootReferenceFrame(), nullptr)) {
mBuilder.SetPartialBuildFailed(true);
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), nullptr);
mList.DeleteAll(&mBuilder);
return PartialUpdateResult::Failed;
const bool simpleUpdate =
TrySimpleUpdate(modifiedFrames.Frames(), framesWithProps.Frames());
mBuilder.EnterPresShell(RootReferenceFrame());
if (!simpleUpdate) {
if (!ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
&modifiedAGR, framesWithProps.Frames()) ||
!PreProcessDisplayList(&mList, modifiedAGR, result,
RootReferenceFrame(), nullptr)) {
DL_LOGI("RDL - Partial update aborted");
mBuilder.SetPartialBuildFailed(true);
mBuilder.LeavePresShell(RootReferenceFrame(), nullptr);
mList.DeleteAll(&mBuilder);
return PartialUpdateResult::Failed;
}
} else {
modifiedDirty = mBuilder.GetVisibleRect();
}
// This is normally handled by EnterPresShell, but we skipped it so that we
@ -1499,6 +1744,7 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
if (mBuilder.PartialBuildFailed()) {
DL_LOGI("RDL - Partial update failed!");
mBuilder.LeavePresShell(RootReferenceFrame(), nullptr);
RDL::ClearPreviousItems(&mBuilder, mPreviousItems);
mList.DeleteAll(&mBuilder);
modifiedDL.DeleteAll(&mBuilder);
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::Content;
@ -1519,15 +1765,26 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
// we call RestoreState on nsDisplayWrapList it resets the clip to the base
// clip, and we need the UpdateBounds call (within MergeDisplayLists) to
// move it to the correct inner clip.
Maybe<const ActiveScrolledRoot*> dummy;
if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
if (!simpleUpdate) {
Maybe<const ActiveScrolledRoot*> dummy;
if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
result = PartialUpdateResult::Updated;
}
} else {
MOZ_ASSERT(mList.IsEmpty());
mList = std::move(modifiedDL);
RDL::ClearPreviousItems(&mBuilder, mPreviousItems);
result = PartialUpdateResult::Updated;
}
// printf_stderr("Painting --- Merged list:\n");
// nsIFrame::PrintDisplayList(&mBuilder, mList);
#if 0
if (DL_LOG_TEST(LogLevel::Verbose)) {
printf_stderr("Painting --- Display list:\n");
nsIFrame::PrintDisplayList(&mBuilder, mList);
}
#endif
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
mBuilder.LeavePresShell(RootReferenceFrame(), List());
return result;
}

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

@ -15,6 +15,9 @@ class nsWindowSizes;
namespace mozilla {
class nsDisplayItem;
class nsDisplayList;
/**
* RetainedDisplayListData contains frame invalidation information. It is stored
* in root frames, and used by RetainedDisplayListBuilder.
@ -254,15 +257,33 @@ struct RetainedDisplayListBuilder {
nsIFrame** aOutModifiedAGR);
nsIFrame* RootReferenceFrame() { return mBuilder.RootReferenceFrame(); }
/**
* Tries to perform a simple partial display list build without display list
* merging. In this mode, only the top-level stacking context items and their
* contents are reused, when the frame subtree has not been modified.
*/
bool TrySimpleUpdate(const nsTArray<nsIFrame*>& aModifiedFrames,
nsTArray<nsIFrame*>& aOutFramesWithProps);
friend class MergeState;
nsDisplayListBuilder mBuilder;
RetainedDisplayList mList;
nsRect mPreviousVisibleRect;
WeakFrame mPreviousCaret;
RetainedDisplayListMetrics mMetrics;
// Stores reusable items collected during display list preprocessing.
nsTArray<nsDisplayItem*> mPreviousItems;
};
namespace RDLUtils {
void AssertFrameSubtreeUnmodified(const nsIFrame* aFrame);
void AssertDisplayItemUnmodified(nsDisplayItem* aItem);
void AssertDisplayListUnmodified(nsDisplayList* aList);
} // namespace RDLUtils
} // namespace mozilla
#endif // RETAINEDDISPLAYLISTBUILDER_H_

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

@ -126,7 +126,7 @@ void AssertUniqueItem(nsDisplayItem* aItem) {
for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
if (i->IsPreProcessedItem()) {
if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
continue;
}
MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
@ -694,6 +694,8 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
"Check TYPE_MAX should not overflow");
mIsForContent = XRE_IsContentProcess();
mIsReusingStackingContextItems =
mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
}
static PresShell* GetFocusedPresShell() {
@ -2006,6 +2008,20 @@ void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
}
}
void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
const auto* previous = mCurrentContainerASR;
const auto* asr = aItem->GetActiveScrolledRoot();
mCurrentContainerASR =
ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
if (previous != mCurrentContainerASR) {
DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
mCurrentContainerASR);
}
aItem->SetReusedItem();
}
void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
aDestination.BorderBackground()->AppendToTop(BorderBackground());
aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());

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

@ -1691,6 +1691,20 @@ class nsDisplayListBuilder {
*/
nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);
/**
* Returns true if this is a retained builder and reuse stacking contexts
* mode is enabled by pref.
*/
bool IsReusingStackingContextItems() const {
return mIsReusingStackingContextItems;
}
/**
* Marks the given display item |aItem| as reused, and updates the necessary
* display list builder state.
*/
void ReuseDisplayItem(nsDisplayItem* aItem);
private:
bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
const nsRect& aVisibleRect,
@ -1885,6 +1899,7 @@ class nsDisplayListBuilder {
gfx::CompositorHitTestInfo mCompositorHitTestInfo;
bool mIsForContent;
bool mIsReusingStackingContextItems;
};
class nsDisplayItem;
@ -2756,6 +2771,44 @@ class nsDisplayItem : public nsDisplayItemLink {
virtual const HitTestInfo& GetHitTestInfo() { return HitTestInfo::Empty(); }
enum class ReuseState : uint8_t {
None,
// Set during display list building.
Reusable,
// Set during display list preprocessing.
PreProcessed,
// Set during partial display list build.
Reused,
};
void SetReusable() {
MOZ_ASSERT(mReuseState == ReuseState::None ||
mReuseState == ReuseState::Reused);
mReuseState = ReuseState::Reusable;
}
bool IsReusable() const { return mReuseState == ReuseState::Reusable; }
void SetPreProcessed() {
MOZ_ASSERT(mReuseState == ReuseState::Reusable);
mReuseState = ReuseState::PreProcessed;
}
bool IsPreProcessed() const {
return mReuseState == ReuseState::PreProcessed;
}
void SetReusedItem() {
MOZ_ASSERT(mReuseState == ReuseState::PreProcessed);
mReuseState = ReuseState::Reused;
}
bool IsReusedItem() const { return mReuseState == ReuseState::Reused; }
void ResetReuseState() { mReuseState = ReuseState::None; }
ReuseState GetReuseState() const { return mReuseState; }
nsIFrame* mFrame; // 8
private:
@ -2784,7 +2837,7 @@ class nsDisplayItem : public nsDisplayItemLink {
DisplayItemType mType = DisplayItemType::TYPE_ZERO; // 1
uint8_t mExtraPageForPageNum = 0; // 1
uint16_t mPerFrameIndex = 0; // 2
// 2 free bytes here
ReuseState mReuseState = ReuseState::None;
OldListIndex mOldListIndex; // 4
uintptr_t mOldList = 0; // 8
@ -3484,6 +3537,13 @@ class RetainedDisplayList : public nsDisplayList {
return *this;
}
RetainedDisplayList& operator=(nsDisplayList&& aOther) {
MOZ_ASSERT(!Count(), "Can only move into an empty list!");
MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
AppendToTop(&aOther);
return *this;
}
void DeleteAll(nsDisplayListBuilder* aBuilder) override {
for (OldItemInfo& i : mOldItems) {
if (i.mItem && i.mOwnsItem) {
@ -3682,22 +3742,22 @@ class nsDisplayReflowCount : public nsPaintedDisplayItem {
nscolor mColor;
};
# define DO_GLOBAL_REFLOW_COUNT_DSP(_name) \
PR_BEGIN_MACRO \
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
PresShell()->IsPaintingFrameCounts()) { \
aLists.Outlines()->AppendNewToTop<nsDisplayReflowCount>(aBuilder, this, \
_name); \
} \
# define DO_GLOBAL_REFLOW_COUNT_DSP(_name) \
PR_BEGIN_MACRO \
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
PresShell()->IsPaintingFrameCounts()) { \
aLists.Outlines()->AppendNewToTop<mozilla::nsDisplayReflowCount>( \
aBuilder, this, _name); \
} \
PR_END_MACRO
# define DO_GLOBAL_REFLOW_COUNT_DSP_COLOR(_name, _color) \
PR_BEGIN_MACRO \
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
PresShell()->IsPaintingFrameCounts()) { \
aLists.Outlines()->AppendNewToTop<nsDisplayReflowCount>(aBuilder, this, \
_name, _color); \
} \
# define DO_GLOBAL_REFLOW_COUNT_DSP_COLOR(_name, _color) \
PR_BEGIN_MACRO \
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
PresShell()->IsPaintingFrameCounts()) { \
aLists.Outlines()->AppendNewToTop<mozilla::nsDisplayReflowCount>( \
aBuilder, this, _name, _color); \
} \
PR_END_MACRO
/*
@ -4099,7 +4159,7 @@ class nsDisplayBackgroundImage : public nsPaintedDisplayItem {
nsIFrame* GetDependentFrame() override { return mDependentFrame; }
void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
if (!aBuilder->IsRetainingDisplayList()) {
if (!aBuilder->IsRetainingDisplayList() || mDependentFrame == aFrame) {
return;
}
mDependentFrame = aFrame;
@ -4388,7 +4448,7 @@ class nsDisplayBackgroundColor : public nsPaintedDisplayItem {
nsIFrame* GetDependentFrame() override { return mDependentFrame; }
void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
if (!aBuilder->IsRetainingDisplayList()) {
if (!aBuilder->IsRetainingDisplayList() || mDependentFrame == aFrame) {
return;
}
mDependentFrame = aFrame;

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

@ -340,8 +340,9 @@ void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey,
bool aRebuildDisplayItems) {
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
if (GetTableFrame()->IsBorderCollapse()) {
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
aDisplayItemKey, false);
aDisplayItemKey, rebuild);
}
}
@ -354,7 +355,7 @@ void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect,
// we get an inactive layer created and this is computed
// within FrameLayerBuilder
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
false);
aRebuildDisplayItems);
}
bool nsTableCellFrame::ShouldPaintBordersAndBackgrounds() const {

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

@ -188,8 +188,9 @@ void nsTableColFrame::InvalidateFrame(uint32_t aDisplayItemKey,
bool aRebuildDisplayItems) {
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
if (GetTableFrame()->IsBorderCollapse()) {
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
aDisplayItemKey, false);
aDisplayItemKey, rebuild);
}
}
@ -203,5 +204,5 @@ void nsTableColFrame::InvalidateFrameWithRect(const nsRect& aRect,
// we get an inactive layer created and this is computed
// within FrameLayerBuilder
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
false);
aRebuildDisplayItems);
}

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

@ -423,8 +423,9 @@ void nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
bool aRebuildDisplayItems) {
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
if (GetTableFrame()->IsBorderCollapse()) {
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
aDisplayItemKey, false);
aDisplayItemKey, rebuild);
}
}
@ -437,7 +438,7 @@ void nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
// we get an inactive layer created and this is computed
// within FrameLayerBuilder
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
false);
aRebuildDisplayItems);
}
#ifdef DEBUG_FRAME_DUMP

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

@ -1388,8 +1388,9 @@ void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey,
bool aRebuildDisplayItems) {
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
if (GetTableFrame()->IsBorderCollapse()) {
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
aDisplayItemKey, false);
aDisplayItemKey, rebuild);
}
}
@ -1402,7 +1403,7 @@ void nsTableRowFrame::InvalidateFrameWithRect(const nsRect& aRect,
// we get an inactive layer created and this is computed
// within FrameLayerBuilder
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
false);
aRebuildDisplayItems);
}
/* ----- global methods ----- */

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

@ -1933,8 +1933,9 @@ void nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
bool aRebuildDisplayItems) {
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
if (GetTableFrame()->IsBorderCollapse()) {
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
aDisplayItemKey, false);
aDisplayItemKey, rebuild);
}
}
@ -1947,5 +1948,5 @@ void nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
// we get an inactive layer created and this is computed
// within FrameLayerBuilder
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
false);
aRebuildDisplayItems);
}

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

@ -7754,6 +7754,11 @@
value: true
mirror: always
- name: layout.display-list.retain.sc
type: RelaxedAtomicBool
value: false
mirror: always
# Set the maximum number of modified frames allowed before doing a full
# display list rebuild.
- name: layout.display-list.rebuild-frame-limit