Add benchmark for scrolling very long pictures (#133434)
This benchmark will track the performance of the RTree implementation to cull very large pictures down to just the portion visible on the screen.
This commit is contained in:
parent
82ded60350
commit
b51859ec33
20
.ci.yaml
20
.ci.yaml
@ -2114,6 +2114,16 @@ targets:
|
||||
["devicelab", "android", "linux"]
|
||||
task_name: fullscreen_textfield_perf__e2e_summary
|
||||
|
||||
- name: Linux_android very_long_picture_scrolling_perf__e2e_summary
|
||||
recipe: devicelab/devicelab_drone
|
||||
bringup: true
|
||||
presubmit: false
|
||||
timeout: 120
|
||||
properties:
|
||||
tags: >
|
||||
["devicelab", "android", "linux"]
|
||||
task_name: very_long_picture_scrolling_perf__e2e_summary
|
||||
|
||||
- name: Linux_android hello_world__memory
|
||||
recipe: devicelab/devicelab_drone
|
||||
presubmit: false
|
||||
@ -4196,6 +4206,16 @@ targets:
|
||||
["devicelab", "ios", "mac"]
|
||||
task_name: fullscreen_textfield_perf_ios__e2e_summary
|
||||
|
||||
- name: Mac_ios very_long_picture_scrolling_perf_ios__e2e_summary
|
||||
recipe: devicelab/devicelab_drone
|
||||
bringup: true
|
||||
presubmit: false
|
||||
timeout: 120
|
||||
properties:
|
||||
tags: >
|
||||
["devicelab", "ios", "mac"]
|
||||
task_name: very_long_picture_scrolling_perf_ios__e2e_summary
|
||||
|
||||
- name: Mac_ios tiles_scroll_perf_ios__timeline_summary
|
||||
recipe: devicelab/devicelab_drone
|
||||
presubmit: false
|
||||
|
@ -89,6 +89,7 @@
|
||||
/dev/devicelab/bin/tasks/spell_check_test_ios.dart @camsim99 @flutter/android
|
||||
/dev/devicelab/bin/tasks/spell_check_test.dart @camsim99 @flutter/android
|
||||
/dev/devicelab/bin/tasks/textfield_perf__e2e_summary.dart @zanderso @flutter/engine
|
||||
/dev/devicelab/bin/tasks/very_long_picture_scrolling_perf__e2e_summary.dart @flar @flutter/engine
|
||||
/dev/devicelab/bin/tasks/web_size__compile_test.dart @yjbanov @flutter/web
|
||||
/dev/devicelab/bin/tasks/wide_gamut_ios.dart @gaaclarke @flutter/engine
|
||||
/dev/devicelab/bin/tasks/animated_advanced_blend_perf__timeline_summary.dart @gaaclarke @flutter/engine
|
||||
@ -212,6 +213,7 @@
|
||||
/dev/devicelab/bin/tasks/route_test_ios.dart @vashworth @flutter/tool
|
||||
/dev/devicelab/bin/tasks/simple_animation_perf_ios.dart @cyanglaz @flutter/engine
|
||||
/dev/devicelab/bin/tasks/tiles_scroll_perf_ios__timeline_summary.dart @cyanglaz @flutter/engine
|
||||
/dev/devicelab/bin/tasks/very_long_picture_scrolling_perf_ios__e2e_summary.dart @flar @flutter/engine
|
||||
/dev/devicelab/bin/tasks/animated_blur_backdrop_filter_perf_ios__timeline_summary.dart @jonahwilliams @flutter/engine
|
||||
/dev/devicelab/bin/tasks/draw_points_perf_ios__timeline_summary.dart @jonahwilliams @flutter/engine
|
||||
/dev/devicelab/bin/tasks/draw_vertices_perf_ios__timeline_summary.dart @jonahwilliams @flutter/engine
|
||||
|
@ -13,6 +13,7 @@ const String kLargeImageChangerRouteName = '/large_image_changer';
|
||||
const String kLargeImagesRouteName = '/large_images';
|
||||
const String kPathTessellationRouteName = '/path_tessellation';
|
||||
const String kTextRouteName = '/text';
|
||||
const String kVeryLongPictureScrollingRouteName = '/very_long_picture_scrolling';
|
||||
const String kFullscreenTextRouteName = '/fullscreen_text';
|
||||
const String kAnimatedPlaceholderRouteName = '/animated_placeholder';
|
||||
const String kClipperCacheRouteName = '/clipper_cache';
|
||||
|
@ -42,6 +42,7 @@ import 'src/simple_scroll.dart';
|
||||
import 'src/sliders.dart';
|
||||
import 'src/stack_size.dart';
|
||||
import 'src/text.dart';
|
||||
import 'src/very_long_picture_scrolling.dart';
|
||||
|
||||
const String kMacrobenchmarks = 'Macrobenchmarks';
|
||||
|
||||
@ -97,6 +98,7 @@ class MacrobenchmarksApp extends StatelessWidget {
|
||||
kDrawVerticesPageRouteName: (BuildContext context) => const DrawVerticesPage(),
|
||||
kDrawAtlasPageRouteName: (BuildContext context) => const DrawAtlasPage(),
|
||||
kAnimatedAdvancedBlend: (BuildContext context) => const AnimatedAdvancedBlend(),
|
||||
kVeryLongPictureScrollingRouteName: (BuildContext context) => const VeryLongPictureScrollingPerf(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -373,6 +375,13 @@ class HomePage extends StatelessWidget {
|
||||
Navigator.pushNamed(context, kAnimatedAdvancedBlend);
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
key: const Key(kVeryLongPictureScrollingRouteName),
|
||||
child: const Text('Very Long Picture Scrolling'),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, kVeryLongPictureScrollingRouteName);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -0,0 +1,240 @@
|
||||
// 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:math';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Adapted from test case submitted in
|
||||
// https://github.com/flutter/flutter/issues/92366
|
||||
// Converted to use fixed data rather than reading a waveform file
|
||||
class VeryLongPictureScrollingPerf extends StatefulWidget {
|
||||
const VeryLongPictureScrollingPerf({super.key});
|
||||
|
||||
@override
|
||||
State createState() => VeryLongPictureScrollingPerfState();
|
||||
}
|
||||
|
||||
class VeryLongPictureScrollingPerfState extends State<VeryLongPictureScrollingPerf> {
|
||||
bool consolidate = false;
|
||||
bool useList = false;
|
||||
Int16List waveData = loadGraph();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actions: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
const Text('list:'),
|
||||
Checkbox(value: useList, onChanged: (bool? value) => setState(() {
|
||||
useList = value!;
|
||||
}),),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
const Text('consolidate:'),
|
||||
Checkbox(value: consolidate, onChanged: (bool? value) => setState(() {
|
||||
consolidate = value!;
|
||||
}),),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
body: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: useList
|
||||
? ListView.builder(
|
||||
key: const ValueKey<String>('vlp_list_view_scrollable'),
|
||||
scrollDirection: Axis.horizontal,
|
||||
clipBehavior: Clip.none,
|
||||
itemCount: (waveData.length / 200).ceil(),
|
||||
itemExtent: 100,
|
||||
itemBuilder: (BuildContext context, int index) => CustomPaint(
|
||||
painter: PaintSomeTest(
|
||||
waveData: waveData,
|
||||
from: index * 200,
|
||||
to: min((index + 1) * 200, waveData.length - 1),
|
||||
)
|
||||
),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
key: const ValueKey<String>('vlp_single_child_scrollable'),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 20,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: RepaintBoundary(
|
||||
child: CustomPaint(
|
||||
isComplex: true,
|
||||
painter: PaintTest(
|
||||
consolidate: consolidate,
|
||||
waveData: waveData,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PaintTest extends CustomPainter {
|
||||
const PaintTest({
|
||||
required this.consolidate,
|
||||
required this.waveData,
|
||||
});
|
||||
|
||||
final bool consolidate;
|
||||
final Int16List waveData;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final double height = size.height;
|
||||
double x = 0;
|
||||
const double strokeSize = .5;
|
||||
const double zoomFactor = .5;
|
||||
|
||||
final Paint paintPos = Paint()
|
||||
..color = Colors.pink
|
||||
..strokeWidth = strokeSize
|
||||
..isAntiAlias = false
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final Paint paintNeg = Paint()
|
||||
..color = Colors.pink
|
||||
..strokeWidth = strokeSize
|
||||
..isAntiAlias = false
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final Paint paintZero = Paint()
|
||||
..color = Colors.green
|
||||
..strokeWidth = strokeSize
|
||||
..isAntiAlias = false
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
int index = 0;
|
||||
Paint? listPaint;
|
||||
final Float32List offsets = Float32List(consolidate ? waveData.length * 4 : 4);
|
||||
int used = 0;
|
||||
for (index = 0; index < waveData.length; index++) {
|
||||
Paint curPaint;
|
||||
Offset p1;
|
||||
if (waveData[index].isNegative) {
|
||||
curPaint = paintPos;
|
||||
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32768 * (height / 2));
|
||||
} else if (waveData[index] == 0) {
|
||||
curPaint = paintZero;
|
||||
p1 = Offset(x, height * 1 / 2 + 1);
|
||||
} else {
|
||||
curPaint = (waveData[index] == 0) ? paintZero : paintNeg;
|
||||
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32767 * (height / 2));
|
||||
}
|
||||
final Offset p0 = Offset(x, height * 1 / 2);
|
||||
if (consolidate) {
|
||||
if (listPaint != null && listPaint != curPaint) {
|
||||
canvas.drawRawPoints(PointMode.lines, offsets.sublist(0, used), listPaint);
|
||||
used = 0;
|
||||
}
|
||||
listPaint = curPaint;
|
||||
offsets[used++] = p0.dx;
|
||||
offsets[used++] = p0.dy;
|
||||
offsets[used++] = p1.dx;
|
||||
offsets[used++] = p1.dy;
|
||||
} else {
|
||||
canvas.drawLine(p0, p1, curPaint);
|
||||
}
|
||||
x += zoomFactor;
|
||||
}
|
||||
if (consolidate && used > 0) {
|
||||
canvas.drawRawPoints(PointMode.lines, offsets.sublist(0, used), listPaint!);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||
return oldDelegate is! PaintTest ||
|
||||
oldDelegate.consolidate != consolidate ||
|
||||
oldDelegate.waveData != waveData;
|
||||
}
|
||||
}
|
||||
|
||||
class PaintSomeTest extends CustomPainter {
|
||||
const PaintSomeTest({
|
||||
required this.waveData,
|
||||
int? from,
|
||||
int? to,
|
||||
}) : from = from ?? 0, to = to?? waveData.length;
|
||||
|
||||
final Int16List waveData;
|
||||
final int from;
|
||||
final int to;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final double height = size.height;
|
||||
double x = 0;
|
||||
const double strokeSize = .5;
|
||||
const double zoomFactor = .5;
|
||||
|
||||
final Paint paintPos = Paint()
|
||||
..color = Colors.pink
|
||||
..strokeWidth = strokeSize
|
||||
..isAntiAlias = false
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final Paint paintNeg = Paint()
|
||||
..color = Colors.pink
|
||||
..strokeWidth = strokeSize
|
||||
..isAntiAlias = false
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final Paint paintZero = Paint()
|
||||
..color = Colors.green
|
||||
..strokeWidth = strokeSize
|
||||
..isAntiAlias = false
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
for (int index = from; index <= to; index++) {
|
||||
Paint curPaint;
|
||||
Offset p1;
|
||||
if (waveData[index].isNegative) {
|
||||
curPaint = paintPos;
|
||||
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32768 * (height / 2));
|
||||
} else if (waveData[index] == 0) {
|
||||
curPaint = paintZero;
|
||||
p1 = Offset(x, height * 1 / 2 + 1);
|
||||
} else {
|
||||
curPaint = (waveData[index] == 0) ? paintZero : paintNeg;
|
||||
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32767 * (height / 2));
|
||||
}
|
||||
final Offset p0 = Offset(x, height * 1 / 2);
|
||||
canvas.drawLine(p0, p1, curPaint);
|
||||
x += zoomFactor;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||
return oldDelegate is! PaintSomeTest ||
|
||||
oldDelegate.waveData != waveData ||
|
||||
oldDelegate.from != from ||
|
||||
oldDelegate.to != to;
|
||||
}
|
||||
}
|
||||
|
||||
Int16List loadGraph() {
|
||||
final Int16List waveData = Int16List(350000);
|
||||
final Random r = Random(0x42);
|
||||
for (int i = 0; i < waveData.length; i++) {
|
||||
waveData[i] = r.nextInt(32768) - 16384;
|
||||
}
|
||||
return waveData;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
// 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/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:macrobenchmarks/common.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
void main() {
|
||||
macroPerfTestE2E(
|
||||
'very_long_picture_scrolling_perf',
|
||||
kVeryLongPictureScrollingRouteName,
|
||||
pageDelay: const Duration(seconds: 1),
|
||||
duration: const Duration(seconds: 30),
|
||||
body: (WidgetController controller) async {
|
||||
final Finder nestedScroll = find.byKey(const ValueKey<String>('vlp_single_child_scrollable'));
|
||||
expect(nestedScroll, findsOneWidget);
|
||||
Future<void> scrollOnce(double offset) async {
|
||||
await controller.timedDrag(
|
||||
nestedScroll,
|
||||
Offset(offset, 0.0),
|
||||
const Duration(milliseconds: 3500),
|
||||
);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
}
|
||||
for (int i = 0; i < 2; i += 1) {
|
||||
await scrollOnce(-3000.0);
|
||||
await scrollOnce(-3000.0);
|
||||
await scrollOnce(3000.0);
|
||||
await scrollOnce(3000.0);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
// 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/framework/devices.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/tasks/perf_tests.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.android;
|
||||
await task(createVeryLongPictureScrollingPerfE2ETest(enableImpeller: false));
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
// 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/framework/devices.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/tasks/perf_tests.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.ios;
|
||||
await task(createVeryLongPictureScrollingPerfE2ETest(enableImpeller: false));
|
||||
}
|
@ -308,6 +308,13 @@ TaskFunction createTextfieldPerfE2ETest() {
|
||||
).run;
|
||||
}
|
||||
|
||||
TaskFunction createVeryLongPictureScrollingPerfE2ETest({required bool enableImpeller}) {
|
||||
return PerfTest.e2e(
|
||||
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
|
||||
'test/very_long_picture_scrolling_perf_e2e.dart',
|
||||
enableImpeller: enableImpeller,
|
||||
).run;
|
||||
}
|
||||
TaskFunction createSlidersPerfTest() {
|
||||
return PerfTest(
|
||||
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
|
||||
|
Loading…
x
Reference in New Issue
Block a user