[framework] restore old zoom page transition for benchmarking. (#133346)

In the past I switched the implementation of the zoom page transition because the performance of the old transition was terrible, but I'm hopeful that with Impeller we'll be able to identify and fix the issues that made it so slow. In order to evaluate this though, we need to be able to opt into the old transition for benchmarks on CI.

https://github.com/flutter/flutter/issues/129742
https://github.com/flutter/flutter/issues/121325
This commit is contained in:
Jonah Williams 2023-10-10 15:27:07 -07:00 committed by GitHub
parent daa52b2501
commit a106b81cc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 232 additions and 0 deletions

View File

@ -2453,6 +2453,17 @@ targets:
["devicelab", "android", "linux"]
task_name: new_gallery_impeller__transition_perf
# Pixel 7 Pro, Impeller (Vulkan)
- name: Linux_pixel_7pro new_gallery_impeller_old_zoom__transition_perf
recipe: devicelab/devicelab_drone
bringup: true
presubmit: false
timeout: 60
properties:
tags: >
["devicelab", "android", "linux", "pixel", "7pro"]
task_name: new_gallery_impeller_old_zoom__transition_perf
# Pixel 7 Pro, Impeller (Vulkan)
- name: Linux_pixel_7pro new_gallery_impeller__transition_perf
recipe: devicelab/devicelab_drone

View File

@ -149,6 +149,7 @@
/dev/devicelab/bin/tasks/microbenchmarks.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/new_gallery__transition_perf.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/new_gallery_impeller__transition_perf.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/new_gallery_impeller_old_zoom__transition_perf.dart @jonahwilliams @flutter/engine
/dev/devicelab/bin/tasks/new_gallery_opengles_impeller__transition_perf.dart @gaaclarke @flutter/engine
/dev/devicelab/bin/tasks/picture_cache_perf__timeline_summary.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/platform_channel_sample_test.dart @zanderso @flutter/engine

View File

@ -0,0 +1,24 @@
// 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:io';
import 'package:flutter_devicelab/framework/devices.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:flutter_devicelab/tasks/new_gallery.dart';
import 'package:path/path.dart' as path;
Future<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.android;
final Directory galleryParentDir = Directory.systemTemp.createTempSync('flutter_new_gallery_test.');
final Directory galleryDir = Directory(path.join(galleryParentDir.path, 'gallery'));
try {
await task(NewGalleryPerfTest(galleryDir, enableImpeller: true, dartDefine: 'flutter.benchmarks.force_disable_snapshot=true').run);
} finally {
rmTree(galleryParentDir);
}
}

View File

@ -649,6 +649,11 @@ class ZoomPageTransitionsBuilder extends PageTransitionsBuilder {
/// not be snapshotted.
final bool allowEnterRouteSnapshotting;
// Allows devicelab benchmarks to force disable the snapshotting. This is
// intended to allow us to profile and fix the underlying performance issues
// for the Impeller backend.
static const bool _kProfileForceDisableSnapshotting = bool.fromEnvironment('flutter.benchmarks.force_disable_snapshot');
@override
Widget buildTransitions<T>(
PageRoute<T>? route,
@ -657,6 +662,13 @@ class ZoomPageTransitionsBuilder extends PageTransitionsBuilder {
Animation<double> secondaryAnimation,
Widget? child,
) {
if (_kProfileForceDisableSnapshotting) {
return _ZoomPageTransitionNoCache(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
}
return _ZoomPageTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
@ -1027,3 +1039,187 @@ class _ZoomExitTransitionPainter extends SnapshotPainter {
super.dispose();
}
}
// Zooms and fades a new page in, zooming out the previous page. This transition
// is designed to match the Android Q activity transition.
//
// This was the historical implementation of the cacheless zoom page transition
// that was too slow to run on the Skia backend. This is being benchmarked on
// the Impeller backend so that we can improve performance enough to restore
// the default behavior.
class _ZoomPageTransitionNoCache extends StatelessWidget {
/// Creates a [_ZoomPageTransitionNoCache].
///
/// The [animation] and [secondaryAnimation] argument are required and must
/// not be null.
const _ZoomPageTransitionNoCache({
required this.animation,
required this.secondaryAnimation,
this.child,
});
/// The animation that drives the [child]'s entrance and exit.
///
/// See also:
///
/// * [TransitionRoute.animation], which is the value given to this property
/// when the [_ZoomPageTransition] is used as a page transition.
final Animation<double> animation;
/// The animation that transitions [child] when new content is pushed on top
/// of it.
///
/// See also:
///
/// * [TransitionRoute.secondaryAnimation], which is the value given to this
/// property when the [_ZoomPageTransition] is used as a page transition.
final Animation<double> secondaryAnimation;
/// The widget below this widget in the tree.
///
/// This widget will transition in and out as driven by [animation] and
/// [secondaryAnimation].
final Widget? child;
@override
Widget build(BuildContext context) {
return DualTransitionBuilder(
animation: animation,
forwardBuilder: (
BuildContext context,
Animation<double> animation,
Widget? child,
) {
return _ZoomEnterTransitionNoCache(
animation: animation,
child: child,
);
},
reverseBuilder: (
BuildContext context,
Animation<double> animation,
Widget? child,
) {
return _ZoomExitTransitionNoCache(
animation: animation,
reverse: true,
child: child,
);
},
child: DualTransitionBuilder(
animation: ReverseAnimation(secondaryAnimation),
forwardBuilder: (
BuildContext context,
Animation<double> animation,
Widget? child,
) {
return _ZoomEnterTransitionNoCache(
animation: animation,
reverse: true,
child: child,
);
},
reverseBuilder: (
BuildContext context,
Animation<double> animation,
Widget? child,
) {
return _ZoomExitTransitionNoCache(
animation: animation,
child: child,
);
},
child: child,
),
);
}
}
class _ZoomEnterTransitionNoCache extends StatelessWidget {
const _ZoomEnterTransitionNoCache({
required this.animation,
this.reverse = false,
this.child,
});
final Animation<double> animation;
final Widget? child;
final bool reverse;
@override
Widget build(BuildContext context) {
double opacity = 0;
// The transition's scrim opacity only increases on the forward transition.
// In the reverse transition, the opacity should always be 0.0.
//
// Therefore, we need to only apply the scrim opacity animation when
// the transition is running forwards.
//
// The reason that we check that the animation's status is not `completed`
// instead of checking that it is `forward` is that this allows
// the interrupted reversal of the forward transition to smoothly fade
// the scrim away. This prevents a disjointed removal of the scrim.
if (!reverse && animation.status != AnimationStatus.completed) {
opacity = _ZoomEnterTransitionState._scrimOpacityTween.evaluate(animation)!;
}
final Animation<double> fadeTransition = reverse
? kAlwaysCompleteAnimation
: _ZoomEnterTransitionState._fadeInTransition.animate(animation);
final Animation<double> scaleTransition = (reverse
? _ZoomEnterTransitionState._scaleDownTransition
: _ZoomEnterTransitionState._scaleUpTransition
).animate(animation);
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget? child) {
return ColoredBox(
color: Colors.black.withOpacity(opacity),
child: child,
);
},
child: FadeTransition(
opacity: fadeTransition,
child: ScaleTransition(
scale: scaleTransition,
filterQuality: FilterQuality.none,
child: child,
),
),
);
}
}
class _ZoomExitTransitionNoCache extends StatelessWidget {
const _ZoomExitTransitionNoCache({
required this.animation,
this.reverse = false,
this.child,
});
final Animation<double> animation;
final bool reverse;
final Widget? child;
@override
Widget build(BuildContext context) {
final Animation<double> fadeTransition = reverse
? _ZoomExitTransitionState._fadeOutTransition.animate(animation)
: kAlwaysCompleteAnimation;
final Animation<double> scaleTransition = (reverse
? _ZoomExitTransitionState._scaleDownTransition
: _ZoomExitTransitionState._scaleUpTransition
).animate(animation);
return FadeTransition(
opacity: fadeTransition,
child: ScaleTransition(
scale: scaleTransition,
filterQuality: FilterQuality.none,
child: child,
),
);
}
}