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

Make shader warm-up async so it can handle image (#28687)

## Description

This moves another 15-20ms from the animation jank of one of our important client to the startup latency. Unfortunately, this is probably not captured in our current benchmarks (presumably some other bottlenecks overshadow this shader compilation in the worst_frame benchmark). Considering that drawing images is such a common operation, maybe we should add one in the future to benchmark this.

We need this PR to land soon for our client because this changes the API to return Future.

## Related Issues

https://github.com/flutter/flutter/issues/813
parent 98739667
...@@ -10,8 +10,8 @@ import 'package:macrobenchmarks/main.dart' as app; ...@@ -10,8 +10,8 @@ import 'package:macrobenchmarks/main.dart' as app;
class CubicBezierShaderWarmUp extends DefaultShaderWarmUp { class CubicBezierShaderWarmUp extends DefaultShaderWarmUp {
@override @override
void warmUpOnCanvas(Canvas canvas) { Future<void> warmUpOnCanvas(Canvas canvas) async {
super.warmUpOnCanvas(canvas); await super.warmUpOnCanvas(canvas);
// Warm up the cubic shaders used by CubicBezierPage. // Warm up the cubic shaders used by CubicBezierPage.
// //
......
...@@ -9,14 +9,14 @@ import 'dart:ui' as ui; ...@@ -9,14 +9,14 @@ import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/painting.dart' show DefaultShaderWarmUp; import 'package:flutter/painting.dart' show DefaultShaderWarmUp;
void beginFrame(Duration timeStamp) { Future<void> beginFrame(Duration timeStamp) async {
// PAINT // PAINT
final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Rect paintBounds = ui.Rect.fromLTRB(0, 0, 1000, 1000); final ui.Rect paintBounds = ui.Rect.fromLTRB(0, 0, 1000, 1000);
final ui.Canvas canvas = ui.Canvas(recorder, paintBounds); final ui.Canvas canvas = ui.Canvas(recorder, paintBounds);
final ui.Paint backgroundPaint = ui.Paint()..color = Colors.white; final ui.Paint backgroundPaint = ui.Paint()..color = Colors.white;
canvas.drawRect(paintBounds, backgroundPaint); canvas.drawRect(paintBounds, backgroundPaint);
const DefaultShaderWarmUp().warmUpOnCanvas(canvas); await const DefaultShaderWarmUp().warmUpOnCanvas(canvas);
final ui.Picture picture = recorder.endRecording(); final ui.Picture picture = recorder.endRecording();
// COMPOSITE // COMPOSITE
...@@ -27,7 +27,7 @@ void beginFrame(Duration timeStamp) { ...@@ -27,7 +27,7 @@ void beginFrame(Duration timeStamp) {
ui.window.render(sceneBuilder.build()); ui.window.render(sceneBuilder.build());
} }
void main() { Future<void> main() async {
ui.window.onBeginFrame = beginFrame; ui.window.onBeginFrame = beginFrame;
ui.window.scheduleFrame(); ui.window.scheduleFrame();
} }
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -65,6 +67,8 @@ abstract class ShaderWarmUp { ...@@ -65,6 +67,8 @@ abstract class ShaderWarmUp {
/// Trigger draw operations on a given canvas to warm up GPU shader /// Trigger draw operations on a given canvas to warm up GPU shader
/// compilation cache. /// compilation cache.
/// ///
/// Parameter [image] is to be used for drawImage related operations.
///
/// To decide which draw operations to be added to your custom warm up /// To decide which draw operations to be added to your custom warm up
/// process, try capture an skp using `flutter screenshot --observatory- /// process, try capture an skp using `flutter screenshot --observatory-
/// port=<port> --type=skia` and analyze it with https://debugger.skia.org. /// port=<port> --type=skia` and analyze it with https://debugger.skia.org.
...@@ -73,20 +77,20 @@ abstract class ShaderWarmUp { ...@@ -73,20 +77,20 @@ abstract class ShaderWarmUp {
/// Skia draw operations are commonly used, and which shader compilations /// Skia draw operations are commonly used, and which shader compilations
/// are causing janks. /// are causing janks.
@protected @protected
void warmUpOnCanvas(ui.Canvas canvas); Future<void> warmUpOnCanvas(ui.Canvas canvas);
/// Construct an offscreen image of [size], and execute [warmUpOnCanvas] on a /// Construct an offscreen image of [size], and execute [warmUpOnCanvas] on a
/// canvas associated with that image. /// canvas associated with that image.
void execute() { Future<void> execute() async {
final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder); final ui.Canvas canvas = ui.Canvas(recorder);
warmUpOnCanvas(canvas); await warmUpOnCanvas(canvas);
final ui.Picture picture = recorder.endRecording(); final ui.Picture picture = recorder.endRecording();
final TimelineTask shaderWarmUpTask = TimelineTask(); final TimelineTask shaderWarmUpTask = TimelineTask();
shaderWarmUpTask.start('Warm-up shader'); shaderWarmUpTask.start('Warm-up shader');
picture.toImage(size.width.ceil(), size.height.ceil()).then((ui.Image image) { picture.toImage(size.width.ceil(), size.height.ceil()).then((ui.Image _) {
shaderWarmUpTask.finish(); shaderWarmUpTask.finish();
}); });
} }
...@@ -104,7 +108,7 @@ class DefaultShaderWarmUp extends ShaderWarmUp { ...@@ -104,7 +108,7 @@ class DefaultShaderWarmUp extends ShaderWarmUp {
/// Trigger common draw operations on a canvas to warm up GPU shader /// Trigger common draw operations on a canvas to warm up GPU shader
/// compilation cache. /// compilation cache.
@override @override
void warmUpOnCanvas(ui.Canvas canvas) { Future<void> warmUpOnCanvas(ui.Canvas canvas) {
final ui.RRect rrect = ui.RRect.fromLTRBXY(20.0, 20.0, 60.0, 60.0, 10.0, 10.0); final 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 rrectPath = ui.Path()..addRRect(rrect);
...@@ -179,5 +183,31 @@ class DefaultShaderWarmUp extends ShaderWarmUp { ...@@ -179,5 +183,31 @@ class DefaultShaderWarmUp extends ShaderWarmUp {
final ui.Paragraph paragraph = paragraphBuilder.build() final ui.Paragraph paragraph = paragraphBuilder.build()
..layout(const ui.ParagraphConstraints(width: 60.0)); ..layout(const ui.ParagraphConstraints(width: 60.0));
canvas.drawParagraph(paragraph, const ui.Offset(20.0, 20.0)); canvas.drawParagraph(paragraph, const ui.Offset(20.0, 20.0));
// Construct an image for drawImage related operations
const int imageWidth = 40;
const int imageHeight = 40;
final Uint8List pixels = Uint8List.fromList(List<int>.generate(
imageWidth * imageHeight * 4,
(int i) => i % 4 < 2 ? 0x00 : 0xFF, // opaque blue
));
final Completer<void> completer = Completer<void>();
ui.decodeImageFromPixels(pixels, imageWidth, imageHeight, ui.PixelFormat.rgba8888, (ui.Image image) {
// Warm up image shaders
canvas.translate(0.0, 80.0);
canvas.save();
final ui.Rect srcRect = ui.Rect.fromLTWH(0.0, 0.0, image.width.toDouble(), image.height.toDouble());
canvas.drawImage(image, const ui.Offset(20.0, 20.0), ui.Paint());
canvas.translate(80.0, 0.0);
canvas.drawImageRect(image, srcRect, ui.Rect.fromLTWH(20.0, 20.0, 20.0, 20.0), paints[0]);
canvas.translate(80.0, 0.0);
canvas.drawImageRect(image, srcRect, ui.Rect.fromLTWH(10.0, 10.0, 60.0, 60.0), paints[0]);
canvas.restore();
completer.complete();
});
return completer.future;
} }
} }
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