diff --git a/packages/web_benchmarks/CHANGELOG.md b/packages/web_benchmarks/CHANGELOG.md index 615bf8241158f..434f9bee76df6 100644 --- a/packages/web_benchmarks/CHANGELOG.md +++ b/packages/web_benchmarks/CHANGELOG.md @@ -1,6 +1,9 @@ -## NEXT +## 2.0.2 * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Updates benchmark server to serve the app as `crossOriginIsolated`. This +allows us access to high precision timers and allows wasm benchmarks to run +properly as well. ## 2.0.1 diff --git a/packages/web_benchmarks/lib/src/runner.dart b/packages/web_benchmarks/lib/src/runner.dart index 87e924194e934..3ad6f86c96e4b 100644 --- a/packages/web_benchmarks/lib/src/runner.dart +++ b/packages/web_benchmarks/lib/src/runner.dart @@ -160,10 +160,26 @@ class BenchmarkServer { Cascade cascade = Cascade(); // Serves the static files built for the app (html, js, images, fonts, etc) - cascade = cascade.add(createStaticHandler( + final Handler buildFolderHandler = createStaticHandler( path.join(benchmarkAppDirectory.path, 'build', 'web'), defaultDocument: 'index.html', - )); + ); + // We want our page to be crossOriginIsolated. This will allow us to run the + // skwasm renderer, which uses a SharedArrayBuffer, which requires the page + // to be crossOriginIsolated. But also, even in the non-skwasm case, running + // in crossOriginIsolated gives us access to more accurate timers which are + // useful for capturing good benchmarking data. + // See https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/High_precision_timing#reduced_precision + cascade = cascade.add((Request request) async { + final Response response = await buildFolderHandler(request); + if (response.mimeType == 'text/html') { + return response.change(headers: { + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + }); + } + return response; + }); // Serves the benchmark server API used by the benchmark app to coordinate // the running of benchmarks. diff --git a/packages/web_benchmarks/pubspec.yaml b/packages/web_benchmarks/pubspec.yaml index 02d71a4b0b49e..6386767133d8f 100644 --- a/packages/web_benchmarks/pubspec.yaml +++ b/packages/web_benchmarks/pubspec.yaml @@ -2,7 +2,7 @@ name: web_benchmarks description: A benchmark harness for performance-testing Flutter apps in Chrome. repository: https://github.com/flutter/packages/tree/main/packages/web_benchmarks issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+web_benchmarks%22 -version: 2.0.1 +version: 2.0.2 environment: sdk: ^3.3.0 diff --git a/packages/web_benchmarks/testing/test_app/lib/benchmarks/runner_simple.dart b/packages/web_benchmarks/testing/test_app/lib/benchmarks/runner_simple.dart index 7b6c6ae694db4..c233ebe618738 100644 --- a/packages/web_benchmarks/testing/test_app/lib/benchmarks/runner_simple.dart +++ b/packages/web_benchmarks/testing/test_app/lib/benchmarks/runner_simple.dart @@ -2,6 +2,7 @@ // 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:web_benchmarks/client.dart'; @@ -12,7 +13,11 @@ class SimpleRecorder extends AppRecorder { @override Future automate() async { - // Do nothing. + // Record whether we are in wasm mode or not. Ideally, we'd have a more + // first-class way to add metadata like this, but this will work for us to + // pass information about the environment back to the server for the + // purposes of our own tests. + profile.extraData['isWasm'] = kIsWasm ? 1 : 0; } } diff --git a/packages/web_benchmarks/testing/web_benchmarks_test.dart b/packages/web_benchmarks/testing/web_benchmarks_test.dart index 547ddee4f7fcf..3cdefc671d70c 100644 --- a/packages/web_benchmarks/testing/web_benchmarks_test.dart +++ b/packages/web_benchmarks/testing/web_benchmarks_test.dart @@ -19,27 +19,45 @@ Future main() async { }, timeout: Timeout.none); test('Can run a web benchmark with an alternate initial page', () async { - await _runBenchmarks( + final BenchmarkResults results = await _runBenchmarks( benchmarkNames: ['simple'], entryPoint: 'lib/benchmarks/runner_simple.dart', initialPage: 'index.html#about', ); + + // The simple runner just puts an `isWasm` metric in there so we can make + // sure that we're running in the right environment. + final List? scores = results.scores['simple']; + expect(scores, isNotNull); + + final BenchmarkScore isWasmScore = + scores!.firstWhere((BenchmarkScore score) => score.metric == 'isWasm'); + expect(isWasmScore.value, 0); }, timeout: Timeout.none); test( 'Can run a web benchmark with wasm', () async { - await _runBenchmarks( + final BenchmarkResults results = await _runBenchmarks( benchmarkNames: ['simple'], entryPoint: 'lib/benchmarks/runner_simple.dart', compilationOptions: const CompilationOptions.wasm(), ); + + // The simple runner just puts an `isWasm` metric in there so we can make + // sure that we're running in the right environment. + final List? scores = results.scores['simple']; + expect(scores, isNotNull); + + final BenchmarkScore isWasmScore = scores! + .firstWhere((BenchmarkScore score) => score.metric == 'isWasm'); + expect(isWasmScore.value, 1); }, timeout: Timeout.none, ); } -Future _runBenchmarks({ +Future _runBenchmarks({ required List benchmarkNames, required String entryPoint, String initialPage = defaultInitialPage, @@ -53,12 +71,17 @@ Future _runBenchmarks({ compilationOptions: compilationOptions, ); + // The skwasm renderer doesn't have preroll or apply frame steps in its rendering. + final List expectedMetrics = compilationOptions.useWasm + ? ['drawFrameDuration'] + : [ + 'preroll_frame', + 'apply_frame', + 'drawFrameDuration', + ]; + for (final String benchmarkName in benchmarkNames) { - for (final String metricName in [ - 'preroll_frame', - 'apply_frame', - 'drawFrameDuration', - ]) { + for (final String metricName in expectedMetrics) { for (final String valueName in [ 'average', 'outlierAverage', @@ -83,4 +106,5 @@ Future _runBenchmarks({ const JsonEncoder.withIndent(' ').convert(taskResult.toJson()), isA(), ); + return taskResult; }