Skip to content

Commit

Permalink
Bug 1872487. Create less WebRenderLayerScrollData. r=botond
Browse files Browse the repository at this point in the history
In order to create less WebRenderLayerScrollData currently we use a deferred transform item

https://searchfox.org/mozilla-central/rev/593c49fa812ceb4be45fcea7c9e90d15f59edb70/gfx/layers/wr/StackingContextHelper.h#82

We don't need a WebRenderLayerScrollData for every transform because a lot of transforms don't contain any ASRs, so the created WebRenderLayerScrollData would be useless.

However this optimization can lead to us creating a lot more WebRenderLayerScrollData later if the transform does contain ASRs. For example, if there is a transform, and then inside that is a ASR, and inside the ASR is a lot of small transforms, we end up having to create a WebRenderLayerScrollData for every little transform which don't contain any ASRs. This is doing the opposite of what the optimization intended.

WebRenderLayerScrollData creation happens during the CreateWebRenderCommands phase, so the display list build phase is complete, and we can tell cheaply if a transform contains any ASRs during display list building. So we just record that and use that to inform our decision about when to defer the transform item or not.

This optimization drastically reduces the total number of WebRenderLayerScrollData that we create during a full run of speedometer3 (summing the number created each paint over every paint). In my testing it went from 12-13k to 2-3k. Mostly subtests fell into two buckets: a single digit number of WebRenderLayerScrollData to begin with and this patch didn't change that, and 100 WebRenderLayerScrollData down to single digits after this patch. So the savings are concentrated in a few subtests that hit the described behaviour above.

I compared a profile before and after this patch of 10 runs of speedometer3, this patch saved 100 samples/ms serializing WebRenderLayerScrollData, which was what I expected based on how long serialization took before the patch combined with how many WebRenderLayerScrollData we were avoiding. The whole run took about 100,000 samples/ms, so this should hopefully be good for about 0.1% improvment. There is also potential savings in other areas outside of serialization step but that was a little harder to measure.

Differential Revision: https://phabricator.services.mozilla.com/D197446
  • Loading branch information
tnikkel committed Feb 29, 2024
1 parent 103c9ce commit 64e0a7e
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 19 deletions.
24 changes: 16 additions & 8 deletions layout/generic/nsIFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3409,6 +3409,9 @@ void nsIFrame::BuildDisplayListForStackingContext(
ApplyClipProp(transformedCssClip);
}

uint32_t numActiveScrollframesEncounteredBefore =
aBuilder->GetNumActiveScrollframesEncountered();

nsDisplayListCollection set(aBuilder);
Maybe<nsRect> clipForMask;
{
Expand Down Expand Up @@ -3692,17 +3695,22 @@ void nsIFrame::BuildDisplayListForStackingContext(
if (transformItem) {
resultList.AppendToTop(transformItem);
createdContainer = true;
}

if (hasPerspective) {
transformItem->MarkWithAssociatedPerspective();
if (numActiveScrollframesEncounteredBefore !=
aBuilder->GetNumActiveScrollframesEncountered()) {
transformItem->SetContainsASRs(true);
}

if (hasPerspective) {
transformItem->MarkWithAssociatedPerspective();

if (clipCapturedBy == ContainerItemType::Perspective) {
clipState.Restore();
if (clipCapturedBy == ContainerItemType::Perspective) {
clipState.Restore();
}
resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
&resultList);
createdContainer = true;
}
resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
&resultList);
createdContainer = true;
}
}

Expand Down
11 changes: 11 additions & 0 deletions layout/painting/RetainedDisplayListBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,17 @@ class MergeState {
// current ASR, which gets reset during RestoreState(), so we always need
// to run it again.
aOutItem->UpdateBounds(mBuilder->Builder());

if (aOutItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
MOZ_ASSERT(!aNewItem ||
aNewItem->GetType() == DisplayItemType::TYPE_TRANSFORM);
MOZ_ASSERT(aOldItem->GetType() == DisplayItemType::TYPE_TRANSFORM);
static_cast<nsDisplayTransform*>(aOutItem)->SetContainsASRs(
static_cast<nsDisplayTransform*>(aOldItem)->GetContainsASRs() ||
(aNewItem
? static_cast<nsDisplayTransform*>(aNewItem)->GetContainsASRs()
: false));
}
}

bool ShouldUseNewItem(nsDisplayItem* aNewItem) {
Expand Down
28 changes: 18 additions & 10 deletions layout/painting/nsDisplayList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,11 @@ void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
UpdateShouldBuildAsyncZoomContainer();
}

void nsDisplayListBuilder::ForceLayerForScrollParent() {
mForceLayerForScrollParent = true;
mNumActiveScrollframesEncountered++;
}

void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
const Document* document = mReferenceFrame->PresContext()->Document();
mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
Expand Down Expand Up @@ -5974,7 +5979,8 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
mPrerenderDecision(PrerenderDecision::No),
mIsTransformSeparator(true),
mHasTransformGetter(false),
mHasAssociatedPerspective(false) {
mHasAssociatedPerspective(false),
mContainsASRs(false) {
MOZ_COUNT_CTOR(nsDisplayTransform);
MOZ_ASSERT(aFrame, "Must have a frame!");
Init(aBuilder, aList);
Expand All @@ -5990,7 +5996,8 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
mPrerenderDecision(aPrerenderDecision),
mIsTransformSeparator(false),
mHasTransformGetter(false),
mHasAssociatedPerspective(false) {
mHasAssociatedPerspective(false),
mContainsASRs(false) {
MOZ_COUNT_CTOR(nsDisplayTransform);
MOZ_ASSERT(aFrame, "Must have a frame!");
SetReferenceFrameToAncestor(aBuilder);
Expand All @@ -6007,7 +6014,8 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
mPrerenderDecision(PrerenderDecision::No),
mIsTransformSeparator(false),
mHasTransformGetter(true),
mHasAssociatedPerspective(false) {
mHasAssociatedPerspective(false),
mContainsASRs(false) {
MOZ_COUNT_CTOR(nsDisplayTransform);
MOZ_ASSERT(aFrame, "Must have a frame!");
MOZ_ASSERT(aFrame->GetTransformGetter());
Expand Down Expand Up @@ -6640,12 +6648,12 @@ bool nsDisplayTransform::CreateWebRenderCommands(
key};

nsDisplayTransform* deferredTransformItem = nullptr;
if (!mFrame->ChildrenHavePerspective()) {
if (ShouldDeferTransform()) {
// If it has perspective, we create a new scroll data via the
// UpdateScrollData call because that scenario is more complex. Otherwise
// we can just stash the transform on the StackingContextHelper and
// apply it to any scroll data that are created inside this
// nsDisplayTransform.
// UpdateScrollData call because that scenario is more complex. Otherwise,
// if we don't contain any ASRs then just stash the transform on the
// StackingContextHelper and apply it to any scroll data that are created
// inside this nsDisplayTransform.
deferredTransformItem = this;
}

Expand Down Expand Up @@ -6696,14 +6704,14 @@ bool nsDisplayTransform::CreateWebRenderCommands(

bool nsDisplayTransform::UpdateScrollData(
WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) {
if (!mFrame->ChildrenHavePerspective()) {
if (ShouldDeferTransform()) {
// This case is handled in CreateWebRenderCommands by stashing the transform
// on the stacking context.
return false;
}
if (aLayerData) {
aLayerData->SetTransform(GetTransform().GetMatrix());
aLayerData->SetTransformIsPerspective(true);
aLayerData->SetTransformIsPerspective(mFrame->ChildrenHavePerspective());
}
return true;
}
Expand Down
14 changes: 13 additions & 1 deletion layout/painting/nsDisplayList.h
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,10 @@ class nsDisplayListBuilder {
* a displayport, and for scroll handoff to work properly the ancestor
* scrollframes should also get their own scrollable layers.
*/
void ForceLayerForScrollParent() { mForceLayerForScrollParent = true; }
void ForceLayerForScrollParent();
uint32_t GetNumActiveScrollframesEncountered() const {
return mNumActiveScrollframesEncountered;
}
/**
* Set the flag that indicates there is a non-minimal display port in the
* current subtree. This is used to determine display port expiry.
Expand Down Expand Up @@ -1848,6 +1851,8 @@ class nsDisplayListBuilder {
nsDisplayListBuilderMode mMode;
static uint32_t sPaintSequenceNumber;

uint32_t mNumActiveScrollframesEncountered = 0;

bool mContainsBlendMode;
bool mIsBuildingScrollbar;
bool mCurrentScrollbarWillHaveLayer;
Expand Down Expand Up @@ -6414,6 +6419,12 @@ class nsDisplayTransform : public nsPaintedDisplayItem {

bool CreatesStackingContextHelper() override { return true; }

void SetContainsASRs(bool aContainsASRs) { mContainsASRs = aContainsASRs; }
bool GetContainsASRs() const { return mContainsASRs; }
bool ShouldDeferTransform() const {
return !mFrame->ChildrenHavePerspective() && !mContainsASRs;
}

private:
void ComputeBounds(nsDisplayListBuilder* aBuilder);
nsRect TransformUntransformedBounds(nsDisplayListBuilder* aBuilder,
Expand Down Expand Up @@ -6459,6 +6470,7 @@ class nsDisplayTransform : public nsPaintedDisplayItem {
// True if this item is created together with `nsDisplayPerspective`
// from the same CSS stacking context.
bool mHasAssociatedPerspective : 1;
bool mContainsASRs : 1;
};

/* A display item that applies a perspective transformation to a single
Expand Down

0 comments on commit 64e0a7e

Please sign in to comment.