Skip to content

[framework] elide ImageFilter layers when animation is stopped#101731

Merged
fluttergithubbot merged 5 commits intoflutter:masterfrom
jonahwilliams:elide_image_filter
Apr 12, 2022
Merged

[framework] elide ImageFilter layers when animation is stopped#101731
fluttergithubbot merged 5 commits intoflutter:masterfrom
jonahwilliams:elide_image_filter

Conversation

@jonahwilliams
Copy link
Contributor

@jonahwilliams jonahwilliams commented Apr 12, 2022

See also #100972 (comment) for more information.

Using a ScaleTransition or a RotationTransition with a FilterQuality today incurs an extra cost, even when these widgets are not currently being animated. For the reasons outlined in the discussion above, the engine does not skip any work even if the ImageFilter used would be a no-op (for instance, the identity matrix). This results in extra save layers / render target switches.

Since the framework has more knowledge about when it is appropriate to add/remove these layers, the animated widgets should drop Filter Quality if the animation isn't running.

Note that this is not the cause of the regression in the linked issue. Instead, this performance issue is preventing us from switching to using the ImageFilter even though in theory it should perform better compared to the Transform layer.

#100972

@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label Apr 12, 2022
Copy link
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

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

Very succinct way to solve this. Do you have any data on whether this helps the original issue yet?

@jonahwilliams
Copy link
Contributor Author

I ran the new gallery transition perf benchmark on a Pixel 4. I compared the base zoom transition with the zoom transition using FilterQuality.none and this patch.

current:

    "average_frame_rasterizer_time_millis": 10.492328800988888,
    "90th_percentile_frame_rasterizer_time_millis": 16.014,
    "99th_percentile_frame_rasterizer_time_millis": 31.898,

patched:

    "average_frame_rasterizer_time_millis": 9.762356884057967,
    "90th_percentile_frame_rasterizer_time_millis": 13.904,
    "99th_percentile_frame_rasterizer_time_millis": 28.336,

Not super scientific, but we could always switch it over in a separate PR and then use skia perf to track regressions/improvements

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

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

LGTM


@override
Widget build(BuildContext context) {
/// The ImageFilter layer created by setting filterQuality will introduce
Copy link
Member

Choose a reason for hiding this comment

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

nit: // over /// 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.

Done

/// The ImageFilter layer created by setting filterQuality will introduce
/// a saveLayer call. This is usually worthwhile when animating the layer,
/// but leaving it in the layer tree before the animation has started or after
/// it has finished significantly hurts performance.
Copy link
Member

Choose a reason for hiding this comment

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

Should we leave a note about this behavior in the doc comment on filterQuality?

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

/// a saveLayer call. This is usually worthwhile when animating the layer,
/// but leaving it in the layer tree before the animation has started or after
/// it has finished significantly hurts performance.
final bool skipfilterQuality = scale.isCompleted || scale.isDismissed;
Copy link
Member

Choose a reason for hiding this comment

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

we prefer a switch/case on Animation.status over this on the off-chance that we ever need to add another status value to get the analyzer's help for all usages that need to be considered.

https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#avoid-using-if-chains-or--or--with-enum-values

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


@override
Widget build(BuildContext context) {
/// The ImageFilter layer created by setting filterQuality will introduce
Copy link
Member

Choose a reason for hiding this comment

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

All three comments from above also apply 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.

Done

// a saveLayer call. This is usually worthwhile when animating the layer,
// but leaving it in the layer tree before the animation has started or after
// it has finished significantly hurts performance.
final bool skipfilterQuality;
Copy link
Contributor

Choose a reason for hiding this comment

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

A nit. This is a negative name so that we have to do something if it is not set. I tend to prefer positive names that indicate something needs to be done if the flag is set. We're focused on "skipping" the filter quality because that is the improvement we want to see, but this flag is really indicating whether to use the filter quality. Does that make any semantic sense?

So, would "useFilterQuality" and swap all of the true/false/?: logic read better in this code?

Copy link
Contributor

Choose a reason for hiding this comment

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

The exceptional and uncommon condition here is that we are in the process of animating, so the default is to not forward the filterQuality and the condition to call out are the cases when we do want to use 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.

Yeah that reads better to me. Updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants