Unverified Commit f776cc19 authored by liyuqian's avatar liyuqian Committed by GitHub

Add convex path and non-AA paint to shader warm-up (#28614)

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

https://github.com/flutter/flutter/issues/813
parent 5aedf97e
...@@ -38,8 +38,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -38,8 +38,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// shaders that are not covered by [DefaultShaderWarmUp], it may cause jank /// shaders that are not covered by [DefaultShaderWarmUp], it may cause jank
/// in the middle of an animation or interaction. In that case, set /// in the middle of an animation or interaction. In that case, set
/// [shaderWarmUp] to a custom [ShaderWarmUp] before calling [initInstances] /// [shaderWarmUp] to a custom [ShaderWarmUp] before calling [initInstances]
/// (usually before [runApp] for normal flutter apps, and before /// (usually before [runApp] for normal Flutter apps, and before
/// [enableFlutterDriverExtension] for flutter drive tests). Paint the scene /// [enableFlutterDriverExtension] for Flutter drive tests). Paint the scene
/// in the custom [ShaderWarmUp] so Flutter can pre-compile and cache the /// in the custom [ShaderWarmUp] so Flutter can pre-compile and cache the
/// shaders during startup. The warm up is only costly (100ms-200ms, /// shaders during startup. The warm up is only costly (100ms-200ms,
/// depending on the shaders to compile) during the first run after the /// depending on the shaders to compile) during the first run after the
...@@ -49,6 +49,10 @@ mixin PaintingBinding on BindingBase, ServicesBinding { ...@@ -49,6 +49,10 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
/// Currently the warm-up happens synchronously on the GPU thread which means /// 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 rendering of the first frame on the GPU thread will be postponed until
/// the warm-up is finished. /// the warm-up is finished.
///
/// See also:
///
/// * [ShaderWarmUp], the interface of how this warm up works.
static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp(); static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp();
/// The singleton that implements the Flutter framework's image cache. /// The singleton that implements the Flutter framework's image cache.
......
...@@ -9,26 +9,45 @@ import 'package:flutter/foundation.dart'; ...@@ -9,26 +9,45 @@ import 'package:flutter/foundation.dart';
/// Interface for drawing an image to warm up Skia shader compilations. /// 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 /// When Skia first sees a certain type of draw operation on the GPU, it needs
/// compile the corresponding shader. The compilation can be slow (20ms-200ms). /// to compile the corresponding shader. The compilation can be slow (20ms-
/// Having that time as a startup latency is often better than having a jank in /// 200ms). Having that time as startup latency is often better than having
/// the middle of an animation. /// jank in the middle of an animation.
/// ///
/// Therefore, we use this during the [PaintingBinding.initInstances] call to /// Therefore, we use this during the [PaintingBinding.initInstances] call to
/// move common shader compilations from animation time to startup time. By /// move common shader compilations from animation time to startup time. By
/// default, a [DefaultShaderWarmUp] is used. Create a custom [ShaderWarmUp] /// default, a [DefaultShaderWarmUp] is used. If needed, app developers can
/// subclass to replace [PaintingBinding.shaderWarmUp] before /// create a custom [ShaderWarmUp] subclass and hand it to
/// [PaintingBinding.initInstances] is called. Usually, that can be done before /// [PaintingBinding.shaderWarmUp] (so it replaces [DefaultShaderWarmUp])
/// calling [runApp]. /// 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 /// 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 /// 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 /// [PaintingBinding.shaderWarmUp] either to a custom ShaderWarmUp with an empty
/// [warmUpOnCanvas] or null. /// [warmUpOnCanvas] or null.
///
/// See also:
///
/// * [PaintingBinding.shaderWarmUp], the actual instance of [ShaderWarmUp]
/// that's used to warm up the shaders.
abstract class ShaderWarmUp { abstract class ShaderWarmUp {
/// Allow const constructors for subclasses. /// Allow const constructors for subclasses.
const ShaderWarmUp(); const ShaderWarmUp();
...@@ -101,12 +120,27 @@ class DefaultShaderWarmUp extends ShaderWarmUp { ...@@ -101,12 +120,27 @@ class DefaultShaderWarmUp extends ShaderWarmUp {
path.moveTo(60.0, 20.0); path.moveTo(60.0, 20.0);
path.quadraticBezierTo(60.0, 60.0, 20.0, 60.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>[ final List<ui.Paint> paints = <ui.Paint>[
ui.Paint() ui.Paint()
..isAntiAlias = true ..isAntiAlias = true
..style = ui.PaintingStyle.fill, ..style = ui.PaintingStyle.fill,
ui.Paint()
..isAntiAlias = false
..style = ui.PaintingStyle.fill,
ui.Paint() ui.Paint()
..isAntiAlias = true ..isAntiAlias = true
..style = ui.PaintingStyle.stroke ..style = ui.PaintingStyle.stroke
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment