| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/compositor/animation_throughput_reporter.h" |
| |
| #include <memory> |
| |
| #include "base/test/bind.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "cc/metrics/frame_sequence_metrics.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_animation_sequence.h" |
| #include "ui/compositor/layer_animator.h" |
| #include "ui/compositor/scoped_layer_animation_settings.h" |
| #include "ui/compositor/test/animation_throughput_reporter_test_base.h" |
| #include "ui/compositor/test/throughput_report_checker.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| namespace ui { |
| |
| using AnimationThroughputReporterTest = AnimationThroughputReporterTestBase; |
| |
| // Tests animation throughput collection with implicit animation scenario. |
| TEST_F(AnimationThroughputReporterTest, ImplicitAnimation) { |
| Layer layer; |
| layer.SetOpacity(0.5f); |
| root_layer()->Add(&layer); |
| |
| ThroughputReportChecker checker(this); |
| { |
| LayerAnimator* animator = layer.GetAnimator(); |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(48)); |
| layer.SetOpacity(1.0f); |
| } |
| // The animation starts in next frame (16ms) and ends 48 ms later. |
| EXPECT_TRUE(checker.WaitUntilReported()); |
| } |
| |
| // Tests animation throughput collection with implicit animation setup before |
| // Layer is attached to a compositor. |
| TEST_F(AnimationThroughputReporterTest, ImplicitAnimationLateAttach) { |
| Layer layer; |
| layer.SetOpacity(0.5f); |
| |
| ThroughputReportChecker checker(this); |
| { |
| LayerAnimator* animator = layer.GetAnimator(); |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(48)); |
| layer.SetOpacity(1.0f); |
| } |
| |
| // Attach to root after animation setup. |
| root_layer()->Add(&layer); |
| EXPECT_TRUE(checker.WaitUntilReported()); |
| } |
| |
| // Tests animation throughput collection with explicitly created animation |
| // sequence scenario. |
| TEST_F(AnimationThroughputReporterTest, ExplicitAnimation) { |
| Layer layer; |
| layer.SetOpacity(0.5f); |
| root_layer()->Add(&layer); |
| |
| ThroughputReportChecker checker(this); |
| LayerAnimator* animator = layer.GetAnimator(); |
| AnimationThroughputReporter reporter(animator, checker.repeating_callback()); |
| |
| animator->ScheduleAnimation( |
| new LayerAnimationSequence(LayerAnimationElement::CreateOpacityElement( |
| 1.0f, base::Milliseconds(48)))); |
| |
| EXPECT_TRUE(checker.WaitUntilReported()); |
| } |
| |
| // Tests animation throughput collection for a persisted animator of a Layer. |
| TEST_F(AnimationThroughputReporterTest, PersistedAnimation) { |
| auto layer = std::make_unique<Layer>(); |
| layer->SetOpacity(0.5f); |
| root_layer()->Add(layer.get()); |
| |
| // Set a persisted animator to |layer|. |
| LayerAnimator* animator = new LayerAnimator(base::Milliseconds(48)); |
| layer->SetAnimator(animator); |
| |
| // |reporter| keeps reporting as long as it is alive. |
| ThroughputReportChecker checker(this); |
| AnimationThroughputReporter reporter(animator, checker.repeating_callback()); |
| |
| // Report data for animation of opacity goes to 1. |
| layer->SetOpacity(1.0f); |
| EXPECT_TRUE(checker.WaitUntilReported()); |
| |
| // Report data for animation of opacity goes to 0.5. |
| checker.reset(); |
| layer->SetOpacity(0.5f); |
| EXPECT_TRUE(checker.WaitUntilReported()); |
| } |
| |
| // Tests animation throughput not reported when animation is aborted. |
| TEST_F(AnimationThroughputReporterTest, AbortedAnimation) { |
| auto layer = std::make_unique<Layer>(); |
| layer->SetOpacity(0.5f); |
| root_layer()->Add(layer.get()); |
| |
| ThroughputReportChecker checker(this, /*fail_if_reported=*/true); |
| |
| // Reporter started monitoring animation, then deleted, which should be |
| // reported when the animation ends. |
| { |
| LayerAnimator* animator = layer->GetAnimator(); |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(48)); |
| layer->SetOpacity(1.0f); |
| } |
| |
| // Delete |layer| to abort on-going animations. |
| layer.reset(); |
| |
| // Wait a bit to ensure that report does not happen. |
| Advance(base::Milliseconds(100)); |
| |
| // TODO(crbug.com/40161328): Test the scenario where the report exists when |
| // the layer is removed. |
| } |
| |
| // Tests no report and no leak when underlying layer is gone before reporter. |
| TEST_F(AnimationThroughputReporterTest, LayerDestroyedBeforeReporter) { |
| auto layer = std::make_unique<Layer>(); |
| layer->SetOpacity(0.5f); |
| root_layer()->Add(layer.get()); |
| |
| ThroughputReportChecker checker(this, /*fail_if_reported=*/true); |
| LayerAnimator* animator = layer->GetAnimator(); |
| AnimationThroughputReporter reporter(animator, checker.repeating_callback()); |
| { |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(48)); |
| layer->SetOpacity(1.0f); |
| } |
| |
| // Delete |layer| to before the reporter. |
| layer.reset(); |
| |
| // Wait a bit to ensure that report does not happen. |
| Advance(base::Milliseconds(100)); |
| } |
| |
| // Tests animation throughput not reported when detached from timeline. |
| TEST_F(AnimationThroughputReporterTest, NoReportOnDetach) { |
| auto layer = std::make_unique<Layer>(); |
| layer->SetOpacity(0.5f); |
| root_layer()->Add(layer.get()); |
| |
| ThroughputReportChecker checker(this, /*fail_if_reported=*/true); |
| { |
| LayerAnimator* animator = layer->GetAnimator(); |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(48)); |
| layer->SetOpacity(1.0f); |
| } |
| |
| // Detach from the root and attach to a root. |
| root_layer()->Remove(layer.get()); |
| root_layer()->Add(layer.get()); |
| |
| // Wait a bit to ensure that report does not happen. |
| Advance(base::Milliseconds(100)); |
| } |
| |
| // Tests animation throughput not reported and no leak when animation is stopped |
| // without being attached to a root. |
| TEST_F(AnimationThroughputReporterTest, EndDetachedNoReportNoLeak) { |
| auto layer = std::make_unique<Layer>(); |
| layer->SetOpacity(0.5f); |
| |
| ThroughputReportChecker checker(this, /*fail_if_reported=*/true); |
| LayerAnimator* animator = layer->GetAnimator(); |
| // Schedule an animation without being attached to a root. |
| { |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(50)); |
| layer->SetOpacity(1.0f); |
| } |
| |
| // End the animation without being attached to a root. |
| animator->StopAnimating(); |
| |
| // Wait a bit to ensure that report does not happen. |
| Advance(base::Milliseconds(100)); |
| |
| // AnimationTracker in |reporter| should not leak in asan. |
| } |
| |
| // Tests animation throughput are reported if there was a previous animation |
| // preempted under IMMEDIATELY_ANIMATE_TO_NEW_TARGET strategy. |
| TEST_F(AnimationThroughputReporterTest, ReportForAnimateToNewTarget) { |
| auto layer = std::make_unique<Layer>(); |
| layer->SetOpacity(0.f); |
| layer->SetBounds(gfx::Rect(0, 0, 1, 2)); |
| root_layer()->Add(layer.get()); |
| |
| ThroughputReportChecker checker(this, /*fail_if_reported=*/true); |
| LayerAnimator* animator = layer->GetAnimator(); |
| // Schedule an animation that will be preempted. No report should happen. |
| { |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(50)); |
| layer->SetOpacity(0.5f); |
| layer->SetBounds(gfx::Rect(0, 0, 3, 4)); |
| } |
| |
| // Animate to new target. Report should happen. |
| ThroughputReportChecker checker2(this); |
| { |
| AnimationThroughputReporter reporter(animator, |
| checker2.repeating_callback()); |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetPreemptionStrategy( |
| LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| settings.SetTransitionDuration(base::Milliseconds(48)); |
| layer->SetOpacity(1.0f); |
| layer->SetBounds(gfx::Rect(0, 0, 5, 6)); |
| } |
| EXPECT_TRUE(checker2.WaitUntilReported()); |
| } |
| |
| // Tests AnimationThroughputReporter does not leak its AnimationTracker when |
| // there are existing animations but no new animation sequence starts after it |
| // is created. |
| TEST_F(AnimationThroughputReporterTest, NoLeakWithNoAnimationStart) { |
| auto layer = std::make_unique<Layer>(); |
| layer->SetOpacity(0.5f); |
| root_layer()->Add(layer.get()); |
| |
| LayerAnimator* animator = layer->GetAnimator(); |
| |
| // Create an existing animation. |
| { |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetTransitionDuration(base::Milliseconds(50)); |
| layer->SetOpacity(0.0f); |
| } |
| |
| // Create the reporter with the existing animation. |
| ThroughputReportChecker checker(this, /*fail_if_reported=*/true); |
| { |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| } |
| |
| // Wait a bit to ensure to let the existing animation finish. |
| // There should be no report and no leak. |
| Advance(base::Milliseconds(100)); |
| } |
| |
| // Tests smoothness is not reported if the animation will not run. |
| TEST_F(AnimationThroughputReporterTest, NoReportForNoRunAnimations) { |
| Layer layer; |
| root_layer()->Add(&layer); |
| |
| ThroughputReportChecker checker(this, /*fail_if_reported=*/true); |
| { |
| LayerAnimator* animator = layer.GetAnimator(); |
| AnimationThroughputReporter reporter(animator, |
| checker.repeating_callback()); |
| |
| // Simulate views::AnimationBuilder to create an animation that will not |
| // run. |
| ScopedLayerAnimationSettings settings(animator); |
| settings.SetPreemptionStrategy( |
| ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| std::vector<ui::LayerAnimationSequence*> sequences; |
| sequences.push_back(new LayerAnimationSequence( |
| LayerAnimationElement::CreateOpacityElement(1.0f, base::TimeDelta()))); |
| animator->StartTogether(std::move(sequences)); |
| } |
| |
| // Wait a bit to ensure that report does not happen. |
| Advance(base::Milliseconds(100)); |
| } |
| |
| } // namespace ui |