Skip to content

Shader warm up #27660

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

Merged
merged 20 commits into from
Feb 22, 2019
Merged

Shader warm up #27660

merged 20 commits into from
Feb 22, 2019

Conversation

liyuqian
Copy link
Contributor

@liyuqian liyuqian commented Feb 7, 2019

This patch adds a default shader warm up process which moves shader compilation from the animation time to the startup time. This also provides an extension for runApp so developers can customize the warm up process.

This should reduce our worst_frame_rasterizer_time_millis from ~100ms to ~20-30ms for both flutter_gallery and complex_layout benchmarks. Besides, this should also have a significant improvement on 90th and 99th percentile time (50%-100% speedup in some cases, but I haven't tested them thoroughly; I'll let our device lab collect the data afterwards).

The tradeoff the is the startup time (time to first frame). Our flutter run --profile --trace-startup seems to be a little noisy and I see about 100ms-200ms increase in that measurement for complex_layout and flutter_gallery. Note that this only happens on the first run after install or data wipe. Later the Skia persistent cache will remove the overhead.

This also adds a cubic_bezier benchmark to test the custom shader warm up process.

This should fix #813 (either by defaultShaderWarmUp, or a customShaderWarmUp).

@liyuqian liyuqian changed the title Shader warmup Shader warm up Feb 7, 2019
@chinmaygarde
Copy link
Member

By how much does this regress the time to first frame benchmarks?

@liyuqian
Copy link
Contributor Author

liyuqian commented Feb 8, 2019

@chinmaygarde : time to first frame increases about 100ms-200ms for complex_layout and flutter_gallery. Please see my updated PR description.

Copy link
Member

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

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

Thanks for discussing the tradeoffs and providing data. The approach looks good to me. I am sure we can do a better job of documenting how users can use this feature but that can be tackled in future commits. LGTM but someone more familiar with dart conventions should probably also take a look.

@@ -757,6 +757,10 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
///
/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
///
/// Provide [customShaderWarmUp] if some specific Skia shaders need to be
Copy link
Member

Choose a reason for hiding this comment

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

I think this can be elaborated a bit to make it more useful. Something like, "if the application has scenes that require the compilation of complex shaders, it may cause jank in the middle of an animation or interaction. Painting that scene here tells Flutter to precompute and cache the results of expensive operations so they can be reused later. This should only happen during the very first run of the Flutter application and is typically very small. It is advised that you add tracing here so that such pauses are easier to identify on the timeline. The applications main thread will not be blocked when Flutter is doing this work so you should no expect ANR warnings".

Something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I've combined yours with my original documentation. I omit the tracing part because it's already added to _warmUpSkiaShaderCompilations which calls either [defaultShaderWarmUp] or [customShaderWarmUp].

CC @sfshaza2 for more API doc review.

To avoid dependency loop
@zoechi zoechi added framework flutter/packages/flutter repository. See also f: labels. c: performance Relates to speed or footprint issues (see "perf:" labels) labels Feb 8, 2019
/// to move common shader compilations from animation time to startup time.
/// Alternatively, [customShaderWarmUp] can be provided to [runApp] to replace
/// this default warm up function.
ShaderWarmUp defaultShaderWarmUp = (Canvas canvas) {
Copy link
Contributor

Choose a reason for hiding this comment

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

You can define the function as void defaultShaderWarmUp(Canvas canvas) { and it will still be assignable to ShaderWarmUp

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I intentionally wrote ShaderWarmUp here to clarify that the canvas is expected to have a size of 1000x1000. (Otherwise, I could delete ShaderWarmUp and just use the signature directly everywhere.)

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean in terms of how the function is declared:

Suggested change
ShaderWarmUp defaultShaderWarmUp = (Canvas canvas) {
void defaultShaderWarmUp(Canvas canvas) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I understood that. My intention is that when developers see the signature of defaultShaderWarmUp, they can discover a link to ShaderWarmUp and realize that we're doing work on a 1000x1000 canvas. Does that help?

Copy link
Contributor

Choose a reason for hiding this comment

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

If you leave a comment in the dartdoc [ShaderWarmup] they can follow that link as welll

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. Thanks!

];

// Warm up path stroke and fill shaders.
for (Path path in paths) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we're trying to make this as fast as possible, index iteration is slightly faster than an iterator protocol

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

might want to benchmark this. I'm not sure how true that really is in practice.

@liyuqian liyuqian requested a review from sfshaza2 February 8, 2019 21:43
@goderbauer
Copy link
Member

@liyuqian Is this ready to land?

@liyuqian
Copy link
Contributor Author

@goderbauer I guess so if the review from @jonahwilliams is a green light on the Dart side.

BTW, as I'm digging emails, I see that we're in a tree freeze due to P0 Google3/Fuchsia roll failure. Should I wait the tree to be reopened before merging this?

@jonahwilliams
Copy link
Contributor

per the addition to runApp, I do not have a strong opinion on the API but others might

Copy link
Contributor

@jonahwilliams jonahwilliams left a comment

Choose a reason for hiding this comment

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

LGTM with nit

@liyuqian
Copy link
Contributor Author

CC @Hixie for the review of API change to runApp.

@@ -0,0 +1,84 @@
import 'dart:ui';
Copy link
Contributor

Choose a reason for hiding this comment

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

please see the README in this directory.

This probably belongs in the painting library, not foundation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to painting. Painting was my first choice but analyze won't allow me to do that since I used it in scheduler, which creates a dependency loop. Now I've removed all changes to scheduler and ShaderWarmUp is in painting again.

@@ -5,10 +5,11 @@
import 'dart:async';
import 'dart:collection';
import 'dart:developer';
import 'dart:ui' show AppLifecycleState;
import 'dart:ui' show AppLifecycleState, Canvas, Image, Picture, PictureRecorder;
Copy link
Contributor

Choose a reason for hiding this comment

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

the scheduler shouldn't know about these things. They have nothing to do with scheduling.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed changes to scheduler.

@@ -1 +1,2 @@
const String kCullOpacityRouteName = '/cull_opacity';
Copy link
Contributor

Choose a reason for hiding this comment

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

missing license

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.

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
Copy link
Contributor

Choose a reason for hiding this comment

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

missing license

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.

@@ -0,0 +1,40 @@
import 'dart:async';
Copy link
Contributor

Choose a reason for hiding this comment

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

missing license

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.

/// order of magnitude of most devices.
///
/// A custom shader warm up can override this based on targeted devices.
ui.Size get size => const ui.Size(1024, 1024);
Copy link
Contributor

Choose a reason for hiding this comment

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

please use .0 on integral double literals

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.

@Hixie
Copy link
Contributor

Hixie commented Feb 22, 2019

LGTM

cc @goderbauer

@liyuqian
Copy link
Contributor Author

This patch adds a default shader warm up process which moves shader compilation from the animation time to the startup time. This also provides an extension for runApp so developers can customize the warm up process.

This should reduce our worst_frame_rasterizer_time_millis from ~100ms to ~20-30ms for both flutter_gallery and complex_layout benchmarks. Besides, this should also have a significant improvement on 90th and 99th percentile time (50%-100% speedup in some cases, but I haven't tested them thoroughly; I'll let our device lab collect the data afterwards).

The tradeoff the is the startup time (time to first frame). Our flutter run --profile --trace-startup seems to be a little noisy and I see about 100ms-200ms increase in that measurement for complex_layout and flutter_gallery. Note that this only happens on the first run after install or data wipe. Later the Skia persistent cache will remove the overhead.

This also adds a cubic_bezier benchmark to test the custom shader warm up process.

This should fix #813 (either by defaultShaderWarmUp, or a customShaderWarmUp).

@liyuqian liyuqian merged commit a44f174 into flutter:master Feb 22, 2019
liyuqian added a commit to liyuqian/flutter that referenced this pull request Feb 23, 2019
This reverts commit a44f174.

Reason: start_up tests become flaky.

See flutter#28374

TBR: xster
liyuqian added a commit that referenced this pull request Feb 23, 2019
This reverts commit a44f174.

Reason: start_up tests become flaky.

See #28374

TBR: xster

Merge on red to fix the tree
liyuqian added a commit to liyuqian/flutter that referenced this pull request Feb 26, 2019
liyuqian added a commit that referenced this pull request Feb 27, 2019
This reverts commit adc8e15.

This should be safe to land once #28530 gets merged

Merge on yellow doc test because the doc test is actually green.
@ivnsch
Copy link
Contributor

ivnsch commented Mar 18, 2019

I saw some back and forth with this PR in master, is now the final version merged?
Asking because I'm running the gallery demo from master on my iPhone 6 and it's still janky the first time. Up to date, with flutter run --release.

@liyuqian
Copy link
Contributor Author

I assume that it's still janky when you reopen the app and run the animation for the first time? If so, I guess that it's caused by something else. Can you please run the gallery demo with flutter run --profile --trace-skia, save the trace from observatory timeline, and upload it here so we can take a deeper look?

@ivnsch
Copy link
Contributor

ivnsch commented Mar 19, 2019

The same happens after closing the app (where closing means really closing, not only sending to the background).

Attached the trace. This is of a new installation, where I opened "Studies". Then for each of the 4 items inside, opened it and pressed back. There was jank in all of them (the push animation almost jumps to its finished state, in some more than the others).

dart-timeline-2019-2-19.json.zip

@msw333
Copy link

msw333 commented Mar 19, 2019

I can confirm that jittering on first startup still exists on iOS (just create any type of app with a list with 30 items with listview.builder and scroll it when you launch the app).

@Hixie
Copy link
Contributor

Hixie commented Mar 19, 2019

I recommend that anyone running into slowness on startup file a bug with a small repro test case. There are probably many unrelated causes. This PR was to solve #813 which was specifically about the slowness in the Gallery.

@ivnsch
Copy link
Contributor

ivnsch commented Mar 19, 2019

@Hixie My comment was about the gallery (not sure if you're addressing me as well).

@liyuqian
Copy link
Contributor Author

I think Hixie's comment is only for @msw333 . For that list with 30 items issue, it would helpful if @msw333 could create a new issue, attach the trace and a single-file Flutter app that can allow us to reproduce the issue.

For @i-schuetz 's issue, yes it's currently still expected to have a little jank right after the new installation. Your trace is consistent with my local experiment with a new installation. But once I close the app and reopen it on my iPhone6S, the jank would disappear if I open the same item. Can you please confirm that it's also the case in your iPhone6? If not, can you please attach the trace of the second first-run after the installation?

@Hixie
Copy link
Contributor

Hixie commented Mar 23, 2019

We care about first-run performance too, very much in fact. :-)

@msw333
Copy link

msw333 commented Mar 23, 2019

Ok ;-). Hope that helps. Issue is observable in every flutter app I was using. iPhone X, 12.1.4 (iOS).
Issue is more observable when using a list which is a little more complex.

How to reproduce:

  • Open a flutter app with a list on iOS, e.g. my code sample below.
  • Switch to a different app and scroll a list.
  • Switch back to the flutter app via iOS swipe gesture and directly scroll the list.
  • Same behavior is observable on first startup of the flutter app
    (within profile mode you see how fps drop into the red)

Videos don't give it justice, but show the issue:
https://www.youtube.com/watch?v=oHKNKi08qw8
https://www.youtube.com/watch?v=1mdM_Uv574w

Code to reproduce (just use as main.dart). Again, I can reproduce it with any flutter app on iOS. Jittering issue increases within apps with higher complexity:
https://gist.github.com/msw333/dd0a06fb1fe8b4eb4ae627542202d43a

@liyuqian
Copy link
Contributor Author

Thanks @msw333 ! That's very helpful. Let's further discuss this issue on #28113

@ivnsch
Copy link
Contributor

ivnsch commented Mar 26, 2019

Moved to #28113 (comment)

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
c: performance Relates to speed or footprint issues (see "perf:" labels) framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Jittery performance on first run of animation/transitions due to shader compilations
9 participants