Repository navigation
Maintain dirtiness and use retained engine layers - #23434
Conversation
test Keep LeaderLayer and FollowerLayer dirty Update subtree dirtiness for widget inspector and screenshot Remove test code Update packages/flutter/lib/src/material/page_transitions_theme.dart Update packages/flutter/lib/src/rendering/layer.dart Update packages/flutter/lib/src/rendering/layer.dart Update packages/flutter/lib/src/rendering/object.dart Fixes Make addToScene & updateSubtreeDirtiness protected We make a new API buildScene to encapsulate those calls. Capture offset in OpacityLayer Add unit tests Remove test code Suppress invalid_use_of_protected_member in tests Remove warning Remove obsolete code Make markDirty protected
| /// be always considered as dirty. | ||
| /// {@endtemplate} | ||
| @override | ||
| bool get isDirty => true; |
There was a problem hiding this comment.
The danger of overriding isDirty is that it will likely lead to bugs in the superclass code when that code is written assuming that _isDirty and isDirty are equivalent. I think it would be safer if we had an explicit getter that expressed whether or not a layer needs to always be walked each scene, and then have the logic that tests for dirtiness also check for that flag.
This would have the other advantage that it would mean isDirty wasn't exposed at all, which makes it less likely that people will write code that depends on it (always a bit sketchy).
| /// This would enable the retained rendering of this layer if it stays clean | ||
| /// for future frames. | ||
| @protected | ||
| void markClean() => _isDirty = false; |
There was a problem hiding this comment.
looks like this is only used in tests, so it can probably be safely removed (the one local call can be replaced by just setting _isDirty directly).
There was a problem hiding this comment.
Done with debugMarkClean.
| ui.EngineLayer _engineLayer; | ||
|
|
||
| /// Traverse the layer tree and compute if any subtree has a dirty layer (by | ||
| /// calling [markDirty]). The [ContainerLayer] will override this to respect |
There was a problem hiding this comment.
"will" implies it doesn't yet.
Also, as with other places, please make sure all dartdocs have a single explanatory sentence in a standalone paragraph first, then additional material in subsequent paragraphs. The first paragraph is used for summaries in tables of contents, autocomplete, etc.
| ui.EngineLayer _engineLayer; | ||
|
|
||
| /// Traverse the layer tree and compute if any subtree has a dirty layer (by | ||
| /// calling [markDirty]). The [ContainerLayer] will override this to respect |
There was a problem hiding this comment.
"by calling markDirty" implies that the computation calls markDirty, which seems unlikely to be what you intended
There was a problem hiding this comment.
(maybe "if any subtree has a layer that has been marked dirty using [markDirty]" or some such)
| /// | ||
| /// (This is valid after calling [updateSubtreeDirtiness].) | ||
| @protected | ||
| bool get isSubtreeDirty => _isSubtreeDirty; |
There was a problem hiding this comment.
As far as I can tell, you don't need to expose this. You only use it where you can get to the private field directly.
There was a problem hiding this comment.
Done with debugIsSubtreeDirty.
| /// calling [markDirty]). The [ContainerLayer] will override this to respect | ||
| /// its children. | ||
| @protected | ||
| void updateSubtreeDirtiness() { |
There was a problem hiding this comment.
since implementing this requires direct access to a private variable, you might as well make this private too
There was a problem hiding this comment.
I need to override it in ContainerLayer and private method can't be overridden...
| @@ -168,8 +229,9 @@ class PictureLayer extends Layer { | |||
| bool willChangeHint = false; | |||
There was a problem hiding this comment.
should changing these other attributes also flag the layer as dirty?
There was a problem hiding this comment.
Since this only affects cache behavior, not the rendering result, it's Ok to not dirty the layer here. But there are probably many other mutable fields that I should mark dirty once they get changed.
There was a problem hiding this comment.
After a second thought, I added getters and setters to them just for consistency. (I've added so many getters and setters, it doesn't matter to add two more...)
| @override | ||
| void updateSubtreeDirtiness() { | ||
| _isSubtreeDirty = isDirty; | ||
| for (Layer child = firstChild; child != null; child = child.nextSibling) { |
There was a problem hiding this comment.
consider using a while loop, it may be easier to read (your call)
| assert(child.attached == attached); | ||
| assert(_debugUltimatePreviousSiblingOf(child, equals: firstChild)); | ||
| assert(_debugUltimateNextSiblingOf(child, equals: lastChild)); | ||
| markDirty(); |
There was a problem hiding this comment.
this should probably be done in Layer.dropChild, not ContainerLayer._removeChild, in case we ever add another kind of layer that has children.
| assert(node != child); // indicates we are about to create a cycle | ||
| return true; | ||
| }()); | ||
| markDirty(); |
There was a problem hiding this comment.
this should probably be done in Layer.adoptChild
| /// pipeline. | ||
| Offset offset; | ||
| Offset get offset => _offset; | ||
| set offset(Offset offset) { |
There was a problem hiding this comment.
(here and elsewhere) we generally prefer the name value for the argument to the setter, it makes for easier copy/paste later. (see the style guide)
| int alpha; | ||
|
|
||
| /// Offset from parent in the parent's coordinate system. | ||
| Offset offset; |
There was a problem hiding this comment.
should changing this or the alpha mark the layer as dirty?
There was a problem hiding this comment.
Yes, adding similar getters and setters to this Layer and other layers with mutable fields. Meanwhile, I'll also make as many Layer fields final as possible to minimize the change in this PR. If a Layer's fields become mutable in the future, we can add the related logic to just that Layer in that future PR. That should be much cleaner than a big change to every Layer in this PR.
|
This is pretty close. The one main thing I think we should consider before landing is whether all the mutable fields on all the layer objects that exist today should be adjusted to mark the layer as dirty when mutated. Before they didn't because we assumed that we always walked the entire tree. |
liyuqian
left a comment
There was a problem hiding this comment.
I've now either marked the layer fields as final, or handled their needsAddToScene (the old isDirty) accordingly in the setter.
| /// This would enable the retained rendering of this layer if it stays clean | ||
| /// for future frames. | ||
| @protected | ||
| void markClean() => _isDirty = false; |
There was a problem hiding this comment.
Done with debugMarkClean.
| /// | ||
| /// (This is valid after calling [updateSubtreeDirtiness].) | ||
| @protected | ||
| bool get isSubtreeDirty => _isSubtreeDirty; |
There was a problem hiding this comment.
Done with debugIsSubtreeDirty.
| ui.EngineLayer _engineLayer; | ||
|
|
||
| /// Traverse the layer tree and compute if any subtree has a dirty layer (by | ||
| /// calling [markDirty]). The [ContainerLayer] will override this to respect |
| /// calling [markDirty]). The [ContainerLayer] will override this to respect | ||
| /// its children. | ||
| @protected | ||
| void updateSubtreeDirtiness() { |
There was a problem hiding this comment.
I need to override it in ContainerLayer and private method can't be overridden...
| @@ -168,8 +229,9 @@ class PictureLayer extends Layer { | |||
| bool willChangeHint = false; | |||
There was a problem hiding this comment.
Since this only affects cache behavior, not the rendering result, it's Ok to not dirty the layer here. But there are probably many other mutable fields that I should mark dirty once they get changed.
| @override | ||
| void updateSubtreeDirtiness() { | ||
| _isSubtreeDirty = isDirty; | ||
| for (Layer child = firstChild; child != null; child = child.nextSibling) { |
| assert(child.attached == attached); | ||
| assert(_debugUltimatePreviousSiblingOf(child, equals: firstChild)); | ||
| assert(_debugUltimateNextSiblingOf(child, equals: lastChild)); | ||
| markDirty(); |
| /// pipeline. | ||
| Offset offset; | ||
| Offset get offset => _offset; | ||
| set offset(Offset offset) { |
| int alpha; | ||
|
|
||
| /// Offset from parent in the parent's coordinate system. | ||
| Offset offset; |
There was a problem hiding this comment.
Yes, adding similar getters and setters to this Layer and other layers with mutable fields. Meanwhile, I'll also make as many Layer fields final as possible to minimize the change in this PR. If a Layer's fields become mutable in the future, we can add the related logic to just that Layer in that future PR. That should be much cleaner than a big change to every Layer in this PR.
| /// be always considered as dirty. | ||
| /// {@endtemplate} | ||
| @override | ||
| bool get isDirty => true; |
|
|
||
| /// Mark that this layer has changed and [addToScene] needs to be called. | ||
| @protected | ||
| void markNeedsAddToScene() => _needsAddToScene = true; |
There was a problem hiding this comment.
trivial nit: use a block for this, otherwise you're returning bool despite being declared void
| }()); | ||
| } | ||
|
|
||
| /// Subclass may override this to true to disable retained rendering. |
There was a problem hiding this comment.
Either "Subclasses", plural, or "A subclass".
| markNeedsAddToScene(); | ||
| } | ||
| } | ||
| Rect _overlayRect; |
There was a problem hiding this comment.
nit: (here and elsewhere) style guide suggests the order getter, field, setter.
|
|
||
| @override | ||
| void updateSubtreeNeedsAddToScene() { | ||
| _subtreeNeedsAddToScene = _needsAddToScene || alwaysNeedsAddToScene; |
There was a problem hiding this comment.
it's a pity ContainerLayer directly accesses private fields of its superclass (it means e.g. that you can't create a different implementation of ContainerLayer), but I don't immediately see a clean way to fix this, so, oh well.
There was a problem hiding this comment.
Oh, I expected Dart Analysis to give me errors if a subclass is accessing its parent class' private fields! It seems to be not the case and I have to be careful by myself. I think super.updateSubtreeNeedsAddToScene() would work well in this case.
| markNeedsAddToScene(); | ||
| super.adoptChild(child); | ||
| } | ||
|
|
There was a problem hiding this comment.
i'd probably put this in Layer, not ContainerLayer, but I guess it doesn't matter.
|
cc @goderbauer who may want to review this. |
| S find<S>(Offset regionOffset); | ||
|
|
||
| /// Override this method to upload this layer to the engine. | ||
| void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]); |
There was a problem hiding this comment.
DBC
This is a breaking change, and will require a manual roll into Fuchsia. See: https://logs.chromium.org/logs/fuchsia/buildbucket/cr-buildbucket.appspot.com/8931658542863723648/+/steps/build/0/steps/build_fuchsia/0/steps/ninja/0/stdout
There was a problem hiding this comment.
A patch was actually ready to fix it since 10/11: https://fuchsia-review.googlesource.com/c/topaz/+/211549
However, I forgot to add tag back then and now the presubmit somehow always gets red. Do you or @rmacnak-google know what's wrong with topaz-x64-perf-dawson_canyon there?
There was a problem hiding this comment.
Update: https://fuchsia-review.googlesource.com/c/topaz/+/211549 has landed so it must be a flaky test. Can we kick an auto roll into Fuchsia to see if succeeds this time?
There was a problem hiding this comment.
Did we make a a breaking change announcement for this?
This breaks anyone that extends Layer, e.g it broke #23412
|
No, we missed it. Will post an announcement today. Thanks for the remind!
…On Wed, Oct 31, 2018, 7:55 AM Amir Hardon ***@***.*** wrote:
***@***.**** commented on this pull request.
------------------------------
In packages/flutter/lib/src/rendering/layer.dart
<#23434 (comment)>:
> @@ -104,7 +169,29 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
S find<S>(Offset regionOffset);
/// Override this method to upload this layer to the engine.
- void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]);
Did we make a a breaking change announcement for this?
This breaks anyone that extends Layer, e.g it broke #23412
<#23412>
—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
<#23434 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AV7DMHLqF7T2mhEFJ1P2_rzeuiM4RFeQks5uqbnIgaJpZM4X21U6>
.
|
…23781) Re-landing flutter#23412 with a fix to the PlatformLayer's addToScene signature. flutter#23412 was broken by the change to Layer done in flutter#23434. It seemed green as the presubmits were done before flutter#23434 was landed, and when flutter#23412 landed it broke the build. Reverts flutter#23779
For #21756
The old PR is broken by Github again... I'll try to not reference that PR here to see if this PR will stay unbroken.