0

Support interrupted animation smoothness reporting

Allow TrayItemView "show" animation smoothness metric to be recorded
when the "show" animation interrupts an on-going "hide" animation, and
vice versa.

Bug: b:294890121
Change-Id: Id8b537430baafd25c17cc7a70a8c5bf926cf4f70
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4766287
Reviewed-by: Andre Le <leandre@chromium.org>
Commit-Queue: Elliot Tuck <etuck@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1184818}
This commit is contained in:
Elliot Tuck
2023-08-17 18:28:49 +00:00
committed by Chromium LUCI CQ
parent 787b25420b
commit 18754a5b6a
3 changed files with 159 additions and 8 deletions

@ -200,14 +200,14 @@ void TrayItemView::PerformVisibilityAnimation(bool visible) {
if (target_visible_) {
SetupThroughputTrackerForAnimationSmoothness(
GetWidget(), throughput_tracker_,
GetWidget(), show_throughput_tracker_,
kShowAnimationSmoothnessHistogramName);
animation_->SetSlideDuration(base::Milliseconds(400));
animation_->Show();
AnimationProgressed(animation_.get());
} else {
SetupThroughputTrackerForAnimationSmoothness(
GetWidget(), throughput_tracker_,
GetWidget(), hide_throughput_tracker_,
kHideAnimationSmoothnessHistogramName);
animation_->SetSlideDuration(base::Milliseconds(100));
animation_->Hide();
@ -303,10 +303,16 @@ void TrayItemView::AnimationEnded(const gfx::Animation* animation) {
views::View::SetVisible(target_visible_);
layer()->SetOpacity(target_visible_ ? 1.0 : 0.0);
if (throughput_tracker_) {
// Reset `throughput_tracker_` to reset animation metrics recording.
throughput_tracker_->Stop();
throughput_tracker_.reset();
if (show_throughput_tracker_) {
// Reset `show_throughput_tracker_` to reset animation metrics recording.
show_throughput_tracker_->Stop();
show_throughput_tracker_.reset();
}
if (hide_throughput_tracker_) {
// Reset `hide_throughput_tracker_` to reset animation metrics recording.
hide_throughput_tracker_->Stop();
hide_throughput_tracker_.reset();
}
if (animation_idle_closure_) {

@ -213,8 +213,11 @@ class ASH_EXPORT TrayItemView : public views::View,
raw_ptr<views::ImageView, DanglingUntriaged | ExperimentalAsh> image_view_ =
nullptr;
// Measure animation smoothness metrics for `animation_`.
absl::optional<ui::ThroughputTracker> throughput_tracker_;
// Measures animation smoothness metrics for "show" animation.
absl::optional<ui::ThroughputTracker> show_throughput_tracker_;
// Measures animation smoothness metrics for "hide" animation.
absl::optional<ui::ThroughputTracker> hide_throughput_tracker_;
// Number of active requests to disable animation.
size_t disable_animation_count_ = 0u;

@ -8,15 +8,61 @@
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/test_utils.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
constexpr char kShowAnimationSmoothnessHistogramName[] =
"Ash.StatusArea.TrayItemView.Show";
constexpr char kHideAnimationSmoothnessHistogramName[] =
"Ash.StatusArea.TrayItemView.Hide";
} // namespace
// A class that can be used to wait for the given `TrayItemView`'s visibility
// animation to finish (`TrayItemView` does not currently use layer animations,
// so we can't just use `ui::LayerAnimationStoppedWaiter`).
class TrayItemViewAnimationWaiter {
public:
explicit TrayItemViewAnimationWaiter(TrayItemView* tray_item)
: tray_item_(tray_item) {}
TrayItemViewAnimationWaiter(const TrayItemViewAnimationWaiter&) = delete;
TrayItemViewAnimationWaiter& operator=(const TrayItemViewAnimationWaiter&) =
delete;
~TrayItemViewAnimationWaiter() = default;
// Waits for `tray_item_`'s visibility animation to finish, or no-op if it is
// not currently animating.
void Wait() {
if (tray_item_->IsAnimating()) {
tray_item_->SetAnimationIdleClosureForTest(base::BindOnce(
&TrayItemViewAnimationWaiter::OnTrayItemAnimationFinished,
weak_ptr_factory_.GetWeakPtr()));
run_loop_.Run();
}
}
private:
// Called when `tray_item_`'s visibility animation finishes.
void OnTrayItemAnimationFinished() { run_loop_.Quit(); }
// The tray item whose animation is being waited for. Owned by the views
// hierarchy.
raw_ptr<TrayItemView, ExperimentalAsh> tray_item_ = nullptr;
base::RunLoop run_loop_;
base::WeakPtrFactory<TrayItemViewAnimationWaiter> weak_ptr_factory_{this};
};
class TestTrayItemView : public TrayItemView {
public:
explicit TestTrayItemView(Shelf* shelf) : TrayItemView(shelf) {}
@ -52,6 +98,18 @@ class TrayItemViewTest : public AshTestBase {
AshTestBase::TearDown();
}
// Helper function that waits not only for `tray_item()`'s animation to finish
// but also for any animation throughput data to be passed from cc to ui.
void WaitForAnimation() {
TrayItemViewAnimationWaiter waiter(tray_item());
waiter.Wait();
// Ensure there is one more frame presented after animation finishes to
// allow animation throughput data to be passed from cc to ui.
EXPECT_TRUE(ui::WaitForNextFrameToBePresented(
tray_item()->GetWidget()->GetCompositor()));
}
views::Widget* widget() { return widget_.get(); }
TrayItemView* tray_item() { return tray_item_; }
@ -163,4 +221,88 @@ TEST_F(TrayItemViewTest, LargeImageIcon) {
EXPECT_EQ(tray_item()->CalculatePreferredSize(), kLargeImageSize);
}
// Tests that a smoothness metric is recorded for the "show" animation.
TEST_F(TrayItemViewTest, SmoothnessMetricRecordedForShowAnimation) {
// Start with the tray item hidden. Note that animations still complete
// immediately in this part of the test, so no smoothness metrics are emitted.
tray_item()->SetVisible(false);
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(kHideAnimationSmoothnessHistogramName, 0);
// Set the animation duration scale to a non-zero value for the rest of the
// test. Smoothness metrics should be emitted from this point onward.
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Start the tray item's "show" animation and wait for it to finish.
tray_item()->SetVisible(true);
WaitForAnimation();
// Verify that the "show" animation's smoothness metric was recorded.
histogram_tester.ExpectTotalCount(kShowAnimationSmoothnessHistogramName, 1);
}
// Tests that a smoothness metric is recorded for the "hide" animation.
TEST_F(TrayItemViewTest, SmoothnessMetricRecordedForHideAnimation) {
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(kHideAnimationSmoothnessHistogramName, 0);
// Set the animation duration scale to a non-zero value for the rest of the
// test. Smoothness metrics should be emitted from this point onward.
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Start the tray item's "hide" animation and wait for it to finish.
tray_item()->SetVisible(false);
WaitForAnimation();
// Verify that the "hide" animation's smoothness metric was recorded.
histogram_tester.ExpectTotalCount(kHideAnimationSmoothnessHistogramName, 1);
}
// Tests that the smoothness metric for the "hide" animation is still recorded
// even when the "hide" animation interrupts the "show" animation.
TEST_F(TrayItemViewTest, HideSmoothnessMetricRecordedWhenHideInterruptsShow) {
// Start with the tray item hidden. Note that animations still complete
// immediately in this part of the test, so no smoothness metrics are emitted.
tray_item()->SetVisible(false);
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(kHideAnimationSmoothnessHistogramName, 0);
// Set the animation duration scale to a non-zero value for the rest of the
// test. Smoothness metrics should be emitted from this point onward.
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Start the tray item's "show" animation, but interrupt it with the "hide"
// animation. Wait for the "hide" animation to complete.
tray_item()->SetVisible(true);
tray_item()->SetVisible(false);
WaitForAnimation();
// Verify that the "hide" animation's smoothness metric was recorded.
histogram_tester.ExpectTotalCount(kHideAnimationSmoothnessHistogramName, 1);
}
// Tests that the smoothness metric for the "show" animation is still recorded
// even when the "show" animation interrupts the "hide" animation.
TEST_F(TrayItemViewTest, ShowSmoothnessMetricRecordedWhenShowInterruptsHide) {
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(kHideAnimationSmoothnessHistogramName, 0);
// Set the animation duration scale to a non-zero value for the rest of the
// test. Smoothness metrics should be emitted from this point onward.
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Start the tray item's "hide" animation, but interrupt it with the "show"
// animation. Wait for the "show" animation to complete.
tray_item()->SetVisible(false);
tray_item()->SetVisible(true);
WaitForAnimation();
// Verify that the "show" animation's smoothness metric was recorded.
histogram_tester.ExpectTotalCount(kShowAnimationSmoothnessHistogramName, 1);
}
} // namespace ash