mirror of
https://github.com/flutter/flutter
synced 2024-08-27 03:50:33 +00:00
Reland remove DefaultShaderWarmup (#88455)
This commit is contained in:
parent
7a21bd1000
commit
c02961256a
|
@ -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',
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This example shows the draw operations to warm up the GPU shaders by default.
|
||||
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Future<void> beginFrame(Duration timeStamp) async {
|
||||
// PAINT
|
||||
final ui.PictureRecorder recorder = ui.PictureRecorder();
|
||||
const 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);
|
||||
await const DefaultShaderWarmUp(
|
||||
drawCallSpacing: 80.0,
|
||||
canvasSize: ui.Size(1024, 1024),
|
||||
).warmUpOnCanvas(canvas);
|
||||
final ui.Picture picture = recorder.endRecording();
|
||||
|
||||
// COMPOSITE
|
||||
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder()
|
||||
..pushClipRect(paintBounds)
|
||||
..addPicture(ui.Offset.zero, picture)
|
||||
..pop();
|
||||
ui.window.render(sceneBuilder.build());
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
ui.window.onBeginFrame = beginFrame;
|
||||
ui.window.scheduleFrame();
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// ignore: deprecated_member_use
|
||||
import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf;
|
||||
|
||||
import '../../../raw/shader_warm_up.dart' as demo;
|
||||
|
||||
void main() {
|
||||
test('layers smoketest for raw/shader_warm_up.dart', () {
|
||||
demo.main();
|
||||
});
|
||||
}
|
|
@ -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
|
||||
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
class TestCanvas implements Canvas {
|
||||
TestCanvas();
|
||||
|
||||
final List<Invocation> invocations = <Invocation>[];
|
||||
|
||||
@override
|
||||
void noSuchMethod(Invocation invocation) {
|
||||
invocations.add(invocation);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('DefaultShaderWarmUp has expected canvas invocations', () {
|
||||
final TestCanvas canvas = TestCanvas();
|
||||
const DefaultShaderWarmUp s = DefaultShaderWarmUp();
|
||||
s.warmUpOnCanvas(canvas);
|
||||
|
||||
bool hasDrawRectAfterClipRRect = false;
|
||||
for (int i = 0; i < canvas.invocations.length - 1; i += 1) {
|
||||
if (canvas.invocations[i].memberName == #clipRRect && canvas.invocations[i + 1].memberName == #drawRect) {
|
||||
hasDrawRectAfterClipRRect = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
expect(hasDrawRectAfterClipRRect, true);
|
||||
});
|
||||
|
||||
test('ShaderWarmUp.execute disposes the image and picture', () async {
|
||||
const DefaultShaderWarmUp shaderWarmUp = DefaultShaderWarmUp();
|
||||
late ui.Picture capturedPicture;
|
||||
late ui.Image capturedImage;
|
||||
debugCaptureShaderWarmUpPicture = (ui.Picture picture) {
|
||||
capturedPicture = picture;
|
||||
expect(picture.approximateBytesUsed, greaterThan(0));
|
||||
return true;
|
||||
};
|
||||
debugCaptureShaderWarmUpImage = (ui.Image image) {
|
||||
capturedImage = image;
|
||||
expect(image.width, 100);
|
||||
expect(image.height, 100);
|
||||
return true;
|
||||
};
|
||||
await shaderWarmUp.execute();
|
||||
expect(
|
||||
() => capturedPicture.approximateBytesUsed,
|
||||
throwsA(isA<String>().having((String message) => message, 'message', 'Object has been disposed.')),
|
||||
);
|
||||
expect(capturedImage.debugDisposed, true);
|
||||
}, skip: kIsWeb); // [intended] Browser doesn't support approximateBytesUsed and doesn't rasterize the picture at this time.
|
||||
}
|
Loading…
Reference in a new issue