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
This commit is contained in:
liyuqian 2019-02-28 20:07:55 -08:00 committed by GitHub
parent 98739667ae
commit c63dcf3bcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 10 deletions

View File

@ -10,8 +10,8 @@ import 'package:macrobenchmarks/main.dart' as app;
class CubicBezierShaderWarmUp extends DefaultShaderWarmUp {
@override
void warmUpOnCanvas(Canvas canvas) {
super.warmUpOnCanvas(canvas);
Future<void> warmUpOnCanvas(Canvas canvas) async {
await super.warmUpOnCanvas(canvas);
// Warm up the cubic shaders used by CubicBezierPage.
//

View File

@ -9,14 +9,14 @@ import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart' show DefaultShaderWarmUp;
void beginFrame(Duration timeStamp) {
Future<void> beginFrame(Duration timeStamp) async {
// PAINT
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Rect paintBounds = ui.Rect.fromLTRB(0, 0, 1000, 1000);
final ui.Canvas canvas = ui.Canvas(recorder, paintBounds);
final ui.Paint backgroundPaint = ui.Paint()..color = Colors.white;
canvas.drawRect(paintBounds, backgroundPaint);
const DefaultShaderWarmUp().warmUpOnCanvas(canvas);
await const DefaultShaderWarmUp().warmUpOnCanvas(canvas);
final ui.Picture picture = recorder.endRecording();
// COMPOSITE
@ -27,7 +27,7 @@ void beginFrame(Duration timeStamp) {
ui.window.render(sceneBuilder.build());
}
void main() {
Future<void> main() async {
ui.window.onBeginFrame = beginFrame;
ui.window.scheduleFrame();
}

View File

@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:developer';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
@ -65,6 +67,8 @@ abstract class ShaderWarmUp {
/// Trigger draw operations on a given canvas to warm up GPU shader
/// 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
/// process, try capture an skp using `flutter screenshot --observatory-
/// port=<port> --type=skia` and analyze it with https://debugger.skia.org.
@ -73,20 +77,20 @@ abstract class ShaderWarmUp {
/// Skia draw operations are commonly used, and which shader compilations
/// are causing janks.
@protected
void warmUpOnCanvas(ui.Canvas canvas);
Future<void> warmUpOnCanvas(ui.Canvas canvas);
/// Construct an offscreen image of [size], and execute [warmUpOnCanvas] on a
/// canvas associated with that image.
void execute() {
Future<void> execute() async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
warmUpOnCanvas(canvas);
await warmUpOnCanvas(canvas);
final ui.Picture picture = recorder.endRecording();
final TimelineTask shaderWarmUpTask = TimelineTask();
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();
});
}
@ -104,7 +108,7 @@ class DefaultShaderWarmUp extends ShaderWarmUp {
/// Trigger common draw operations on a canvas to warm up GPU shader
/// compilation cache.
@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.Path rrectPath = ui.Path()..addRRect(rrect);
@ -179,5 +183,31 @@ class DefaultShaderWarmUp extends ShaderWarmUp {
final ui.Paragraph paragraph = paragraphBuilder.build()
..layout(const ui.ParagraphConstraints(width: 60.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;
}
}