Skip to content

Conversation

@dnfield
Copy link
Contributor

@dnfield dnfield commented Jan 26, 2019

Fixes #25673

Updates the paint method on _RenderPhysicalModelBase to ask the pipeline owner to check the elevation in the area of the screen it wants to paint in. If there is such a problem, a FlutterError is thrown and the screen is painted with a path outlining the two problematic elevations. This is only done in debug mode (or at least only when asserts are enabled).

The algorithm used has a worst-case performance of 1 + 2 + 3 + 4 + .. + n, where n is the number of _RenderPhysicalModelBase objects in the render tree (which should be a small subset of total render objects in the tree for a given frame). I've added a parameter to control how many iterations we acutally check for (so at runtime it can be set lower if needed). The default is completely arbitrary right now, I'll try to test it on an older phone soon. The default for tests is arbitrarily high to be more aggressive about catching them in testing scenarios - but even for a specific test they can be turned off.

@dnfield dnfield requested review from Hixie and goderbauer January 26, 2019 23:34
@dnfield dnfield added a: tests "flutter test", flutter_test, or one of our tests framework flutter/packages/flutter repository. See also f: labels. a: fidelity Matching the OEM platforms better customer: fuchsia customer: dream (g3) labels Jan 26, 2019
@dnfield
Copy link
Contributor Author

dnfield commented Jan 26, 2019

A couple screen shot of this in action. In both, the top shapes are a stack with two overlapping materials with elevations out of order; the bottom has two materials also out of order elevations; in the first screen shot they're overlapping, in the second they're not.

screen shot 2019-01-26 at 3 49 52 pm

screen shot 2019-01-26 at 3 51 18 pm

@dnfield
Copy link
Contributor Author

dnfield commented Jan 27, 2019

I have to fix the tests. I tried this at one point and it was passing, but probably because the implementation was off.

@dnfield dnfield changed the title Test for invalid elevations [WIP] Test for invalid elevations Jan 28, 2019
@dnfield
Copy link
Contributor Author

dnfield commented Jan 28, 2019

Marking this as WIP until I can figure out a way to determine whether a widget should be excluded from this test or not. There seems to be a whole class of objects that ask to paint but don't actually affect the rendering.

@dnfield dnfield changed the title [WIP] Test for invalid elevations Test for invalid elevations Jan 28, 2019
@dnfield
Copy link
Contributor Author

dnfield commented Jan 28, 2019

There's still a potentially open question about this around whether we really should scope to overlays (or, RenderOffstages here).

I'd like to leave it as is for now, as it at least gives a check for a lot of common cases. We can figure out how to handle this for transitions once we get some more input from the folks who want this.

final Matrix4 matrix = Matrix4.identity()..translate(offset.dx, offset.dy);
return p.transform(matrix.storage);
}
bool _printedExceededmaxElevationObjectsToCheckWarning = false;
Copy link
Member

Choose a reason for hiding this comment

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

Should Max be capitalized in here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, done

/// [RenderPhysicalModel] or [RenderPhysicalShape]. The [area] parameter
/// specifies the [Rect] on the screen in which the physical model will be
/// drawn, and the [path] parameter specifies the actual [Path] to be drawn.
/// The path is expected to be smaller than the area.
Copy link
Member

Choose a reason for hiding this comment

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

Can both be the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes - clarifying this...

/// drawn, and the [path] parameter specifies the actual [Path] to be drawn.
/// The path is expected to be smaller than the area.
///
/// If the durrent paint cycle results in drawing a shape at an elevation
Copy link
Member

Choose a reason for hiding this comment

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

durrent > current

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

double elevation,
Rect area,
Path path,
RenderObject object,
Copy link
Member

Choose a reason for hiding this comment

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

Can you document these two parameters as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


Map<RenderObject, List<_ElevationData>> _elevations;
Path _translatePath(Path p, Offset offset) {
final Matrix4 matrix = Matrix4.identity()..translate(offset.dx, offset.dy);
Copy link
Member

Choose a reason for hiding this comment

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

his probably also need to take other paint transformations into consideration? (applyPaintTransform maybe)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

I could use some feedback on whether I'm right to ignore RenderView. That one ends up applying th edevicepixelratio to the transform, and it messes things up. But I'm not sure how else it might get used.

);
// Check if the union created a path with a single segment. If so, the
// paths overlap.
// This will not work if we're dealing with input paths that are already
Copy link
Member

Choose a reason for hiding this comment

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

I wonder how common this is going to be - and how expensive it actually would be to account for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've updated the logic to support this - if it's looking good I'll do some perf tests and we can figure out if it needs to go or maybe get guarded somehow.

@dnfield
Copy link
Contributor Author

dnfield commented Feb 2, 2019

@goderbauer This is ready for another review pass. I've added some tests to acocunt for a Transform and added a bunch of comments.

@dnfield
Copy link
Contributor Author

dnfield commented Feb 5, 2019

@amirh I'm hoping you have some context on the failing test here (notched app bar with no margin).

The algorithm I'm using marks it as a problem, because we're trying to draw a FAB with elevation 6 after an app bar with elevation 8, and the edges are touching. I'm not entirely sure what that would do in Fuchsia, but it seems off (in fact, the whole shadow rendering on iOS and Android for this seems pretty wrong to me).

///
/// See also: [debugCheckElevationData].
int get maxElevationObjectsToCheck => _maxElevationObjectsToCheck;
int _maxElevationObjectsToCheck = 10;
Copy link
Member

Choose a reason for hiding this comment

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

I wonder what the default should be here. Setting this to 10 may break some existing apps? Should we have a way to opt-in to this?

/// an `assert` from within the framework. All of the parameters must not be
/// null.
///
/// The [elevation] refers to the elevation specified on the
Copy link
Member

Choose a reason for hiding this comment

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

Document if this value is relative to the parent or in terms of absolute elevation?

/// [RenderPhysicalModel] or [RenderPhysicalShape]. The [area] parameter
/// specifies the [Rect] on the screen in which the physical model will be
/// drawn, and the [path] parameter specifies the actual [Path] to be drawn,
/// with the corret paint translations already applied. The path may be
Copy link
Member

Choose a reason for hiding this comment

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

What coordinate system are these in?

final Matrix4 transform = Matrix4.identity()..translate(offset.dx, offset.dy);
while (ancestor != null) {
if (ancestor is _RenderPhysicalModelBase) {
// We're the direct child of another physical model. We need to add
Copy link
Member

Choose a reason for hiding this comment

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

Given the while loop, it may not be a direct child, but a grandchild or grand-grand-...-child etc?

}
if (ancestor is RenderObject) {
// We don't want the devicePixelRatio included in this transform.
if (ancestor is! RenderView) {
Copy link
Member

Choose a reason for hiding this comment

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

This seems odd. Is the problem maybe that it introduces another transform and the algo can't handle two transformation?

It seems odd to me that the path drawn on the canvas below is not transformed back into the coordinate system of that canvas...

// doing the more expensive path comparison
area.overlaps(elevationData.area) &&
// ignore PhysicalModels that are direct descendants of this
// one, in case a parent is getting checked after its children
Copy link
Member

Choose a reason for hiding this comment

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

This one is odd. Why would that happen?

@goderbauer
Copy link
Member

I wonder if the problem with that failing test is that we mark shapes that are just touching as overlapping.

@Hixie
Copy link
Contributor

Hixie commented Feb 5, 2019

Rather than breaking out of the transforms, I would recommend moving the logic for reporting this to somewhere higher-level, e.g. a widget child of WidgetsApp, the same way we do the DEBUG banner.

@goderbauer
Copy link
Member

I am closing this one for now since @dnfield is working on a new approach, which is going to be different from what's proposed here.

@goderbauer goderbauer closed this Feb 13, 2019
@dnfield dnfield mentioned this pull request Mar 29, 2019
9 tasks
@dnfield dnfield deleted the elevations branch January 7, 2020 03:57
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

a: fidelity Matching the OEM platforms better a: tests "flutter test", flutter_test, or one of our tests customer: dream (g3) customer: fuchsia framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A Stack widget with children at different elevations renders in painting order for 2D but will not do so for "3D"

4 participants