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

Perf test for color filter with saveLayer (#52063)

parent 3bc36095
......@@ -11,3 +11,4 @@ const String kPictureCacheRouteName = '/picture_cache';
const String kLargeImagesRouteName = '/large_images';
const String kTextRouteName = '/text';
const String kAnimatedPlaceholderRouteName = '/animated_placeholder';
const String kColorFilterAndFadeRouteName = '/color_filter_and_fade';
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:macrobenchmarks/src/color_filter_and_fade.dart';
import 'package:macrobenchmarks/src/large_images.dart';
import 'package:macrobenchmarks/src/picture_cache.dart';
......@@ -38,6 +39,7 @@ class MacrobenchmarksApp extends StatelessWidget {
kLargeImagesRouteName: (BuildContext context) => LargeImagesPage(),
kTextRouteName: (BuildContext context) => TextPage(),
kAnimatedPlaceholderRouteName: (BuildContext context) => AnimatedPlaceholderPage(),
kColorFilterAndFadeRouteName: (BuildContext context) => ColorFilterAndFadePage(),
},
);
}
......@@ -115,6 +117,13 @@ class HomePage extends StatelessWidget {
Navigator.pushNamed(context, kAnimatedPlaceholderRouteName);
},
),
RaisedButton(
key: const Key(kColorFilterAndFadeRouteName),
child: const Text('Color Filter and Fade'),
onPressed: () {
Navigator.pushNamed(context, kColorFilterAndFadeRouteName);
},
),
],
),
);
......
// 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 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
// This tests whether the Opacity layer raster cache works with color filters.
// See https://github.com/flutter/flutter/issues/51975.
class ColorFilterAndFadePage extends StatefulWidget {
@override
_ColorFilterAndFadePageState createState() => _ColorFilterAndFadePageState();
}
class _ColorFilterAndFadePageState extends State<ColorFilterAndFadePage> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final Widget shadowWidget = _ShadowWidget(
width: 24,
height: 24,
useColorFilter: _useColorFilter,
shadow: ui.Shadow(
color: Colors.black45,
offset: const Offset(0.0, 2.0),
blurRadius: 4.0,
),
);
final Widget row = Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
shadowWidget,
const SizedBox(width: 12),
shadowWidget,
const SizedBox(width: 12),
shadowWidget,
const SizedBox(width: 12),
shadowWidget,
const SizedBox(width: 12),
shadowWidget,
const SizedBox(width: 12),
],
);
final Widget column = Column(mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
row,
const SizedBox(height: 12),
row,
const SizedBox(height: 12),
row,
const SizedBox(height: 12),
row,
const SizedBox(height: 12),
],
);
final Widget fadeTransition = FadeTransition(
opacity: _opacityAnimation,
// This RepaintBoundary is necessary to not let the opacity change
// invalidate the layer raster cache below. This is necessary with
// or without the color filter.
child: RepaintBoundary(
child: column,
),
);
return Scaffold(
backgroundColor: Colors.lightBlue,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
fadeTransition,
Container(height: 20),
const Text('Use Color Filter:'),
Checkbox(
value: _useColorFilter,
onChanged: (bool value) {
setState(() {
_useColorFilter = value;
});
},
),
],
),
),
);
}
// Create a looping fade-in fade-out animation for opacity.
void _initAnimation() {
_controller = AnimationController(duration: const Duration(seconds: 3), vsync: this);
_opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_opacityAnimation.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
_controller.reverse();
} else if (status == AnimationStatus.dismissed) {
_controller.forward();
}
});
_controller.forward();
}
@override
void initState() {
super.initState();
_initAnimation();
}
AnimationController _controller;
Animation<double> _opacityAnimation;
bool _useColorFilter = true;
}
class _ShadowWidget extends StatelessWidget {
const _ShadowWidget({
@required this.width,
@required this.height,
@required this.useColorFilter,
@required this.shadow,
});
final double width;
final double height;
final bool useColorFilter;
final Shadow shadow;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: CustomPaint(
painter: _ShadowPainter(
useColorFilter: useColorFilter,
shadow: shadow,
),
size: Size(width, height),
isComplex: true,
willChange: false,
),
);
}
}
class _ShadowPainter extends CustomPainter {
const _ShadowPainter({this.useColorFilter, @required this.shadow});
final bool useColorFilter;
final Shadow shadow;
@override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
final Paint paint = Paint();
if (useColorFilter) {
paint.colorFilter = ColorFilter.mode(shadow.color, BlendMode.srcIn);
}
canvas.saveLayer(null, paint);
canvas.translate(shadow.offset.dx, shadow.offset.dy);
canvas.drawRect(rect, Paint());
canvas.drawRect(rect, Paint()..maskFilter = MaskFilter.blur(BlurStyle.normal, shadow.blurSigma));
canvas.restore();
canvas.drawRect(rect, Paint()..color = useColorFilter ? Colors.white : Colors.black);
}
@override
bool shouldRepaint(_ShadowPainter oldDelegate) => oldDelegate.useColorFilter != useColorFilter;
}
// 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 'package:flutter_driver/driver_extension.dart';
import 'package:macrobenchmarks/main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}
// 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 'package:macrobenchmarks/common.dart';
import 'util.dart';
void main() {
macroPerfTest(
'color_filter_and_fade_perf',
kColorFilterAndFadeRouteName,
pageDelay: const Duration(seconds: 1),
duration: const Duration(seconds: 10),
);
}
// 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:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
Future<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.android;
await task(createColorFilterAndFadePerfTest());
}
......@@ -173,6 +173,14 @@ TaskFunction createTextfieldPerfTest() {
).run;
}
TaskFunction createColorFilterAndFadePerfTest() {
return PerfTest(
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
'test_driver/color_filter_and_fade_perf.dart',
'color_filter_and_fade_perf',
).run;
}
/// Measure application startup performance.
class StartupTest {
......
......@@ -190,6 +190,12 @@ tasks:
stage: devicelab
required_agent_capabilities: ["mac/android"]
color_filter_and_fade_perf__timeline_summary:
description: >
Measures the runtime performance of color filter with fade on Android.
stage: devicelab
required_agent_capabilities: ["mac/android"]
flavors_test:
description: >
Checks that flavored builds work on Android.
......
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