blob: 893a539864d27462720e2b338927499174e1a5a8 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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 <utility>
#include "base/bind.h"
#include "base/check.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/scoped_observation.h"
#include "cc/animation/animation.h"
#include "ui/compositor/callback_layer_animation_observer.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_delegate.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/layer_observer.h"
#include "ui/compositor/throughput_tracker.h"
namespace ui {
// AnimationTracker tracks the layer animations that are created during the
// lifetime of its owner AnimationThroughputReporter.
//
// Lifetime of this tracker class is a bit complicated. If there are animations
// to track (i.e. HasAnimationsToTrack() returns true) when the owner reporter
// is going away, it needs to have the same lifetime of the animations to track
// the performance. In such case, the owner reporter would drop the ownership
// and set set_should_delete() to let the tracker manages its own lifetime
// based on LayerDetroyed and LayerAnimationObserver signals. On the other hand,
// if there are no animations to track, the tracker is released with its owner
// reporter.
class AnimationThroughputReporter::AnimationTracker
: public CallbackLayerAnimationObserver,
public LayerObserver {
public:
AnimationTracker(Layer* layer, ReportCallback report_callback)
: CallbackLayerAnimationObserver(
base::BindRepeating(&AnimationTracker::OnAnimationEnded,
base::Unretained(this))),
animator_(layer->GetAnimator()),
report_callback_(std::move(report_callback)) {
DCHECK(report_callback_);
layer_observation_.Observe(layer);
}
AnimationTracker(const AnimationTracker& other) = delete;
AnimationTracker& operator=(const AnimationTracker& other) = delete;
~AnimationTracker() override {
// No auto delete in the observer callbacks since `this` is being
// destructed.
should_delete_ = false;
// Cancels existing tracking if any.
throughput_tracker_.reset();
// Stops observing animations so that `animator` destruction does not call
// back into half destructed `this` if `this` holds the last reference of
// `animator_`.
StopObserving();
}
// Whether there are/will be animations to track. That is, there is an
// underlying layer and there are attached animation sequences.
bool HasAnimationsToTrack() const {
return layer_observation_.IsObserving() && !attached_sequences().empty();
}
void set_should_delete(bool should_delete) { should_delete_ = should_delete; }
private:
// CallbackLayerAnimationObserver:
void OnAnimatorAttachedToTimeline() override { MaybeStartTracking(); }
void OnAnimatorDetachedFromTimeline() override {
// Gives up tracking when detached from the timeline.
first_animation_group_id_.reset();
if (throughput_tracker_)
throughput_tracker_.reset();
// OnAnimationEnded would not happen after detached from the timeline.
// So do the clean up here.
if (should_delete_)
delete this;
}
void OnLayerAnimationStarted(LayerAnimationSequence* sequence) override {
CallbackLayerAnimationObserver::OnLayerAnimationStarted(sequence);
if (!first_animation_group_id_.has_value()) {
first_animation_group_id_ = sequence->animation_group_id();
MaybeStartTracking();
}
// Make sure SetActive() is called so that OnAnimationEnded callback will be
// invoked when all attached layer animation sequences finish.
if (!active())
SetActive();
}
void OnLayerAnimationAborted(LayerAnimationSequence* sequence) override {
// Check whether the aborted animation sequence is among the relevant ones
// (started while the tracker is alive). This is done by checking the
// animation_group_id() and assuming the id is monotonic increasing.
if (first_animation_group_id_.has_value() &&
first_animation_group_id_.value() <= sequence->animation_group_id()) {
started_animations_aborted_ = true;
}
// Note the following call could delete |this|.
CallbackLayerAnimationObserver::OnLayerAnimationAborted(sequence);
}
// LayerObserver:
void LayerDestroyed(Layer* layer) override {
DCHECK(layer_observation_.IsObservingSource(layer));
layer_observation_.Reset();
// No more tracking needed when underlying layer is gone.
if (should_delete_)
delete this;
}
void MaybeStartTracking() {
// No tracking if no layer animation sequence is started.
if (!first_animation_group_id_.has_value())
return;
// No tracking if |animator_| is not attached to a timeline. Layer animation
// sequence would not tick without a timeline.
if (!AnimationThroughputReporter::IsAnimatorAttachedToTimeline(
animator_.get())) {
return;
}
ui::Compositor* compositor =
AnimationThroughputReporter::GetCompositor(animator_.get());
throughput_tracker_ = compositor->RequestNewThroughputTracker();
throughput_tracker_->Start(report_callback_);
}
// Invoked when all animation sequences finish.
bool OnAnimationEnded(const CallbackLayerAnimationObserver& self) {
// |throughput_tracker| could reset when detached from animation timeline.
if (throughput_tracker_) {
if (started_animations_aborted_)
throughput_tracker_->Cancel();
else
throughput_tracker_->Stop();
}
first_animation_group_id_.reset();
started_animations_aborted_ = false;
return should_delete_;
}
// Whether this class should delete itself on animation ended.
bool should_delete_ = false;
base::ScopedObservation<Layer, LayerObserver> layer_observation_{this};
scoped_refptr<LayerAnimator> animator_;
base::Optional<ThroughputTracker> throughput_tracker_;
base::Optional<int> first_animation_group_id_;
bool started_animations_aborted_ = false;
AnimationThroughputReporter::ReportCallback report_callback_;
};
AnimationThroughputReporter::AnimationThroughputReporter(
LayerAnimator* animator,
ReportCallback report_callback)
: animator_(animator),
animation_tracker_(
std::make_unique<AnimationTracker>(animator_->delegate()->GetLayer(),
std::move(report_callback))) {
animator_->AddObserver(animation_tracker_.get());
}
AnimationThroughputReporter::~AnimationThroughputReporter() {
// Directly remove |animation_tracker_| from |LayerAnimator::observers_|
// rather than calling LayerAnimator::RemoveObserver(), to avoid removing it
// from the scheduled animation sequences.
animator_->observers_.RemoveObserver(animation_tracker_.get());
// |animation_tracker_| deletes itself when its tracked animations finish.
if (animation_tracker_->HasAnimationsToTrack())
animation_tracker_.release()->set_should_delete(true);
}
// static
Compositor* AnimationThroughputReporter::GetCompositor(
LayerAnimator* animator) {
return animator->delegate()->GetLayer()->GetCompositor();
}
// static
bool AnimationThroughputReporter::IsAnimatorAttachedToTimeline(
LayerAnimator* animator) {
return animator->animation_->animation_timeline();
}
} // namespace ui