Skip to content

Do not rebuild Routes when a new opaque Route is pushed on top #48900

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 22, 2020

Conversation

goderbauer
Copy link
Member

@goderbauer goderbauer commented Jan 15, 2020

Also known as: Don't rebuild OverlayEntries when one changes opaqueness.

Description

Prior to this change we were re-arranging the tree when an OverlayEntry went from onstage to offstage (due to opaqueness changes) and vice versa:

return _Theatre(
onstage: Stack(
fit: StackFit.expand,
children: onstageChildren.reversed.toList(growable: false),
),
offstage: offstageChildren,
);

Since the OverlayEntries have a global key their corresponding element was deactivated at the old location inside the Stack and re-activated at the new location outside the Stack. This caused all StatefulWidgets within that subtree to rebuild because their ancestor had changed.

This change avoids the rebuild by not re-arranging the tree. Instead, both onstage and offstage OverlayEntries are children of the _Theatre, so they don't have to be moved around in the tree. The RenderObject of the _Theatre implements a special version of a Stack that just ignores the offstage children during rendering and layout. (Since the offstage children are always all at the beginning of the child list, this is implemented by providing an integer skipCount indicating the number of children to skip.)

The unnecessary rebuilds were most commonly felt when pushing new Routes because Navigator uses an Overlay internally.

Related Issues

Fixes #45797.

Tests

I added the following tests:

  • OverlayEntries do not rebuild when opaque entry is added
  • OverlayEntries do not rebuild when opaqueness changes
  • entries below opaque entries are ignored for hit testing
  • Semantics of entries below opaque entries are ignored
  • Can used Positioned within OverlayEntry
  • Pushing opaque Route does not rebuild routes below

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read Handling breaking changes.

Sorry, something went wrong.

@fluttergithubbot fluttergithubbot added f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. labels Jan 15, 2020
@goderbauer goderbauer removed f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. labels Jan 15, 2020
@goderbauer
Copy link
Member Author

@Hixie Even though I had to update framework tests for this change I would like to handle it as a non-breaking change. Can you grant an exception?

The changes to the tests were to update error messages and error-message-like output. In one instance I had to change a type due to the changed internals of _Theatre.

Copy link
Contributor

@dnfield dnfield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I agree changing the tests shouldn't be considered breaking here.

I may be missing something, but the skipCount seems sketchy to me. I think we shoudl at least be asserting that the children we think are offstage are in fact offstage, and vice-versa.

Is there ever a case where an opaque route should still layout other routes? Could we have a route that doesn't cover the whole view?

children: onstageChildren.reversed.toList(growable: false),
),
offstage: offstageChildren,
skipCount: children.length - onstageCount,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it would fail if the logic above added children in an order like [offstage, onstage, offstage, offstage] for some reason.

If we think that's invalid, we should probably assert it. If it's actually valid we need different logic here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing something like [offstage, onstage, offstage, offstage] is not supported by the overlay. The top x entries are always onstage, everything else is either completely ignored or - if maintainState is true - rendered offstage.

Not sure what we should assert here. The assert would basically duplicate the algorithm above.

@override
void debugVisitOnstageChildren(ElementVisitor visitor) {
assert(children.length >= widget.skipCount);
children.skip(widget.skipCount).forEach(visitor);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar concern - I think we at least want an assert here that all the widgets we're skipping are in fact offstage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the other way around, though: The fact that we are skipping them makes them offstage. There's no other marker that marks them as offstage that we could assert on.

int get skipCount => _skipCount;
int _skipCount;
set skipCount(int value) {
assert(value != null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert that the children we're skipping are offstage?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same answer as above: The fact that we are skipping them makes them offstage. Not the other way around.

);
if (isHit)
return true;
child = childParentData.previousSibling;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert the previous sibling is onstage?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

@goderbauer
Copy link
Member Author

goderbauer commented Jan 15, 2020

I may be missing something, but the skipCount seems sketchy to me. I think we shoudl at least be asserting that the children we think are offstage are in fact offstage, and vice-versa.

The definition of the (private) _Theatre widget is: "give me a list of children and a number n. I'll not layout/render the first n children (effectively putting them offstage)."

The number n defines what is going to be offstage. There's no other mechanism to tell if a child should be offstage or not...

Copy link
Contributor

@dnfield dnfield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Hixie
Copy link
Contributor

Hixie commented Jan 21, 2020

I haven't reviewed the test of the patch (I'm on my phone) but the test changes call be considered exempt from the breaking change policy as they are only testing internals.

@goderbauer goderbauer merged commit 8eecdbe into flutter:master Jan 22, 2020
@goderbauer goderbauer deleted the theatre2 branch January 22, 2020 00:24
@goderbauer
Copy link
Member Author

This change positively affected some of our Gallery transition benchmarks:

Screen Shot 2020-01-22 at 9 24 29 AM

  • dropped from 63 frames to 50 (minus 21%)

Screen Shot 2020-01-22 at 9 24 23 AM

  • dropped from 50.45ms to 36.64ms (minus 27%)

Screen Shot 2020-01-22 at 9 21 54 AM

  • dropped from 7.38ms to 6.13ms (minus 17%)

@dnfield
Copy link
Contributor

dnfield commented Jan 22, 2020

Wow I'm so used to seeing posts like that linked to a today bug due to a regression. Nice!

bparrishMines added a commit that referenced this pull request Jan 23, 2020
bparrishMines added a commit that referenced this pull request Jan 23, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
#48900)" (#49366)

This reverts commit 8eecdbe.
@dkwingsmt
Copy link
Contributor

If we were to reland this PR, make sure to address the issue mentioned in #49318, where the order between multiple dependencies are not consistent. They might change due to an irrelevant change, or be different between platforms.

@fluttergithubbot fluttergithubbot added f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. labels Jan 23, 2020
goderbauer added a commit that referenced this pull request Jan 23, 2020
goderbauer added a commit that referenced this pull request Jan 24, 2020
@liyuqian liyuqian added perf: speed Performance issues related to (mostly rendering) speed c: performance Relates to speed or footprint issues (see "perf:" labels) labels May 1, 2020
@shamilovtim
Copy link

Hello, is this released?

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 31, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
c: performance Relates to speed or footprint issues (see "perf:" labels) f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. perf: speed Performance issues related to (mostly rendering) speed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optimize unneeded rebuild when pushing overlay/navigator opaque content
8 participants