-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Check for invalid elevations #30215
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
Check for invalid elevations #30215
Conversation
| } | ||
|
|
||
| /// Returns the descendants of this layer in depth first order. | ||
| Iterable<Layer> depthFirstIterateChildren() sync* { |
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.
In general sync* has very bad performance, it is better to avoid it.
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.
Is there some way to inspect the generated code here? I strongly suspect it will be at least as efficient as writing our own iterable implementation for this in the current usage...
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.
Although this is making me realize that I'm modifying the tree while iterating it which is a bad idea. I can fix that though.
| List<PictureLayer> _debugCheckElevations() { | ||
| final List<PhysicalModelLayer> physicalModelLayers = depthFirstIterateChildren().whereType<PhysicalModelLayer>().toList(); | ||
| final List<PictureLayer> addedLayers = <PictureLayer>[]; | ||
| bool _predecessorIsNotDirectAncestor(PhysicalModelLayer predecessor, Layer child) { |
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.
This should be _predecessorIsAncestor
goderbauer
left a comment
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.
There's also the one elevation case I just showed you that didn't seem to work. Probably needs elevation addition?
| void applyTransform(Layer child, Matrix4 transform) { | ||
| assert(child != null); | ||
| assert(transform != null); | ||
| if (_lastEffectiveTransform == null && this.transform != null) { |
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.
What if transform is also null? Wouldn't line 1210 throw then? If it is guaranteed to not be null, maybe add an assert instead?
| ]; | ||
| } | ||
|
|
||
| /// Checks that no [PhysicalModelLayer] would paint after another |
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.
Should this maybe mention that this condition is only problematic if they are overlapping?
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.
Done
|
|
||
| List<PictureLayer> _processConflictingPhysicalLayers(PhysicalModelLayer predecessor, PhysicalModelLayer child) { | ||
| FlutterError.reportError(FlutterErrorDetails( | ||
| exception: FlutterError('Painting order is out of order with respect to elevation.'), |
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.
This could be more informative. Can we give any call to action to resolve the issue? Where can a developer learn more?
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.
The full error ends up looking like this:
I/flutter (15065): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (15065): The following assertion was thrown during compositing:
I/flutter (15065): Painting order is out of order with respect to elevation.
I/flutter (15065):
I/flutter (15065): Attempted to composite layer:
I/flutter (15065): PhysicalModelLayer#c1c68(creator: PhysicalModel ← AnimatedPhysicalModel ← Material ← ConstrainedBox
I/flutter (15065): ← Container ← Stack ← Column ← Directionality ← [root], elevation: 1.0, color: MaterialColor(primary
I/flutter (15065): value: Color(0xff2196f3)))
I/flutter (15065): after layer:
I/flutter (15065): PhysicalModelLayer#d7ff6(creator: PhysicalModel ← AnimatedPhysicalModel ← Material ← ConstrainedBox
I/flutter (15065): ← Container ← Stack ← Column ← Directionality ← [root], elevation: 1.2, color: MaterialColor(primary
I/flutter (15065): value: Color(0xff4caf50)))
I/flutter (15065): which occupies the same area at a higher elevation.
I/flutter (15065): ════════════════════════════════════════════════════════════════════════════════════════════════════
Although unfortunately only for the first one. Is there some way you think I could highlight that this will be added with more information elsewhere?
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.
Maybe we need a doc page on flutter.dev going over this concept?
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.
Linking to somewhere with more information would probable be good. For now maybe just link back to the docs of debugCheckElevationsEnabled which have a nice example?
| bool debugRepaintTextRainbowEnabled = false; | ||
|
|
||
| /// Causes [PhysicalModelLayer]s to paint a red rectangle around themselves if | ||
| /// they are painted out of order with regard to their elevation. |
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.
Should this summary maybe already include the overlapping bit?
|
|
||
| List<PictureLayer> _processConflictingPhysicalLayers(PhysicalModelLayer predecessor, PhysicalModelLayer child) { | ||
| FlutterError.reportError(FlutterErrorDetails( | ||
| exception: FlutterError('Painting order is out of order with respect to elevation.'), |
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.
Linking to somewhere with more information would probable be good. For now maybe just link back to the docs of debugCheckElevationsEnabled which have a nice example?
| debugDisableShadows = true; | ||
| } | ||
|
|
||
| testWidgets('entirely overlapping, direct child', (WidgetTester tester) async { |
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.
Future me who may have to debug these in the future wishes there were diagrams on these too
goderbauer
left a comment
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
| /// For example, a rectangular elevation at 3.0 that is painted before an | ||
| /// overlapping rectangular elevation at 2.0 would render this way on Android | ||
| /// and iOS (with fake shadows): | ||
| /// ┌───────────────────┐ |
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.
nit: should these be enclosed in ``` to ensure they render with a fixed-width font in dartdocs?
jacob314
left a comment
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.
Thanks for the heads up! I've filed flutter/devtools#508
to track adding this service extension to devtools.
* master: (209 commits) Allow mouse hover to only respond to some mouse events but not all. (flutter#30886) Fix issue 23527: Exception: RenderViewport exceeded its maximum number of layout cycles (flutter#30809) Keep hover annotation layers in sync with the mouse detector. (flutter#30829) Use identical instead of '==' in a constant expression. (flutter#30921) Add toggle for debugProfileWidgetBuilds (flutter#30867) Revert "Manual engine roll with disabled service authentication codes (flutter#30919)" (flutter#30930) Manual engine roll with disabled service authentication codes (flutter#30919) Baseline Aligned Row (flutter#30746) [Material] Fix showDialog crasher caused by old contexts (flutter#30754) Let `sliver.dart` `_createErrorWidget` work with other Widgets (flutter#30880) Add more dialog doc cross-reference (flutter#30887) Allow downloading of desktop embedding artifacts (flutter#30648) CupertinoDatePicker initialDateTime accounts for minuteInterval (flutter#30862) add golden tests for CupertinoDatePicker (flutter#30828) Simplify toImage future handling (flutter#30876) Fixed Table flex column layout error flutter#30437 (flutter#30470) Fix iTunes Transporter quirk (flutter#30883) Bump Android build tools to 28.0.3 in Dockerfile (flutter#30832) Update the upload key which seems to have trouble for some reason (flutter#30871) Check for invalid elevations (flutter#30215) ...
|
Apologies if mentioned above - are there plans to allow iOS/Android to render in elevation order? If not, is there a best practice for bringing a widget out of a multi child? I've tried a few things, but the only way I've found to avoid typing the widget code out in 2 places is GlobalKey.currentWidget |
Description
Today, Android and iOS render in painting order regaredless of elevation. Fuchsia renders in elevation order regardless of painting order. This can lead to vastly different rendering output between the two platforms, particularly when stacks are involved.
This test expects that overlapping layers at the same z-index will be handled in some other way, and are valid.
This patch creates a check at the layer tree level for invalid elevation with regard to painting order in
PhysicalModelLayers. This is a recording with the check turned on in some parts of the Flutter Gallery that currently violate this.The key to turn this on from the console is
z. Alternatively, it can be programatically enabled via thedebugCheckElevationsEnabledservice extension, or by settingdebugCheckElevationsEnabledto true fromrendering/debug.dart./cc @mklim who is working on related efforts for z-fighting.
/cc @jacob314 and @DanTup - at some point there may be a desire to enable toggling this from the IDE plugins.
Related Issues
Fixes #25673
#27891 - which enabled physical layer compositing on all platforms, making this check possible.
#27137 - which was an initial attempt at this that fails we can't trust a call to
painton the widgets being tested.Tests
I added the following tests:
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.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?