-
Notifications
You must be signed in to change notification settings - Fork 28.7k
Improve ShapeDecoration performance. #108648
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
Improve ShapeDecoration performance. #108648
Conversation
8142b79
to
4edb88f
Compare
Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change). If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. Changes reported for pull request #108648 at sha 4edb88ff4f45d7e38b5128f3f928c39cdec64f8e |
I think we should ignore that. It is probably a path vs circle rounding error thing. |
b228ee4
to
4edb88f
Compare
Yeah it is indeed a change in antialiasing. Maybe drawCircle and drawPath differ slightly? I've marked it as OK. |
Yeah I would not make that change either. |
@@ -180,6 +180,17 @@ abstract class BoxBorder extends ShapeBorder { | |||
..addRect(rect); | |||
} | |||
|
|||
@override | |||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) { | |||
// If `ShapeDecoration(shape: Border.all())`, ShapeDecoration doesn't |
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.
If -> For?
Maybe clarify this further, explaining that this class only ever paints a rounded border if paint
is given one and that this is an extension to the superclass' API.
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! I re-wrote it:
// For `ShapeDecoration(shape: Border.all())`, a rectangle with sharp edges
// is always painted. There is no BorderRadius parameter for
// ShapeDecoration or Border, only for BoxDecoration, which doesn't call
// this method.
_decoration.shape.getOuterPath(rect.shift(shadow.offset).inflate(shadow.spreadRadius), textDirection: textDirection), | ||
shadow.toPaint(), | ||
); | ||
}), |
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 guess technically we're computing the paints more than we need to (we only need to compute them the first time, not when rect changes). Maybe add a TODO? Or not, your call. It's not a big deal.
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 need to test, but what if rotation, Transform, resize happens? Is it the same shadow?
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'll try later (possibly tomorrow) but if we can get this working, optimization can be really big.
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.
well the paint definitely isn't affected by the rect (it's computed by shadow.toPaint()
, which doesn't get the rect). But the question is whether it's more expensive to compute the paint, or more expensive to handle the overhead of storing the paint in some other data structure in parallel with the rect bounds / paths. IIRC you had it the other way before and it was slower?
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 believe it was slower because of the additional loop, maybe the map, not exactly the additional structure. In this case, everything is going to be mostly similar. But I'll test, let's see.
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 have a proposal.
What if we had a mixin PrimitiveShape { paintInterior(...) }
that is applied on RoundedRectangleBorder / CircleBorder / Stadium Border, then we do if (shape is PrimitiveShape) { (shape as PrimitiveShape).paintInterior(...) }
?
We wouldn't need preferPaintInterior
. And... maybe, one day, we could delimit the BorderSide.only
/MultipleBorderSide
(I still need to convince you) to only work on shapes that have that mixin. Is this too crazy?
We would solve a variable issue and a future documentation issue.
--
Alternate proposal: rename preferPaintInterior
to isPrimitiveShape
and explain the concept I just invented. But mixin sounds more fun.
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.
Found what you said. Now I understand the issue. Testing a few things.
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.
After debugging today: negligible difference, I'm reverting for the old way. Not changing what already works fine. My sample only uses a single color, if paint is complex enough, there might be differences.
Again, you are correct. Thanks!
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 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
@bernaferrari What is the status of this PR? It looks like the three last comments above may still be unresolved? |
I traveled for the past few days, so couldn't find time to run some additional benchmarks to see if a variable was better or worse than a loop. I'll probably take a look tomorrow. I also made a proposal to use a mixin instead of the hasPaintInside getter, but didn't get an answer. Regardless, everything should be resolved on the next few days. |
9f8713e
to
2eeddc2
Compare
Sorry for the delay, here we go!! Fixed what needed to be fixed. I rewrote this, if it is ok, feel free to add auto-submit. If it is not, let me know how I can improve. That's the last thing. flutter/packages/flutter/lib/src/painting/box_border.dart Lines 185 to 188 in d742c5a
|
Golden file changes are available for triage from new commit, Click here to view. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
You don't need the |
Thanks, I didn't know that. |
Golden file changes are available for triage from new commit, Click here to view. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
Don't worry, you have no real way to have found out. It's documented in the bottom of a locked filing cabinet stuck in a disused lavatory with a sign on the door saying "Beware of the Leopard": https://github.com/flutter/flutter/blob/master/dev/bots/analyze_snippet_code.dart#L51 |
Golden file changes are available for triage from new commit, Click here to view. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
Discussion: #108052
I believe that PR became too big and it is doing too many things at once. This PR is not breaking but might require a g3fix. So I'm splitting it. Easier to fix and easier to track the performance gains.
cc @Hixie. Same code you already reviewed (with an additional comment in
box_border.dart
).Edit: I could make this change (really small optimization, only render border if width != 0) yet the code looks horrible. So I'm not making it, yet.
