// Copyright 2017 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. #ifndef CC_TREES_IMAGE_ANIMATION_CONTROLLER_H_ #define CC_TREES_IMAGE_ANIMATION_CONTROLLER_H_ #include "base/cancelable_callback.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/time/time.h" #include "cc/cc_export.h" #include "cc/paint/discardable_image_map.h" #include "cc/paint/image_id.h" #include "cc/paint/paint_image.h" #include "cc/paint/paint_image_generator.h" #include "cc/tiles/tile_priority.h" namespace cc { class PaintImage; // ImageAnimationController is responsible for tracking state for ticking image // animations in the compositor. // // 1) It receives the updated metadata for these images from new recordings // received from the client using UpdateAnimatedImage. The controller tracks // the frame index of an image used on a tree, and advances the animation to // the desired frame each time a new sync tree is created. // // 2) An AnimationDriver can register itself for deciding whether the // controller animates an image. The animation is paused if there are no // registered drivers interested in animating it. // // 3) An animation is only advanced on the sync tree, which is requested to be // created using the |invalidation_callback|. This effectively means that // the frame of the image used remains consistent throughout the lifetime of // a tree, guaranteeing that the image update is atomic. class CC_EXPORT ImageAnimationController { public: // AnimationDrivers are clients interested in driving image animations. An // animation is ticked if there is at least one driver registered for which // ShouldAnimate returns true. Once // no drivers are registered for an image, or none of the registered drivers // want us to animate, the animation is no longer ticked. class CC_EXPORT AnimationDriver { public: virtual ~AnimationDriver() {} // Returns true if the image should be animated. virtual bool ShouldAnimate(PaintImage::Id paint_image_id) const = 0; }; // |invalidation_callback| is the callback to trigger an invalidation and // create a sync tree for advancing an image animation. The controller is // guaranteed to receive a call to AnimateForSyncTree when the sync tree is // created. // |task_runner| is the thread on which the controller is used. The // invalidation_callback can only be run on this thread. // |enable_image_animation_resync| specifies whether the animation can be // reset to the beginning to avoid skipping many frames. ImageAnimationController(base::SingleThreadTaskRunner* task_runner, base::RepeatingClosure invalidation_callback, bool enable_image_animation_resync); ~ImageAnimationController(); // Called to update the state for a PaintImage received in a new recording. void UpdateAnimatedImage( const DiscardableImageMap::AnimatedImageMetadata& data); // Registers/Unregisters an animation driver interested in animating this // image. // Note that the state for this image must have been populated to the // controller using UpdatePaintImage prior to registering any drivers. void RegisterAnimationDriver(PaintImage::Id paint_image_id, AnimationDriver* driver); void UnregisterAnimationDriver(PaintImage::Id paint_image_id, AnimationDriver* driver); // Called to advance the animations to the frame to be used on the sync tree. // This should be called only once for a sync tree and must be followed with // a call to DidActivate when this tree is activated. // Returns the set of images that were animated and should be invalidated on // this sync tree. const PaintImageIdFlatSet& AnimateForSyncTree(base::TimeTicks now); // Called whenever the ShouldAnimate response for a driver could have changed. // For instance on a change in the visibility of the image, we would pause // off-screen animations. // This is called after every DrawProperties update and commit. void UpdateStateFromDrivers(base::TimeTicks now); // Called when the sync tree was activated and the animations' associated // state should be pushed to the active tree. void DidActivate(); // Returns the frame index to use for the given PaintImage and tree. size_t GetFrameIndexForImage(PaintImage::Id paint_image_id, WhichTree tree) const; void set_did_navigate() { did_navigate_ = true; }; const base::flat_set& GetDriversForTesting( PaintImage::Id paint_image_id) const; size_t GetLastNumOfFramesSkippedForTesting( PaintImage::Id paint_image_id) const; size_t animation_state_map_size_for_testing() { return animation_state_map_.size(); } private: class AnimationState { public: AnimationState(); AnimationState(AnimationState&& other); AnimationState& operator=(AnimationState&& other); ~AnimationState(); bool ShouldAnimate() const; bool AdvanceFrame(base::TimeTicks now, bool enable_image_animation_resync); void UpdateMetadata(const DiscardableImageMap::AnimatedImageMetadata& data); void PushPendingToActive(); void AddDriver(AnimationDriver* driver); void RemoveDriver(AnimationDriver* driver); void UpdateStateFromDrivers(); bool has_drivers() const { return !drivers_.empty(); } size_t pending_index() const { return pending_index_; } size_t active_index() const { return active_index_; } base::TimeTicks next_desired_frame_time() const { return next_desired_frame_time_; } const base::flat_set& drivers_for_testing() const { return drivers_; } size_t last_num_frames_skipped_for_testing() const { return last_num_frames_skipped_; } private: void ResetAnimation(); size_t NextFrameIndex() const; bool is_complete() const { return completion_state_ == PaintImage::CompletionState::DONE; } PaintImage::Id paint_image_id_ = PaintImage::kInvalidId; // The frame metadata received from the most updated recording with this // PaintImage. std::vector frames_; // The number of animation loops requested for this image. For a value > 0, // this number represents the exact number of iterations requested. A few // special cases are represented using constants defined in // cc/paint/image_animation_count.h int requested_repetitions_ = kAnimationNone; // The number of loops the animation has finished so far. int repetitions_completed_ = 0; // A set of drivers interested in animating this image. base::flat_set drivers_; // The index being used on the active tree, if a recording with this image // is still present. size_t active_index_ = PaintImage::kDefaultFrameIndex; // The index being displayed on the pending tree. size_t pending_index_ = PaintImage::kDefaultFrameIndex; // The time at which we would like to display the next frame. This can be in // the past, for instance, if we pause the animation from the image becoming // invisible. base::TimeTicks next_desired_frame_time_; // Set if there is at least one driver interested in animating this image, // cached from the last update. bool should_animate_from_drivers_ = false; // Set if the animation has been started. bool animation_started_ = false; // The last synchronized sequence id for resetting this animation. PaintImage::AnimationSequenceId reset_animation_sequence_id_ = 0; // Whether the image is known to be completely loaded in the most recent // recording received. PaintImage::CompletionState completion_state_ = PaintImage::CompletionState::PARTIALLY_DONE; // The number of frames skipped during catch-up the last time this animation // was advanced. size_t last_num_frames_skipped_ = 0u; DISALLOW_COPY_AND_ASSIGN(AnimationState); }; class DelayedNotifier { public: DelayedNotifier(base::SingleThreadTaskRunner* task_runner, base::RepeatingClosure closure); ~DelayedNotifier(); void Schedule(base::TimeTicks now, base::TimeTicks notification_time); void Cancel(); void WillAnimate(); private: void Notify(); base::SingleThreadTaskRunner* task_runner_; base::RepeatingClosure closure_; // Set if a notification is currently pending. base::Optional pending_notification_time_; // Set if the notification was dispatched and the resulting animation on the // next sync tree is pending. bool animation_pending_ = false; base::WeakPtrFactory weak_factory_; }; // The AnimationState for images is persisted until they are cleared on // navigation. This is because while an image might not be painted anymore, if // it moves out of the interest rect for instance, the state retained is // necessary to resume the animation. // TODO(khushalsagar): Implement clearing of state on navigations. using AnimationStateMap = base::flat_map; AnimationStateMap animation_state_map_; // The set of animations with registered drivers. PaintImageIdFlatSet registered_animations_; // The set of images that were animated and invalidated on the last sync tree. PaintImageIdFlatSet images_animated_on_sync_tree_; DelayedNotifier notifier_; const bool enable_image_animation_resync_; bool did_navigate_ = false; }; } // namespace cc #endif // CC_TREES_IMAGE_ANIMATION_CONTROLLER_H_