Skip to content

Maintain dirtiness and use retained engine layers - #23434

Merged
liyuqian merged 9 commits into
flutter:masterfrom
liyuqian:retained_upload_4
Oct 25, 2018
Merged

liyuqian merged 9 commits into
flutter:masterfrom
liyuqian:retained_upload_4

Conversation

@liyuqian

Copy link
Copy Markdown
Contributor

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.

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
@liyuqian
liyuqian requested a review from Hixie October 23, 2018 23:13
/// be always considered as dirty.
/// {@endtemplate}
@override
bool get isDirty => true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

/// This would enable the retained rendering of this layer if it stays clean
/// for future frames.
@protected
void markClean() => _isDirty = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"by calling markDirty" implies that the computation calls markDirty, which seems unlikely to be what you intended

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(maybe "if any subtree has a layer that has been marked dirty using [markDirty]" or some such)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

///
/// (This is valid after calling [updateSubtreeDirtiness].)
@protected
bool get isSubtreeDirty => _isSubtreeDirty;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done with debugIsSubtreeDirty.

/// calling [markDirty]). The [ContainerLayer] will override this to respect
/// its children.
@protected
void updateSubtreeDirtiness() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

since implementing this requires direct access to a private variable, you might as well make this private too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should changing these other attributes also flag the layer as dirty?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

consider using a while loop, it may be easier to read (your call)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

assert(child.attached == attached);
assert(_debugUltimatePreviousSiblingOf(child, equals: firstChild));
assert(_debugUltimateNextSiblingOf(child, equals: lastChild));
markDirty();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this should probably be done in Layer.dropChild, not ContainerLayer._removeChild, in case we ever add another kind of layer that has children.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

assert(node != child); // indicates we are about to create a cycle
return true;
}());
markDirty();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this should probably be done in Layer.adoptChild

/// pipeline.
Offset offset;
Offset get offset => _offset;
set offset(Offset offset) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(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)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

int alpha;

/// Offset from parent in the parent's coordinate system.
Offset offset;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should changing this or the alpha mark the layer as dirty?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

@Hixie

Hixie commented Oct 24, 2018

Copy link
Copy Markdown
Contributor

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 liyuqian left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done with debugMarkClean.

///
/// (This is valid after calling [updateSubtreeDirtiness].)
@protected
bool get isSubtreeDirty => _isSubtreeDirty;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

/// calling [markDirty]). The [ContainerLayer] will override this to respect
/// its children.
@protected
void updateSubtreeDirtiness() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

assert(child.attached == attached);
assert(_debugUltimatePreviousSiblingOf(child, equals: firstChild));
assert(_debugUltimateNextSiblingOf(child, equals: lastChild));
markDirty();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

/// pipeline.
Offset offset;
Offset get offset => _offset;
set offset(Offset offset) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

int alpha;

/// Offset from parent in the parent's coordinate system.
Offset offset;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.


/// Mark that this layer has changed and [addToScene] needs to be called.
@protected
void markNeedsAddToScene() => _needsAddToScene = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

trivial nit: use a block for this, otherwise you're returning bool despite being declared void

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

}());
}

/// Subclass may override this to true to disable retained rendering.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Either "Subclasses", plural, or "A subclass".

markNeedsAddToScene();
}
}
Rect _overlayRect;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: (here and elsewhere) style guide suggests the order getter, field, setter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.


@override
void updateSubtreeNeedsAddToScene() {
_subtreeNeedsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i'd probably put this in Layer, not ContainerLayer, but I guess it doesn't matter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

@Hixie

Hixie commented Oct 25, 2018

Copy link
Copy Markdown
Contributor

LGTM

cc @goderbauer who may want to review this.

@liyuqian
liyuqian merged commit 883e2dc into flutter:master Oct 25, 2018
S find<S>(Offset regionOffset);

/// Override this method to upload this layer to the engine.
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Did we make a a breaking change announcement for this?
This breaks anyone that extends Layer, e.g it broke #23412

@liyuqian

liyuqian commented Oct 31, 2018 via email

Copy link
Copy Markdown
Contributor Author

amirh added a commit that referenced this pull request Oct 31, 2018
Re-landing #23412 with a fix to the PlatformLayer's addToScene signature.

#23412 was broken by the change to Layer done in #23434.

It seemed green as the presubmits were done before #23434 was landed, and when #23412 landed it broke the build.

Reverts #23779
Xavjer pushed a commit to Xavjer/flutter that referenced this pull request Nov 1, 2018
Xavjer pushed a commit to Xavjer/flutter that referenced this pull request Nov 1, 2018
…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
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Aug 9, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants