Skip to content

Commit

Permalink
Perform no shader warm-up by default (#87126)
Browse files Browse the repository at this point in the history
  • Loading branch information
dnfield committed Jul 27, 2021
1 parent 05e8425 commit 32c2e2b
Show file tree
Hide file tree
Showing 7 changed files with 20 additions and 265 deletions.
2 changes: 1 addition & 1 deletion dev/tracing_tests/test/image_cache_tracing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ void main() {
},
<String, dynamic>{
'name': 'listener',
'args': <String, dynamic>{'parentId': '1', 'isolateId': isolateId}
'args': <String, dynamic>{'parentId': '0', 'isolateId': isolateId}
},
<String, dynamic>{
'name': 'ImageCache.clear',
Expand Down
35 changes: 0 additions & 35 deletions examples/layers/raw/shader_warm_up.dart

This file was deleted.

14 changes: 0 additions & 14 deletions examples/layers/test/smoketests/raw/shader_warm_up_test.dart

This file was deleted.

20 changes: 10 additions & 10 deletions packages/flutter/lib/src/painting/binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ mixin PaintingBinding on BindingBase, ServicesBinding {

/// [ShaderWarmUp] instance to be executed during [initInstances].
///
/// Defaults to an instance of [DefaultShaderWarmUp].
/// Defaults to `null`, meaning no shader warm-up is done. Some platforms may
/// not support shader warm-up before at least one frame has been displayed.
///
/// If the application has scenes that require the compilation of complex
/// shaders that are not covered by [DefaultShaderWarmUp], it may cause jank
/// in the middle of an animation or interaction. In that case, setting
/// [shaderWarmUp] to a custom [ShaderWarmUp] before creating the binding
/// (usually before [runApp] for normal Flutter apps, and before
/// [enableFlutterDriverExtension] for Flutter driver tests) may help if that
/// object paints the difficult scene in its [ShaderWarmUp.warmUpOnCanvas]
/// method, as this allows Flutter to pre-compile and cache the required
/// shaders during startup.
/// shaders, it may cause jank in the middle of an animation or interaction.
/// In that case, setting [shaderWarmUp] to a custom [ShaderWarmUp] before
/// creating the binding (usually before [runApp] for normal Flutter apps, and
/// before [enableFlutterDriverExtension] for Flutter driver tests) may help
/// if that object paints the difficult scene in its
/// [ShaderWarmUp.warmUpOnCanvas] method, as this allows Flutter to
/// pre-compile and cache the required shaders during startup.
///
/// Currently the warm-up happens synchronously on the raster thread which
/// means the rendering of the first frame on the raster thread will be
Expand All @@ -58,7 +58,7 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
///
/// * [ShaderWarmUp], the interface for implementing custom warm-up scenes.
/// * <https://flutter.dev/docs/perf/rendering/shader>
static ShaderWarmUp? shaderWarmUp = const DefaultShaderWarmUp();
static ShaderWarmUp? shaderWarmUp;

/// The singleton that implements the Flutter framework's image cache.
///
Expand Down
149 changes: 5 additions & 144 deletions packages/flutter/lib/src/painting/shader_warm_up.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ import 'debug.dart';
/// 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. 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].
/// move common shader compilations from animation time to startup time. If
/// needed, app developers can create a custom [ShaderWarmUp] subclass and
/// hand it to [PaintingBinding.shaderWarmUp] before
/// [PaintingBinding.initInstances] is called. Usually, that can be done before
/// calling [runApp].
///
/// To determine whether a draw operation is useful for warming up shaders,
/// check whether it improves the slowest frame rasterization time. Also,
Expand Down Expand Up @@ -102,141 +101,3 @@ abstract class ShaderWarmUp {
picture.dispose();
}
}

/// Default way of warming up Skia shader compilations.
///
/// The draw operations being warmed up here are decided according to Flutter
/// engineers' observation and experience based on the apps and the performance
/// issues seen so far.
///
/// This is used for the default value of [PaintingBinding.shaderWarmUp].
/// Consider setting that static property to a different value before the
/// binding is initialized to change the warm-up sequence.
///
/// See also:
///
/// * [ShaderWarmUp], the base class for shader warm-up objects.
/// * <https://flutter.dev/docs/perf/rendering/shader>
class DefaultShaderWarmUp extends ShaderWarmUp {
/// Create an instance of the default shader warm-up logic.
///
/// Since this constructor is `const`, [DefaultShaderWarmUp] can be used as
/// the default value of parameters.
const DefaultShaderWarmUp({
this.drawCallSpacing = 0.0,
this.canvasSize = const ui.Size(100.0, 100.0),
});

/// Distance to place between draw calls for visualizing the draws for
/// debugging purposes (e.g. 80.0).
///
/// Defaults to 0.0.
///
/// When changing this value, the [canvasSize] must also be changed to
/// accommodate the bigger canvas.
final double drawCallSpacing;

/// The [size] of the canvas required to paint the shapes in [warmUpOnCanvas].
///
/// When [drawCallSpacing] is 0.0, this should be at least 100.0 by 100.0.
final ui.Size canvasSize;

@override
ui.Size get size => canvasSize;

/// Trigger common draw operations on a canvas to warm up GPU shader
/// compilation cache.
@override
Future<void> warmUpOnCanvas(ui.Canvas canvas) async {
const ui.RRect rrect = ui.RRect.fromLTRBXY(20.0, 20.0, 60.0, 60.0, 10.0, 10.0);
final ui.Path rrectPath = ui.Path()..addRRect(rrect);
final ui.Path circlePath = ui.Path()..addOval(
ui.Rect.fromCircle(center: const ui.Offset(40.0, 40.0), radius: 20.0),
);

// The following path is based on
// https://skia.org/user/api/SkCanvas_Reference#SkCanvas_drawPath
final ui.Path path = ui.Path();
path.moveTo(20.0, 60.0);
path.quadraticBezierTo(60.0, 20.0, 60.0, 60.0);
path.close();
path.moveTo(60.0, 20.0);
path.quadraticBezierTo(60.0, 60.0, 20.0, 60.0);

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
..strokeWidth = 10,
ui.Paint()
..isAntiAlias = true
..style = ui.PaintingStyle.stroke
..strokeWidth = 0.1, // hairline
];

// Warm up path stroke and fill shaders.
for (int i = 0; i < paths.length; i += 1) {
canvas.save();
for (final ui.Paint paint in paints) {
canvas.drawPath(paths[i], paint);
canvas.translate(drawCallSpacing, 0.0);
}
canvas.restore();
canvas.translate(0.0, drawCallSpacing);
}

// Warm up shadow shaders.
const ui.Color black = ui.Color(0xFF000000);
canvas.save();
canvas.drawShadow(rrectPath, black, 10.0, true);
canvas.translate(drawCallSpacing, 0.0);
canvas.drawShadow(rrectPath, black, 10.0, false);
canvas.restore();

// Warm up text shaders.
canvas.translate(0.0, drawCallSpacing);
final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(
ui.ParagraphStyle(textDirection: ui.TextDirection.ltr),
)..pushStyle(ui.TextStyle(color: black))..addText('_');
final ui.Paragraph paragraph = paragraphBuilder.build()
..layout(const ui.ParagraphConstraints(width: 60.0));
canvas.drawParagraph(paragraph, const ui.Offset(20.0, 20.0));

// Draw a rect inside a rrect with a non-trivial intersection. If the
// intersection is trivial (e.g., equals the rrect clip), Skia will optimize
// the clip out.
//
// Add an integral or fractional translation to trigger Skia's non-AA or AA
// optimizations (as did before in normal FillRectOp in rrect clip cases).
for (final double fraction in <double>[0.0, 0.5]) {
canvas
..save()
..translate(fraction, fraction)
..clipRRect(ui.RRect.fromLTRBR(8, 8, 328, 248, const ui.Radius.circular(16)))
..drawRect(const ui.Rect.fromLTRB(10, 10, 320, 240), ui.Paint())
..restore();
canvas.translate(drawCallSpacing, 0.0);
}
canvas.translate(0.0, drawCallSpacing);
}
}
4 changes: 4 additions & 0 deletions packages/flutter/test/painting/binding_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ Future<void> main() async {
expect(binding.imageCache.clearCount, 1);
expect(binding.imageCache.liveClearCount, 1);
});

test('ShaderWarmUp is null by default', () {
expect(PaintingBinding.shaderWarmUp, null);
});
}

class TestBindingBase implements BindingBase {
Expand Down
61 changes: 0 additions & 61 deletions packages/flutter/test/painting/shader_warm_up_test.dart

This file was deleted.

0 comments on commit 32c2e2b

Please sign in to comment.