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:
parent
98739667ae
commit
c63dcf3bcd
@ -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;
|
|||||||
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) {
|
|||||||
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 @@
|
|||||||
// 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 {
|
|||||||
/// 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 {
|
|||||||
/// 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 {
|
|||||||
/// 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 {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user