-
Notifications
You must be signed in to change notification settings - Fork 28.7k
[performance] Process dirty nodes from top to bottom during paint to avoid unnecessary layer tree walks #98219
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
[performance] Process dirty nodes from top to bottom during paint to avoid unnecessary layer tree walks #98219
Conversation
…avoid unnecessary layer tree walks
FYI @chunhtai since you recently also run into trouble with the upside down processing order in paint. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, the only thing that prevented me from doing this was that I am not sure if some mechanism may depend on the paint order. If this change was introduced as a result of performance improvement, I think it is probably obsolete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let the great experiment begin!
…aint to avoid unnecessary layer tree walks (flutter/flutter#98219)
…aint to avoid unnecessary layer tree walks (flutter/flutter#98219)
…aint to avoid unnecessary layer tree walks (flutter/flutter#98219)
Figured out why this was breaking a test in Google3. A widget there is making brittle assumptions about the order in which nodes are processed during paint. The Render tree looks basically like this:
RenderObjectA and RenderObjectB share some data via a shared object. When RenderObjectA paints it sets some data on that sharedObject, that RenderObjectB is supposed to use during its paint. This is assuming that A paints before B (if B paints first it will use outdated data as A didn't have a chance to update the sharedObject yet). Prior to this PR, we would paint the deepest RenderObject first and since A is deeper in the tree it was guaranteed to paint first. This PR is switching this order around and now B is painting first missing the updated data that A is producing during its paint. Seems a little brittle to rely on paint order like that. It only works because A happens to be deeper in the tree. |
That is definitely an invalid assumption. It's perfectly acceptable for siblings to be painted in any arbitrary order. |
Actually, what I describe above is not some custom internal widget. Turns out, it's our Material Slider widget that has this problematic reliance on node processing order during paint. Filed #98770 for that with more details. We should also add a test for the slider to the framework that would have caught the breakage this caused in google3 earlier on this PR. |
…avoid unnecessary layer tree walks (flutter#98219)
Prior to this change, the
flushPaint
method was the odd one out of all the flush methods on PipelineOwner as it would process dirty nodes from bottom to top instead of top to bottom. Some historic digging shows that the flushPaint processing order was changed to that in 654fc73#diff-d06e00032e4d722205a2189ffbab26c1d8f5e13652efebc849583d0a1359fec9R612. Unfortunately, the exact reason is lost to history. Presumably it was for performance, but no scenario where this order would be beneficial has been identified. Instead, this order of processing causes additional walks of the layer tree during paint that would be avoided with top to bottom processing. Therefore, this PR is changing the order back to top to bottom.Regressions? If this change causes any performance regression it should be rolled back immediately and the scenario where bottom to top processing is beneficial should be documented in flushPaint (or even better: encoded in a test).