Unverified Commit 65b19560 authored by Tong Mu's avatar Tong Mu Committed by GitHub

Add benchmark for Mouse region (web) (#59803)

parent e48b7e99
// 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 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';
import 'recorder.dart';
class _NestedMouseRegion extends StatelessWidget {
const _NestedMouseRegion({this.nests, this.child});
final int nests;
final Widget child;
@override
Widget build(BuildContext context) {
Widget current = child;
for (int i = 0; i < nests; i++) {
current = MouseRegion(
onEnter: (_) {},
child: child,
);
}
return current;
}
}
/// Creates a grid of mouse regions, then continuously hover over them.
///
/// Measures our ability to hit test mouse regions.
class BenchMouseRegionGridHover extends WidgetRecorder {
BenchMouseRegionGridHover() : super(name: benchmarkName);
static const String benchmarkName = 'bench_mouse_region_grid_hover';
final _Tester tester = _Tester();
// Use a non-trivial border to force Web to switch painter
Border _getBorder(int columnIndex, int rowIndex) {
const BorderSide defaultBorderSide = BorderSide();
return Border(
left: columnIndex == 0 ? defaultBorderSide : BorderSide.none,
top: rowIndex == 0 ? defaultBorderSide : BorderSide.none,
right: defaultBorderSide,
bottom: defaultBorderSide,
);
}
bool started = false;
@override
void frameDidDraw() {
if (!started) {
started = true;
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) async {
tester.start();
});
}
super.frameDidDraw();
}
@override
Widget createWidget() {
const int rowsCount = 60;
const int columnsCount = 20;
const double containerSize = 20;
return Directionality(
textDirection: TextDirection.ltr,
child: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 400,
height: 400,
child: ListView.builder(
itemCount: rowsCount,
cacheExtent: rowsCount * containerSize,
physics: const ClampingScrollPhysics(),
itemBuilder: (BuildContext context, int rowIndex) => _NestedMouseRegion(
nests: 10,
child: Row(
children: List<Widget>.generate(
columnsCount,
(int columnIndex) => _NestedMouseRegion(
nests: 10,
child: Container(
decoration: BoxDecoration(
border: _getBorder(columnIndex, rowIndex),
color: Color.fromARGB(255, rowIndex * 20 % 256, 127, 127),
),
width: containerSize,
height: containerSize,
),
),
),
),
),
),
),
),
);
}
}
class _UntilNextFrame {
_UntilNextFrame._();
static Completer<void> _completer;
static Future<void> wait() {
if (_UntilNextFrame._completer == null) {
_UntilNextFrame._completer = Completer<void>();
SchedulerBinding.instance.addPostFrameCallback((_) {
_UntilNextFrame._completer.complete(null);
_UntilNextFrame._completer = null;
});
}
return _UntilNextFrame._completer.future;
}
}
class _Tester {
static const Duration hoverDuration = Duration(milliseconds: 20);
bool _stopped = false;
TestGesture get gesture {
return _gesture ??= TestGesture(
dispatcher: (PointerEvent event, HitTestResult result) async {
RendererBinding.instance.dispatchEvent(event, result);
},
hitTester: (Offset location) {
final HitTestResult result = HitTestResult();
RendererBinding.instance.hitTest(result, location);
return result;
},
kind: PointerDeviceKind.mouse,
);
}
TestGesture _gesture;
Duration currentTime = Duration.zero;
Future<void> _hoverTo(Offset location, Duration duration) async {
currentTime += duration;
await gesture.moveTo(location, timeStamp: currentTime);
await _UntilNextFrame.wait();
}
Future<void> start() async {
await Future<void>.delayed(Duration.zero);
while (!_stopped) {
await _hoverTo(const Offset(30, 10), hoverDuration);
await _hoverTo(const Offset(10, 370), hoverDuration);
await _hoverTo(const Offset(370, 390), hoverDuration);
await _hoverTo(const Offset(390, 30), hoverDuration);
}
}
void stop() {
_stopped = true;
}
}
// 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 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';
import 'recorder.dart';
/// Creates a grid of mouse regions, then continuously scrolls them up and down.
///
/// Measures our ability to render mouse regions.
class BenchMouseRegionGridScroll extends WidgetRecorder {
BenchMouseRegionGridScroll() : super(name: benchmarkName);
static const String benchmarkName = 'bench_mouse_region_grid_scroll';
final _Tester tester = _Tester();
// Use a non-trivial border to force Web to switch painter
Border _getBorder(int columnIndex, int rowIndex) {
const BorderSide defaultBorderSide = BorderSide();
return Border(
left: columnIndex == 0 ? defaultBorderSide : BorderSide.none,
top: rowIndex == 0 ? defaultBorderSide : BorderSide.none,
right: defaultBorderSide,
bottom: defaultBorderSide,
);
}
bool started = false;
@override
void frameDidDraw() {
if (!started) {
started = true;
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) async {
tester.start();
final VoidCallback localDidStop = didStop;
didStop = () {
if (localDidStop != null)
localDidStop();
tester.stop();
};
});
}
super.frameDidDraw();
}
@override
Widget createWidget() {
const int rowsCount = 60;
const int columnsCount = 20;
const double containerSize = 20;
return Directionality(
textDirection: TextDirection.ltr,
child: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 400,
height: 400,
child: ListView.builder(
itemCount: rowsCount,
cacheExtent: rowsCount * containerSize,
physics: const ClampingScrollPhysics(),
itemBuilder: (BuildContext context, int rowIndex) => Row(
children: List<Widget>.generate(
columnsCount,
(int columnIndex) => MouseRegion(
onEnter: (_) {},
child: Container(
decoration: BoxDecoration(
border: _getBorder(columnIndex, rowIndex),
color: Color.fromARGB(255, rowIndex * 20 % 256, 127, 127),
),
width: containerSize,
height: containerSize,
),
),
),
),
),
),
),
);
}
}
class _UntilNextFrame {
_UntilNextFrame._();
static Completer<void> _completer;
static Future<void> wait() {
if (_UntilNextFrame._completer == null) {
_UntilNextFrame._completer = Completer<void>();
SchedulerBinding.instance.addPostFrameCallback((_) {
_UntilNextFrame._completer.complete(null);
_UntilNextFrame._completer = null;
});
}
return _UntilNextFrame._completer.future;
}
}
class _Tester {
static const int scrollFrequency = 60;
static const Offset dragStartLocation = Offset(200, 200);
static const Offset dragUpOffset = Offset(0, 200);
static const Offset dragDownOffset = Offset(0, -200);
static const Duration dragDuration = Duration(milliseconds: 200);
bool _stopped = false;
TestGesture get gesture {
return _gesture ??= TestGesture(
dispatcher: (PointerEvent event, HitTestResult result) async {
RendererBinding.instance.dispatchEvent(event, result);
},
hitTester: (Offset location) {
final HitTestResult result = HitTestResult();
RendererBinding.instance.hitTest(result, location);
return result;
},
kind: PointerDeviceKind.mouse,
);
}
TestGesture _gesture;
Duration currentTime = Duration.zero;
Future<void> _scroll(Offset start, Offset offset, Duration duration) async {
final int durationMs = duration.inMilliseconds;
final Duration fullFrameDuration = const Duration(seconds: 1) ~/ scrollFrequency;
final int frameDurationMs = fullFrameDuration.inMilliseconds;
final int fullFrames = duration.inMilliseconds ~/ frameDurationMs;
final Offset fullFrameOffset = offset * ((frameDurationMs as double) / durationMs);
final Duration finalFrameDuration = duration - fullFrameDuration * fullFrames;
final Offset finalFrameOffset = offset - fullFrameOffset * (fullFrames as double);
await gesture.down(start, timeStamp: currentTime);
for (int frame = 0; frame < fullFrames; frame += 1) {
currentTime += fullFrameDuration;
await gesture.moveBy(fullFrameOffset, timeStamp: currentTime);
await _UntilNextFrame.wait();
}
if (finalFrameOffset != Offset.zero) {
currentTime += finalFrameDuration;
await gesture.moveBy(finalFrameOffset, timeStamp: currentTime);
await _UntilNextFrame.wait();
}
await gesture.up(timeStamp: currentTime);
}
Future<void> start() async {
await Future<void>.delayed(Duration.zero);
while (!_stopped) {
await _scroll(dragStartLocation, dragUpOffset, dragDuration);
await _scroll(dragStartLocation, dragDownOffset, dragDuration);
}
}
void stop() {
_stopped = true;
}
}
......@@ -16,6 +16,8 @@ import 'src/web/bench_child_layers.dart';
import 'src/web/bench_clipped_out_pictures.dart';
import 'src/web/bench_draw_rect.dart';
import 'src/web/bench_dynamic_clip_on_static_picture.dart';
import 'src/web/bench_mouse_region_grid_hover.dart';
import 'src/web/bench_mouse_region_grid_scroll.dart';
import 'src/web/bench_paths.dart';
import 'src/web/bench_picture_recording.dart';
import 'src/web/bench_simple_lazy_text_scroll.dart';
......@@ -42,6 +44,8 @@ final Map<String, RecorderFactory> benchmarks = <String, RecorderFactory>{
BenchDynamicClipOnStaticPicture.benchmarkName: () => BenchDynamicClipOnStaticPicture(),
BenchPictureRecording.benchmarkName: () => BenchPictureRecording(),
BenchUpdateManyChildLayers.benchmarkName: () => BenchUpdateManyChildLayers(),
BenchMouseRegionGridScroll.benchmarkName: () => BenchMouseRegionGridScroll(),
BenchMouseRegionGridHover.benchmarkName: () => BenchMouseRegionGridHover(),
if (isCanvasKit)
BenchBuildColorsGrid.canvasKitBenchmarkName: () => BenchBuildColorsGrid.canvasKit(),
......
......@@ -341,10 +341,10 @@ class TestGesture {
/// Dispatch a pointer down event at the given `downLocation`, caching the
/// hit test result.
Future<void> down(Offset downLocation) async {
Future<void> down(Offset downLocation, { Duration timeStamp = Duration.zero }) async {
return TestAsyncUtils.guard<void>(() async {
_result = _hitTester(downLocation);
return _dispatcher(_pointer.down(downLocation), _result);
return _dispatcher(_pointer.down(downLocation, timeStamp: timeStamp), _result);
});
}
......@@ -416,10 +416,10 @@ class TestGesture {
}
/// End the gesture by releasing the pointer.
Future<void> up() {
Future<void> up({ Duration timeStamp = Duration.zero }) {
return TestAsyncUtils.guard<void>(() async {
assert(_pointer._isDown);
await _dispatcher(_pointer.up(), _result);
await _dispatcher(_pointer.up(timeStamp: timeStamp), _result);
assert(!_pointer._isDown);
_result = null;
});
......@@ -428,10 +428,10 @@ class TestGesture {
/// End the gesture by canceling the pointer (as would happen if the
/// system showed a modal dialog on top of the Flutter application,
/// for instance).
Future<void> cancel() {
Future<void> cancel({ Duration timeStamp = Duration.zero }) {
return TestAsyncUtils.guard<void>(() async {
assert(_pointer._isDown);
await _dispatcher(_pointer.cancel(), _result);
await _dispatcher(_pointer.cancel(timeStamp: timeStamp), _result);
assert(!_pointer._isDown);
_result = null;
});
......
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