Skip to content

Commit

Permalink
Add convex path and non-AA paint to shader warm-up (flutter#28614)
Browse files Browse the repository at this point in the history
One of our important client's SKP shows that this could improve one of
their janky frame by 20ms. This also improves our
flutter_gallery__transition_perf's worst frame time by ~20ms.

On the other hand, 15ms has been added to the start-up latency. I guess
it's a little faster to compile the shader on the start-up because we're
compiling a lot of shaders there and the CPU cache must be hot.

## Related Issues

flutter#813
  • Loading branch information
liyuqian committed Feb 28, 2019
1 parent 5aedf97 commit f776cc1
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 14 deletions.
8 changes: 6 additions & 2 deletions packages/flutter/lib/src/painting/binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// shaders that are not covered by [DefaultShaderWarmUp], it may cause jank
/// in the middle of an animation or interaction. In that case, set
/// [shaderWarmUp] to a custom [ShaderWarmUp] before calling [initInstances]
/// (usually before [runApp] for normal flutter apps, and before
/// [enableFlutterDriverExtension] for flutter drive tests). Paint the scene
/// (usually before [runApp] for normal Flutter apps, and before
/// [enableFlutterDriverExtension] for Flutter drive tests). Paint the scene
/// in the custom [ShaderWarmUp] so Flutter can pre-compile and cache the
/// shaders during startup. The warm up is only costly (100ms-200ms,
/// depending on the shaders to compile) during the first run after the
Expand All @@ -49,6 +49,10 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// Currently the warm-up happens synchronously on the GPU thread which means
/// the rendering of the first frame on the GPU thread will be postponed until
/// the warm-up is finished.
///
/// See also:
///
/// * [ShaderWarmUp], the interface of how this warm up works.
static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp();

/// The singleton that implements the Flutter framework's image cache.
Expand Down
58 changes: 46 additions & 12 deletions packages/flutter/lib/src/painting/shader_warm_up.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,45 @@ import 'package:flutter/foundation.dart';

/// Interface for drawing an image to warm up Skia shader compilations.
///
/// When Skia first sees a certain type of draw operations on GPU, it needs to
/// compile the corresponding shader. The compilation can be slow (20ms-200ms).
/// Having that time as a startup latency is often better than having a jank in
/// the middle of an animation.
/// When Skia first sees a certain type of draw operation on the GPU, it needs
/// to compile the corresponding shader. The compilation can be slow (20ms-
/// 200ms). Having that time as startup latency is often better than having
/// jank in the middle of an animation.
///
/// Therefore, we use this during the [PaintingBinding.initInstances] call to
/// move common shader compilations from animation time to startup time. By
/// default, a [DefaultShaderWarmUp] is used. Create a custom [ShaderWarmUp]
/// subclass to replace [PaintingBinding.shaderWarmUp] before
/// [PaintingBinding.initInstances] is called. Usually, that can be done before
/// calling [runApp].
/// default, a [DefaultShaderWarmUp] is used. If needed, app developers can
/// create a custom [ShaderWarmUp] subclass and hand it to
/// [PaintingBinding.shaderWarmUp] (so it replaces [DefaultShaderWarmUp])
/// before [PaintingBinding.initInstances] is called. Usually, that can be
/// done before calling [runApp].
///
/// This warm up needs to be run on each individual device because the shader
/// To determine whether a draw operation is useful for warming up shaders,
/// check the difference in the `worst_frame_rasterizer_time_millis` benchmarks.
/// Also, tracing with `flutter run --profile --trace-skia` may reveal whether
/// there is shader-compilation-related jank. If there is such jank, some long
/// `GrGLProgramBuilder::finalize` calls would appear in the middle of an
/// animation. Their parent calls, which look like `XyzOp` (e.g., `FillRecOp`,
/// `CircularRRectOp`) would suggest Xyz draw operations are causing the shaders
/// to be compiled. A useful shader warm-up draw operation would eliminate such
/// long compilation calls in the animation. To double-check the warm-up, trace
/// with `flutter run --profile --trace-skia --start-paused`. The
/// `GrGLProgramBuilder` with the associated `XyzOp` should appear during
/// startup rather than in the middle of a later animation.
///
/// This warm-up needs to be run on each individual device because the shader
/// compilation depends on the specific GPU hardware and driver a device has. It
/// can't be pre-computed during the Flutter engine compilation as the engine is
/// device agnostic.
/// device-agnostic.
///
/// If no warm up is desired (e.g., when the startup latency is crucial), set
/// If no warm-up is desired (e.g., when the startup latency is crucial), set
/// [PaintingBinding.shaderWarmUp] either to a custom ShaderWarmUp with an empty
/// [warmUpOnCanvas] or null.
///
/// See also:
///
/// * [PaintingBinding.shaderWarmUp], the actual instance of [ShaderWarmUp]
/// that's used to warm up the shaders.
abstract class ShaderWarmUp {
/// Allow const constructors for subclasses.
const ShaderWarmUp();
Expand Down Expand Up @@ -101,12 +120,27 @@ class DefaultShaderWarmUp extends ShaderWarmUp {
path.moveTo(60.0, 20.0);
path.quadraticBezierTo(60.0, 60.0, 20.0, 60.0);

final List<ui.Path> paths = <ui.Path>[rrectPath, circlePath, path];
final ui.Path convexPath = ui.Path();
convexPath.moveTo(20.0, 30.0);
convexPath.lineTo(40.0, 20.0);
convexPath.lineTo(60.0, 30.0);
convexPath.lineTo(60.0, 60.0);
convexPath.lineTo(20.0, 60.0);
convexPath.close();

// Skia uses different shaders based on the kinds of paths being drawn and
// the associated paint configurations. According to our experience and
// tracing, drawing the following paths/paints generates various of
// shaders that are commonly used.
final List<ui.Path> paths = <ui.Path>[rrectPath, circlePath, path, convexPath];

final List<ui.Paint> paints = <ui.Paint>[
ui.Paint()
..isAntiAlias = true
..style = ui.PaintingStyle.fill,
ui.Paint()
..isAntiAlias = false
..style = ui.PaintingStyle.fill,
ui.Paint()
..isAntiAlias = true
..style = ui.PaintingStyle.stroke
Expand Down

0 comments on commit f776cc1

Please sign in to comment.