-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Started sharing SemanticsProperties between the Widget and the RenderObject #104281
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
Started sharing SemanticsProperties between the Widget and the RenderObject #104281
Conversation
b4a59d1 to
e873d86
Compare
e873d86 to
f55eb80
Compare
|
test-exempt: code refactor with no semantic change however if this breaks anything in flutter/tests or google, we will need a migration guide |
Hixie
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.
cc @goderbauer for visibility
Co-authored-by: Ian Hickson <[email protected]>
I saw no references of subclassing RenderSemanticsAnnotations or allocating it. I think those are things that would potentially break. |
|
|
||
| SemanticsProperties _properties; | ||
| /// Sets the [SemanticsProperties] en masse. | ||
| void updateProperties(SemanticsProperties value) { |
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.
Why is this not just a regular setter for properties?
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.
Because it's a linter error to have a setter without a getter. I guess I could add a getter safely since it's effectively immutable. I tried to give out the least amount of access necessary.
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.
Yeah, having a getter for this is normal style for RenderObjects. Adding one should be fine.
| bool? liveRegion, | ||
| int? maxValueLength, | ||
| int? currentValueLength, | ||
| AttributedString? attributedLabel, |
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.
These also exist on SmeanticsProperties, no? Why are those allowed to be passed in separately?
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.
They had slightly different logic where they get wrapped in AttributeString. I was afraid that if I moved it, I might accidentally start allocating more than one AttributeString. It was hard for me to prove that I wouldn't without storing that value somewhere.
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.
It would be fine to store that value on the RenderObject. It's just confusing that the API of the RO allowed this value to be passed in in two different ways and one just silently gets ignored.
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, moved all that logic from the widget into the renderobject.
| _checked = value; | ||
| markNeedsSemanticsUpdate(); | ||
| } | ||
| bool? get checked => _properties.checked; |
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.
I would be in favor of removing these getters as well, if that's not breaking anything. Are these actually used anywhere?
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.
They are only used locally in the framework afaik. They are probably used in tests. There is tons of documentation written for them. I was worried that some of that information might only live there. It seems like SemanticsProperties is the proper place for that documentation though.
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.
+1. If this information is not also available on SemanticsProperties let's move it there and get rid of these here.
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.
Okay, I skimmed through the docstrings and they seem like copy and paste. I don't think there is anything lost ripping them out and all the tests passed locally so I think we are good. Done.
| hidden: properties.hidden, | ||
| image: properties.image, | ||
| properties: properties, | ||
| attributedLabel: _effectiveAttributedLabel, |
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.
Ah! Maybe we should move the logic for figuring the effectiveAttributed* also down one level to avoid the confusion that attributedLabel is passed in directly, but also as part of the properties.
| ..onDidGainAccessibilityFocus = properties.onDidGainAccessibilityFocus | ||
| ..onDidLoseAccessibilityFocus = properties.onDidLoseAccessibilityFocus | ||
| ..customSemanticsActions = properties.customSemanticsActions; | ||
| ..textDirection = _getTextDirection(context); |
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.
(unrelated) I wonder if we have a "bug" here: properties also sets a textdirection, but we seem to just ignore that one...
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.
Yea, it seemed weird, but I just tried not to touch it. _getTextDirection does use the value of properties if it is available fwiw.
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.
Ah, cool. That makes sense then.
|
Nice clean-up overall! |
Uhhh isn't this entirely a semantic change? I'll leave now... |
…to share-sematics-properties
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
|
|
||
| SemanticsProperties _properties; | ||
| /// Sets the [SemanticsProperties] en masse. | ||
| void updateProperties(SemanticsProperties value) { |
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.
Make this a regular getter/setter pair to follow RenderObject convention? Since SemanticsProperties is immutable anyways, this shouldn't cause any trouble.
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
| if (_properties == value) | ||
| return; | ||
| _properties = value; | ||
| _updateAttributedFields(_properties); |
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 theory, if we are worried about unnecessary object instantiations, we could just reset the attributed fields to null here and do this pre-computation work the first time describeSemanticsConfiguration is called. That way, the AttributedStrings would actually only get instantiated when they are needed (i.e. when Semantics are enabled). Not sure how much this matters. Leaving this as-is is also fine.
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.
I did play around with making it late but I was worried about getting stale data on accident. I just kept it the same.
This starts sharing memory between
SemanticsandRenderSemanticsAnnotations. I noticed a significant decrease in build times incustomer: moneywhen I ripped out all theSemanticswhile testing something else. I theorized that the performance increase was related to the large memory cost ofSemanticswhere almost all of its 62 instance variables were getting copied to the RenderObject. In production apps Semantics widgets become ubiquitous and account for a large percentage of the allocations.I did a bit of testing to see what effect his might have. I ran a before and after with
customer: moneywhere I just clicked on the first button to load the main screen and saw 108MB of allocations go to 85MB, where 210RenderSemanticsAnnotationswhere allocated. That would mean a savings of ~112 KB perRenderSemanticsAnnotations. That doesn't sound plausible, I'm not sure what Dart is doing under the covers though. I would have expected something more like (48 removed ivars * ~16 bytes * 210 instances) = 161 KB total.I did run CPU benchmark tests with
build_bench.dartand didn't notice a difference, but it also isn't that representative. I did try mycustomer: moneybenchmark and didn't notice a difference either but that benchmark isn't quite good either.I don't think it's worth spending more time trying to track down the exact extent of the changes. The cleanup of this optimization is value enough. Writing a microbenchmark just for this may not be worth it. Hopefully I can get the
customer:moneyone showing what we want eventually.before:

after:

Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.