blob: 8b230803c83d717272a9d11ef33ddd2bdb60b93c [file] [log] [blame]
// 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/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/semantics.dart';
import 'material3.dart';
import 'recorder.dart';
/// Measures the cost of semantics when constructing screens containing
/// Material 3 widgets.
class BenchMaterial3Semantics extends WidgetBuildRecorder {
BenchMaterial3Semantics() : super(name: benchmarkName);
static const String benchmarkName = 'bench_material3_semantics';
@override
Future<void> setUpAll() async {
FlutterTimeline.debugCollectionEnabled = true;
super.setUpAll();
SemanticsBinding.instance.ensureSemantics();
}
@override
Future<void> tearDownAll() async {
FlutterTimeline.debugReset();
}
@override
void frameDidDraw() {
// Only record frames that show the widget. Frames that remove the widget
// are not interesting.
if (showWidget) {
final AggregatedTimings timings = FlutterTimeline.debugCollect();
final AggregatedTimedBlock semanticsBlock = timings.getAggregated('SEMANTICS');
final AggregatedTimedBlock getFragmentBlock = timings.getAggregated('Semantics.GetFragment');
final AggregatedTimedBlock compileChildrenBlock = timings.getAggregated('Semantics.compileChildren');
profile!.addTimedBlock(semanticsBlock, reported: true);
profile!.addTimedBlock(getFragmentBlock, reported: true);
profile!.addTimedBlock(compileChildrenBlock, reported: true);
}
super.frameDidDraw();
FlutterTimeline.debugReset();
}
@override
Widget createWidget() {
return const SingleColumnMaterial3Components();
}
}
/// Measures the cost of semantics when scrolling screens containing Material 3
/// widgets.
///
/// The implementation uses a ListView that jumps the scroll position between
/// 0 and 1 every frame. Such a small delta is not enough for lazy rendering to
/// add/remove widgets, but its enough to trigger the framework to recompute
/// some of the semantics.
///
/// The expected output numbers of this benchmarks should be very small as
/// scrolling a list view should be a matter of shifting some widgets and
/// updating the projected clip imposed by the viewport. As of June 2023, the
/// numbers are not great. Semantics consumes >50% of frame time.
class BenchMaterial3ScrollSemantics extends WidgetRecorder {
BenchMaterial3ScrollSemantics() : super(name: benchmarkName);
static const String benchmarkName = 'bench_material3_scroll_semantics';
@override
Future<void> setUpAll() async {
FlutterTimeline.debugCollectionEnabled = true;
super.setUpAll();
SemanticsBinding.instance.ensureSemantics();
}
@override
Future<void> tearDownAll() async {
FlutterTimeline.debugReset();
}
@override
void frameDidDraw() {
final AggregatedTimings timings = FlutterTimeline.debugCollect();
final AggregatedTimedBlock semanticsBlock = timings.getAggregated('SEMANTICS');
final AggregatedTimedBlock getFragmentBlock = timings.getAggregated('Semantics.GetFragment');
final AggregatedTimedBlock compileChildrenBlock = timings.getAggregated('Semantics.compileChildren');
profile!.addTimedBlock(semanticsBlock, reported: true);
profile!.addTimedBlock(getFragmentBlock, reported: true);
profile!.addTimedBlock(compileChildrenBlock, reported: true);
super.frameDidDraw();
FlutterTimeline.debugReset();
}
@override
Widget createWidget() => _ScrollTest();
}
class _ScrollTest extends StatefulWidget {
@override
State<_ScrollTest> createState() => _ScrollTestState();
}
class _ScrollTestState extends State<_ScrollTest> with SingleTickerProviderStateMixin {
late final Ticker ticker;
late final ScrollController scrollController;
@override
void initState() {
super.initState();
scrollController = ScrollController();
bool forward = true;
// A one-off timer is necessary to allow the framework to measure the
// available scroll extents before the scroll controller can be exercised
// to change the scroll position.
Timer.run(() {
ticker = createTicker((_) {
scrollController.jumpTo(forward ? 1 : 0);
forward = !forward;
});
ticker.start();
});
}
@override
void dispose() {
ticker.dispose();
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SingleColumnMaterial3Components(
scrollController: scrollController,
);
}
}