Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 1 | // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "cc/trees/image_animation_controller.h" |
| 6 | |
Gyuyoung Kim | e8366401 | 2017-12-02 00:12:49 | [diff] [blame] | 7 | #include <memory> |
| 8 | |
Sebastien Marchand | 6d0558fd | 2019-01-25 16:49:37 | [diff] [blame] | 9 | #include "base/bind.h" |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 10 | #include "base/run_loop.h" |
| 11 | #include "base/test/gtest_util.h" |
| 12 | #include "base/threading/thread_task_runner_handle.h" |
| 13 | #include "testing/gtest/include/gtest/gtest.h" |
| 14 | |
| 15 | namespace cc { |
| 16 | |
| 17 | class FakeAnimationDriver : public ImageAnimationController::AnimationDriver { |
| 18 | public: |
Chris Watkins | f635329 | 2017-12-04 02:36:05 | [diff] [blame] | 19 | FakeAnimationDriver() = default; |
| 20 | ~FakeAnimationDriver() override = default; |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 21 | |
| 22 | void set_should_animate(bool should_animate) { |
| 23 | should_animate_ = should_animate; |
| 24 | } |
| 25 | |
| 26 | // ImageAnimationController::AnimationDriver implementation. |
| 27 | bool ShouldAnimate(PaintImage::Id paint_image_id) const override { |
| 28 | return should_animate_; |
| 29 | } |
| 30 | |
| 31 | private: |
| 32 | bool should_animate_ = true; |
| 33 | }; |
| 34 | |
| 35 | class DelayTrackingTaskRunner : public base::SingleThreadTaskRunner { |
| 36 | public: |
| 37 | explicit DelayTrackingTaskRunner(base::SingleThreadTaskRunner* task_runner) |
| 38 | : task_runner_(task_runner) {} |
| 39 | |
| 40 | bool PostDelayedTask(const base::Location& from_here, |
| 41 | base::OnceClosure task, |
| 42 | base::TimeDelta delay) override { |
| 43 | last_delay_.emplace(delay); |
| 44 | return task_runner_->PostTask(from_here, std::move(task)); |
| 45 | } |
| 46 | |
| 47 | bool RunsTasksInCurrentSequence() const override { |
| 48 | return task_runner_->RunsTasksInCurrentSequence(); |
| 49 | } |
| 50 | |
| 51 | bool PostNonNestableDelayedTask(const base::Location& from_here, |
| 52 | base::OnceClosure task, |
| 53 | base::TimeDelta delay) override { |
| 54 | last_delay_.emplace(delay); |
| 55 | return task_runner_->PostTask(from_here, std::move(task)); |
| 56 | } |
| 57 | |
| 58 | void VerifyDelay(base::TimeDelta expected) { |
| 59 | DCHECK(last_delay_.has_value()); |
| 60 | EXPECT_EQ(last_delay_.value(), expected); |
| 61 | last_delay_.reset(); |
| 62 | } |
| 63 | |
| 64 | bool has_delay() const { return last_delay_.has_value(); } |
| 65 | |
| 66 | private: |
| 67 | ~DelayTrackingTaskRunner() override = default; |
| 68 | |
| 69 | base::Optional<base::TimeDelta> last_delay_; |
| 70 | base::SingleThreadTaskRunner* task_runner_; |
| 71 | }; |
| 72 | |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 73 | class ImageAnimationControllerTest : public testing::Test, |
| 74 | public ImageAnimationController::Client { |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 75 | public: |
| 76 | void SetUp() override { |
| 77 | task_runner_ = |
| 78 | new DelayTrackingTaskRunner(base::ThreadTaskRunnerHandle::Get().get()); |
Gyuyoung Kim | e8366401 | 2017-12-02 00:12:49 | [diff] [blame] | 79 | controller_ = std::make_unique<ImageAnimationController>( |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 80 | task_runner_.get(), this, GetEnableImageAnimationResync()); |
| 81 | controller_->set_now_callback_for_testing(base::BindRepeating( |
| 82 | &ImageAnimationControllerTest::Now, base::Unretained(this))); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 83 | now_ += base::TimeDelta::FromSeconds(10); |
| 84 | } |
| 85 | |
| 86 | void TearDown() override { controller_.reset(); } |
| 87 | |
| 88 | void LoopOnceNoDelay(PaintImage::Id paint_image_id, |
| 89 | const std::vector<FrameMetadata>& frames, |
| 90 | size_t num_of_frames_to_loop, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 91 | int repetitions_completed, |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 92 | std::vector<base::TimeDelta> expected_delays = {}, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 93 | bool restarting = false) { |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 94 | DCHECK_LE(num_of_frames_to_loop, frames.size()); |
| 95 | |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 96 | if (expected_delays.empty()) { |
| 97 | expected_delays.resize(frames.size()); |
| 98 | for (size_t i = 0; i < frames.size(); ++i) |
| 99 | expected_delays[i] = frames[i].duration; |
| 100 | } |
| 101 | |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 102 | invalidation_count_ = 0; |
| 103 | for (size_t i = 0; i < num_of_frames_to_loop; ++i) { |
| 104 | SCOPED_TRACE(i); |
| 105 | |
| 106 | // Run the pending invalidation. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 107 | RunFrameRequestAndInvalidation(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 108 | |
| 109 | // Animate the image on the sync tree. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 110 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 111 | |
| 112 | // No frames should have been skipped since we add no delay in advancing |
| 113 | // the animation. |
| 114 | EXPECT_EQ( |
| 115 | controller_->GetLastNumOfFramesSkippedForTesting(paint_image_id), 0u); |
| 116 | |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 117 | EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id, |
| 118 | WhichTree::PENDING_TREE), |
| 119 | i); |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 120 | if (i == 0u && !restarting) { |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 121 | // If we are displaying the first frame on the pending tree, then the |
| 122 | // active tree has the first frame as well if this is the first loop, |
| 123 | // otherwise it should be the last frame since we are starting a new |
| 124 | // loop. |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 125 | size_t active_index = 0u; |
| 126 | if (repetitions_completed != 0) |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 127 | active_index = frames.size() - 1; |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 128 | EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id, |
| 129 | WhichTree::ACTIVE_TREE), |
| 130 | active_index); |
| 131 | } else if (i != 0u) { |
| 132 | EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id, |
| 133 | WhichTree::ACTIVE_TREE), |
| 134 | i - 1); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 135 | } |
| 136 | |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 137 | if (i == 0u && repetitions_completed == 0 && !restarting) { |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 138 | // Starting the animation does not perform any invalidation. |
| 139 | EXPECT_EQ(animated_images.size(), 0u); |
| 140 | } else { |
| 141 | EXPECT_EQ(animated_images.size(), 1u); |
| 142 | EXPECT_EQ(animated_images.count(paint_image_id), 1u); |
| 143 | } |
| 144 | |
| 145 | // Animating should schedule an invalidation for the next frame, until we |
| 146 | // reach the last frame. |
| 147 | if (i != num_of_frames_to_loop - 1) |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 148 | task_runner_->VerifyDelay(expected_delays.at(i)); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 149 | |
| 150 | // Activate and advance time to the next frame. |
| 151 | controller_->DidActivate(); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 152 | AdvanceNow(expected_delays.at(i)); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 153 | } |
| 154 | } |
| 155 | |
| 156 | protected: |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 157 | // ImageAnimationController::Client implementation. |
| 158 | void RequestBeginFrameForAnimatedImages() override { begin_frame_count_++; } |
| 159 | void RequestInvalidationForAnimatedImages() override { |
| 160 | invalidation_count_++; |
| 161 | } |
| 162 | |
| 163 | base::TimeTicks Now() { return now_; } |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 164 | |
| 165 | void AdvanceNow(base::TimeDelta delta) { now_ += delta; } |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 166 | viz::BeginFrameArgs BeginFrameArgs(base::TimeTicks now = base::TimeTicks()) { |
| 167 | if (now == base::TimeTicks()) |
| 168 | now = now_; |
| 169 | |
| 170 | return viz::BeginFrameArgs::Create( |
| 171 | BEGINFRAME_FROM_HERE, 1 /* source_id */, 1 /* sequence_number */, |
| 172 | now /* frame_time */, now + interval_ /* deadline */, |
| 173 | interval_ /* interval */, |
| 174 | viz::BeginFrameArgs::BeginFrameArgsType::NORMAL); |
| 175 | } |
| 176 | |
| 177 | void RunFrameRequestAndInvalidation() { |
| 178 | // Frame request. |
| 179 | base::RunLoop().RunUntilIdle(); |
| 180 | EXPECT_EQ(begin_frame_count_, 1); |
| 181 | EXPECT_EQ(invalidation_count_, 0); |
| 182 | begin_frame_count_ = 0; |
| 183 | |
| 184 | // Invalidation request. |
| 185 | controller_->WillBeginImplFrame(BeginFrameArgs()); |
| 186 | EXPECT_EQ(begin_frame_count_, 0); |
| 187 | EXPECT_EQ(invalidation_count_, 1); |
| 188 | invalidation_count_ = 0; |
| 189 | } |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 190 | |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 191 | virtual bool GetEnableImageAnimationResync() const { return true; } |
| 192 | |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 193 | base::TimeTicks now_; |
| 194 | int invalidation_count_ = 0; |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 195 | int begin_frame_count_ = 0; |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 196 | std::unique_ptr<ImageAnimationController> controller_; |
| 197 | scoped_refptr<DelayTrackingTaskRunner> task_runner_; |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 198 | |
| 199 | base::TimeDelta interval_ = base::TimeDelta::FromMilliseconds(1); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 200 | }; |
| 201 | |
| 202 | TEST_F(ImageAnimationControllerTest, AnimationWithDelays) { |
| 203 | std::vector<FrameMetadata> frames = { |
| 204 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)), |
| 205 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 206 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)), |
| 207 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 208 | |
| 209 | DiscardableImageMap::AnimatedImageMetadata data( |
| 210 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 211 | kAnimationLoopInfinite, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 212 | controller_->UpdateAnimatedImage(data); |
| 213 | FakeAnimationDriver driver; |
| 214 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 215 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 216 | |
| 217 | // Display 2 loops in the animation. |
| 218 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0); |
| 219 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1); |
| 220 | |
| 221 | // now_ is set to the time at which the first frame should be displayed for |
| 222 | // the third iteration. Add a delay that causes us to skip the first frame. |
| 223 | base::TimeDelta additional_delay = base::TimeDelta::FromMilliseconds(1); |
| 224 | AdvanceNow(data.frames[0].duration + additional_delay); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 225 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 226 | EXPECT_EQ(animated_images.size(), 1u); |
| 227 | EXPECT_EQ(animated_images.count(data.paint_image_id), 1u); |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 228 | EXPECT_EQ( |
| 229 | controller_->GetLastNumOfFramesSkippedForTesting(data.paint_image_id), |
| 230 | 1u); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 231 | |
| 232 | // The pending tree displays the second frame while the active tree has the |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 233 | // last frame. |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 234 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 235 | WhichTree::PENDING_TREE), |
| 236 | 1u); |
| 237 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 238 | WhichTree::ACTIVE_TREE), |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 239 | 3u); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 240 | |
| 241 | // Invalidation delay is based on the duration of the second frame and the |
| 242 | // delay in creating this sync tree. |
| 243 | task_runner_->VerifyDelay(frames[1].duration - additional_delay); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 244 | |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 245 | // Activate and animate with a delay that causes us to skip another 2 frames. |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 246 | controller_->DidActivate(); |
| 247 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 248 | WhichTree::ACTIVE_TREE), |
| 249 | 1u); |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 250 | AdvanceNow(data.frames[1].duration + data.frames[2].duration + |
| 251 | data.frames[3].duration); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 252 | RunFrameRequestAndInvalidation(); |
| 253 | animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 254 | EXPECT_EQ(animated_images.size(), 1u); |
| 255 | EXPECT_EQ(animated_images.count(data.paint_image_id), 1u); |
Khushal | 48858069 | 2017-10-03 01:02:49 | [diff] [blame] | 256 | EXPECT_EQ( |
| 257 | controller_->GetLastNumOfFramesSkippedForTesting(data.paint_image_id), |
| 258 | 2u); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 259 | |
| 260 | // The pending tree displays the first frame, while the active tree has the |
| 261 | // second frame. |
| 262 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 263 | WhichTree::PENDING_TREE), |
| 264 | 0u); |
| 265 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 266 | WhichTree::ACTIVE_TREE), |
| 267 | 1u); |
| 268 | |
| 269 | // Invalidation delay is based on the duration of the first frame and the |
| 270 | // initial additionaly delay. |
| 271 | task_runner_->VerifyDelay(frames[0].duration - additional_delay); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 272 | |
| 273 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 274 | } |
| 275 | |
| 276 | TEST_F(ImageAnimationControllerTest, DriversControlAnimationTicking) { |
| 277 | std::vector<FrameMetadata> first_image_frames = { |
| 278 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 279 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 280 | DiscardableImageMap::AnimatedImageMetadata first_data( |
| 281 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 282 | first_image_frames, kAnimationLoopOnce, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 283 | controller_->UpdateAnimatedImage(first_data); |
| 284 | FakeAnimationDriver first_driver; |
| 285 | controller_->RegisterAnimationDriver(first_data.paint_image_id, |
| 286 | &first_driver); |
| 287 | |
| 288 | std::vector<FrameMetadata> second_image_frames = { |
| 289 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)), |
| 290 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 291 | DiscardableImageMap::AnimatedImageMetadata second_data( |
| 292 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 293 | second_image_frames, kAnimationLoopOnce, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 294 | controller_->UpdateAnimatedImage(second_data); |
| 295 | FakeAnimationDriver second_driver; |
| 296 | controller_->RegisterAnimationDriver(second_data.paint_image_id, |
| 297 | &second_driver); |
| 298 | |
| 299 | // Disable animating from all drivers, no invalidation request should be made. |
| 300 | first_driver.set_should_animate(false); |
| 301 | second_driver.set_should_animate(false); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 302 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 303 | base::RunLoop().RunUntilIdle(); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 304 | EXPECT_EQ(begin_frame_count_, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 305 | |
| 306 | // Enable animating from the first driver, which should schedule an |
| 307 | // invalidation to advance this animation. |
| 308 | first_driver.set_should_animate(true); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 309 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 310 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 311 | |
| 312 | // Start animating the first image. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 313 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 314 | EXPECT_EQ(animated_images.size(), 0u); |
| 315 | |
| 316 | // Invalidation should be scheduled for this image. |
| 317 | task_runner_->VerifyDelay(first_image_frames[0].duration); |
| 318 | |
| 319 | // Now enable animating the second image instead. |
| 320 | second_driver.set_should_animate(true); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 321 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 322 | |
| 323 | // Invalidation is triggered to start with no delay since the second image has |
| 324 | // not started animating yet. |
| 325 | task_runner_->VerifyDelay(base::TimeDelta()); |
| 326 | |
| 327 | // Disable animating all images. |
| 328 | first_driver.set_should_animate(false); |
| 329 | second_driver.set_should_animate(false); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 330 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 331 | |
| 332 | // Any scheduled invalidation should be cancelled. |
| 333 | base::RunLoop().RunUntilIdle(); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 334 | EXPECT_EQ(begin_frame_count_, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 335 | |
| 336 | controller_->UnregisterAnimationDriver(first_data.paint_image_id, |
| 337 | &first_driver); |
| 338 | controller_->UnregisterAnimationDriver(second_data.paint_image_id, |
| 339 | &second_driver); |
| 340 | } |
| 341 | |
| 342 | TEST_F(ImageAnimationControllerTest, RepetitionsRequested) { |
| 343 | std::vector<FrameMetadata> frames = { |
| 344 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 345 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), |
| 346 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(4))}; |
| 347 | |
| 348 | DiscardableImageMap::AnimatedImageMetadata data( |
| 349 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 350 | kAnimationLoopOnce, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 351 | controller_->UpdateAnimatedImage(data); |
| 352 | FakeAnimationDriver driver; |
| 353 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 354 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 355 | |
| 356 | // Finish a single loop in the animation. |
| 357 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0); |
| 358 | |
| 359 | // No invalidation should be scheduled now, since the requested number of |
| 360 | // loops have been completed. |
| 361 | invalidation_count_ = 0; |
| 362 | base::RunLoop().RunUntilIdle(); |
| 363 | EXPECT_EQ(invalidation_count_, 0); |
| 364 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 365 | |
| 366 | // Now with a repetition count of 5. |
| 367 | data.paint_image_id = PaintImage::GetNextId(); |
| 368 | data.repetition_count = 5; |
| 369 | controller_->UpdateAnimatedImage(data); |
| 370 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 371 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 372 | for (int i = 0; i < data.repetition_count; ++i) { |
| 373 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i); |
| 374 | |
| 375 | // Since we will be looping back to the first frame, the invalidation should |
| 376 | // have the delay of the last frame. Until we reach the end of requested |
| 377 | // iterations. |
| 378 | if (i < data.repetition_count - 1) |
| 379 | task_runner_->VerifyDelay(frames.back().duration); |
| 380 | invalidation_count_ = 0; |
| 381 | } |
| 382 | |
| 383 | // No invalidation should be scheduled now, since the requested number of |
| 384 | // loops have been completed. |
| 385 | invalidation_count_ = 0; |
| 386 | base::RunLoop().RunUntilIdle(); |
| 387 | EXPECT_EQ(invalidation_count_, 0); |
| 388 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 389 | |
| 390 | // Now with kAnimationLoopInfinite. |
| 391 | data.paint_image_id = PaintImage::GetNextId(); |
| 392 | data.repetition_count = kAnimationLoopInfinite; |
| 393 | controller_->UpdateAnimatedImage(data); |
| 394 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 395 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 396 | for (int i = 0; i < 7; ++i) { |
| 397 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i); |
| 398 | |
| 399 | // Since we will be looping back to the first frame, the invalidation should |
| 400 | // have the delay of the last frame. Until we reach the end of requested |
| 401 | // iterations. |
| 402 | if (i < data.repetition_count - 1) |
| 403 | task_runner_->VerifyDelay(frames.back().duration); |
| 404 | invalidation_count_ = 0; |
| 405 | } |
| 406 | |
| 407 | // We still have an invalidation scheduled since the image will keep looping |
| 408 | // till the drivers keep the animation active. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 409 | begin_frame_count_ = 0; |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 410 | base::RunLoop().RunUntilIdle(); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 411 | EXPECT_EQ(begin_frame_count_, 1); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 412 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 413 | |
| 414 | // Now try with a kAnimationNone image, which should result in a DCHECK |
| 415 | // failure. |
| 416 | data.paint_image_id = PaintImage::GetNextId(); |
| 417 | data.repetition_count = kAnimationNone; |
| 418 | EXPECT_DCHECK_DEATH(controller_->UpdateAnimatedImage(data)); |
| 419 | } |
| 420 | |
| 421 | TEST_F(ImageAnimationControllerTest, DisplayCompleteFrameOnly) { |
| 422 | std::vector<FrameMetadata> frames = { |
| 423 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 424 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), |
| 425 | FrameMetadata(false, base::TimeDelta::FromMilliseconds(4))}; |
| 426 | |
| 427 | DiscardableImageMap::AnimatedImageMetadata data( |
| 428 | PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 429 | frames, kAnimationLoopInfinite, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 430 | controller_->UpdateAnimatedImage(data); |
| 431 | FakeAnimationDriver driver; |
| 432 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 433 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 434 | |
| 435 | // Advance until the second frame. |
| 436 | LoopOnceNoDelay(data.paint_image_id, frames, 2, 0); |
| 437 | |
| 438 | // We have no invalidation scheduled since its not possible to animate the |
| 439 | // image further until the second frame is completed. |
| 440 | invalidation_count_ = 0; |
| 441 | base::RunLoop().RunUntilIdle(); |
| 442 | EXPECT_EQ(invalidation_count_, 0); |
| 443 | |
Khushal | 8ece8f98 | 2017-10-10 23:46:37 | [diff] [blame] | 444 | // Completely load the image but the frame is still incomplete. It should not |
| 445 | // be advanced. |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 446 | data.completion_state = PaintImage::CompletionState::DONE; |
| 447 | controller_->UpdateAnimatedImage(data); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 448 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 449 | |
Khushal | 8ece8f98 | 2017-10-10 23:46:37 | [diff] [blame] | 450 | // No invalidation is scheduled since the last frame is still incomplete. |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 451 | base::RunLoop().RunUntilIdle(); |
Khushal | 8ece8f98 | 2017-10-10 23:46:37 | [diff] [blame] | 452 | EXPECT_EQ(invalidation_count_, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 453 | |
| 454 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 455 | } |
| 456 | |
| 457 | TEST_F(ImageAnimationControllerTest, DontLoopPartiallyLoadedImages) { |
| 458 | std::vector<FrameMetadata> frames = { |
| 459 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 460 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 461 | |
| 462 | DiscardableImageMap::AnimatedImageMetadata data( |
| 463 | PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 464 | frames, 2, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 465 | controller_->UpdateAnimatedImage(data); |
| 466 | FakeAnimationDriver driver; |
| 467 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 468 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 469 | |
| 470 | // Finish the first loop. |
| 471 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0); |
| 472 | |
| 473 | // We shouldn't be looping back to the first frame until the image is known to |
| 474 | // be completely loaded. |
| 475 | invalidation_count_ = 0; |
| 476 | base::RunLoop().RunUntilIdle(); |
| 477 | EXPECT_EQ(invalidation_count_, 0); |
| 478 | |
| 479 | // Now add another frame and mark the image complete. The animation should |
| 480 | // advance and we should see another repetition. This verifies that we don't |
| 481 | // mark loops complete on reaching the last frame until the image is |
| 482 | // completely loaded and the frame count is known to be accurate. |
| 483 | frames.push_back(FrameMetadata(true, base::TimeDelta::FromMilliseconds(4))); |
| 484 | data.completion_state = PaintImage::CompletionState::DONE; |
| 485 | data.frames = frames; |
| 486 | controller_->UpdateAnimatedImage(data); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 487 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 488 | |
| 489 | // The animation advances to the last frame. We don't have a delay since we |
| 490 | // already advanced to the desired time in the loop above. |
| 491 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 492 | RunFrameRequestAndInvalidation(); |
| 493 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 494 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 495 | WhichTree::PENDING_TREE), |
| 496 | 2u); |
| 497 | EXPECT_EQ(animated_images.size(), 1u); |
| 498 | EXPECT_EQ(animated_images.count(data.paint_image_id), 1u); |
| 499 | controller_->DidActivate(); |
| 500 | |
| 501 | // Advancing the animation scheduled an invalidation for the next iteration. |
| 502 | task_runner_->VerifyDelay(frames.back().duration); |
| 503 | |
| 504 | // Perform another loop in the animation. |
| 505 | AdvanceNow(frames.back().duration); |
| 506 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1); |
| 507 | |
| 508 | // No invalidation should have been requested at the end of the second loop. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 509 | begin_frame_count_ = 0; |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 510 | base::RunLoop().RunUntilIdle(); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 511 | EXPECT_EQ(begin_frame_count_, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 512 | |
| 513 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 514 | } |
| 515 | |
| 516 | TEST_F(ImageAnimationControllerTest, DontAdvanceUntilDesiredTime) { |
| 517 | std::vector<FrameMetadata> frames = { |
| 518 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 519 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 520 | |
| 521 | DiscardableImageMap::AnimatedImageMetadata data( |
| 522 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 523 | kAnimationLoopOnce, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 524 | controller_->UpdateAnimatedImage(data); |
| 525 | FakeAnimationDriver driver; |
| 526 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 527 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 528 | |
| 529 | // Advance the first frame. |
| 530 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 531 | RunFrameRequestAndInvalidation(); |
| 532 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 533 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 534 | WhichTree::PENDING_TREE), |
| 535 | 0u); |
| 536 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 537 | WhichTree::ACTIVE_TREE), |
| 538 | 0u); |
| 539 | EXPECT_EQ(animated_images.size(), 0u); |
| 540 | controller_->DidActivate(); |
| 541 | |
| 542 | // We have an invalidation request for the second frame. |
| 543 | task_runner_->VerifyDelay(frames[0].duration); |
| 544 | |
| 545 | // While there is still time for the second frame, we get a new sync tree. The |
| 546 | // animation is not advanced. |
| 547 | base::TimeDelta time_remaining = base::TimeDelta::FromMilliseconds(1); |
| 548 | AdvanceNow(frames[0].duration - time_remaining); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 549 | animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 550 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 551 | WhichTree::PENDING_TREE), |
| 552 | 0u); |
| 553 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 554 | WhichTree::ACTIVE_TREE), |
| 555 | 0u); |
| 556 | EXPECT_EQ(animated_images.size(), 0u); |
| 557 | controller_->DidActivate(); |
| 558 | |
| 559 | // We did not get another invalidation request because there is no change in |
| 560 | // the desired time and the previous request is still pending. |
| 561 | EXPECT_FALSE(task_runner_->has_delay()); |
| 562 | |
| 563 | // We have a sync tree before the invalidation task could run. |
| 564 | AdvanceNow(time_remaining); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 565 | animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 566 | EXPECT_EQ(animated_images.size(), 1u); |
| 567 | EXPECT_EQ(animated_images.count(data.paint_image_id), 1u); |
| 568 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 569 | WhichTree::PENDING_TREE), |
| 570 | 1u); |
| 571 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 572 | WhichTree::ACTIVE_TREE), |
| 573 | 0u); |
| 574 | controller_->DidActivate(); |
| 575 | |
| 576 | // We shouldn't have an invalidation because the animation was already |
| 577 | // advanced to the last frame and the previous one should have been cancelled. |
| 578 | invalidation_count_ = 0; |
| 579 | base::RunLoop().RunUntilIdle(); |
| 580 | EXPECT_EQ(invalidation_count_, 0); |
| 581 | |
| 582 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 583 | } |
| 584 | |
| 585 | TEST_F(ImageAnimationControllerTest, RestartAfterSyncCutoff) { |
| 586 | std::vector<FrameMetadata> frames = { |
| 587 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 588 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 589 | |
| 590 | DiscardableImageMap::AnimatedImageMetadata data( |
| 591 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 592 | kAnimationLoopOnce, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 593 | controller_->UpdateAnimatedImage(data); |
| 594 | FakeAnimationDriver driver; |
| 595 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 596 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 597 | |
| 598 | // Advance the first frame. |
| 599 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 600 | RunFrameRequestAndInvalidation(); |
| 601 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 602 | EXPECT_EQ(animated_images.size(), 0u); |
| 603 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 604 | WhichTree::PENDING_TREE), |
| 605 | 0u); |
| 606 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 607 | WhichTree::ACTIVE_TREE), |
| 608 | 0u); |
| 609 | controller_->DidActivate(); |
| 610 | |
| 611 | // Invalidation request for the second frame. |
| 612 | task_runner_->VerifyDelay(frames[0].duration); |
| 613 | |
| 614 | // Advance the time by 10 min. |
| 615 | AdvanceNow(base::TimeDelta::FromMinutes(10)); |
| 616 | |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 617 | // Animate again, it starts from the first frame. We don't see a |
| 618 | // frame update, because that's the frame we are already displaying. |
| 619 | controller_->WillBeginImplFrame(BeginFrameArgs()); |
| 620 | animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 621 | EXPECT_EQ(animated_images.size(), 0u); |
| 622 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 623 | WhichTree::PENDING_TREE), |
| 624 | 0u); |
| 625 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 626 | WhichTree::ACTIVE_TREE), |
| 627 | 0u); |
| 628 | controller_->DidActivate(); |
| 629 | |
| 630 | // New invalidation request since the desired invalidation time changed. |
| 631 | task_runner_->VerifyDelay(frames[0].duration); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 632 | |
| 633 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 634 | } |
| 635 | |
| 636 | TEST_F(ImageAnimationControllerTest, DontSkipLoopsToCatchUpAfterLoad) { |
| 637 | std::vector<FrameMetadata> frames = { |
| 638 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 639 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), |
| 640 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)), |
| 641 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(5))}; |
| 642 | |
| 643 | DiscardableImageMap::AnimatedImageMetadata data( |
| 644 | PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE, |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 645 | frames, kAnimationLoopInfinite, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 646 | controller_->UpdateAnimatedImage(data); |
| 647 | FakeAnimationDriver driver; |
| 648 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 649 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 650 | |
| 651 | // Perform the first loop while the image is partially loaded, until the third |
| 652 | // frame. |
| 653 | LoopOnceNoDelay(data.paint_image_id, frames, 3u, 0); |
| 654 | |
| 655 | // The invalidation has been scheduled with a delay for the third frame's |
| 656 | // duration. |
| 657 | task_runner_->VerifyDelay(frames[2].duration); |
| 658 | |
| 659 | // |now_| is set to the desired time for the fourth frame. Advance further so |
| 660 | // we would reach the time for the second frame. |
| 661 | AdvanceNow(frames[3].duration + frames[0].duration); |
| 662 | |
| 663 | // Finish the image load. |
| 664 | data.completion_state = PaintImage::CompletionState::DONE; |
| 665 | controller_->UpdateAnimatedImage(data); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 666 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 667 | |
| 668 | // Invalidation is scheduled immediately because we are way past the desired |
| 669 | // time. We should start from the first frame after the image is loaded |
| 670 | // instead of skipping frames. |
| 671 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 672 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 673 | EXPECT_EQ(animated_images.size(), 1u); |
| 674 | EXPECT_EQ(animated_images.count(data.paint_image_id), 1u); |
| 675 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 676 | WhichTree::PENDING_TREE), |
| 677 | 0u); |
| 678 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 679 | WhichTree::ACTIVE_TREE), |
| 680 | 2u); |
| 681 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 682 | } |
| 683 | |
| 684 | TEST_F(ImageAnimationControllerTest, FinishRepetitionsDuringCatchUp) { |
| 685 | std::vector<FrameMetadata> frames = { |
| 686 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 687 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), |
| 688 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(4))}; |
| 689 | |
| 690 | // The animation wants 3 loops. |
| 691 | DiscardableImageMap::AnimatedImageMetadata data( |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 692 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, 3, 0); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 693 | controller_->UpdateAnimatedImage(data); |
| 694 | FakeAnimationDriver driver; |
| 695 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 696 | controller_->UpdateStateFromDrivers(); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 697 | |
| 698 | // Finish 2 loops. |
| 699 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0); |
| 700 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1); |
| 701 | |
| 702 | // now_ is set to the desired time for the first frame. Advance it so we would |
| 703 | // reach way beyond the third repeition. |
| 704 | AdvanceNow(base::TimeDelta::FromMinutes(1)); |
| 705 | |
| 706 | // Advance the animation, we should see the last frame since the desired |
| 707 | // repetition count will be reached during catch up. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 708 | RunFrameRequestAndInvalidation(); |
| 709 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 710 | // No invalidation since the active tree is already at the last frame. |
| 711 | EXPECT_EQ(animated_images.size(), 0u); |
| 712 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 713 | WhichTree::PENDING_TREE), |
| 714 | frames.size() - 1); |
| 715 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 716 | WhichTree::ACTIVE_TREE), |
| 717 | frames.size() - 1); |
| 718 | |
| 719 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 720 | } |
| 721 | |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 722 | TEST_F(ImageAnimationControllerTest, ResetAnimations) { |
| 723 | std::vector<FrameMetadata> frames = { |
| 724 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 725 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), |
| 726 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(4))}; |
| 727 | DiscardableImageMap::AnimatedImageMetadata data( |
| 728 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, 3, |
| 729 | 0u); |
| 730 | controller_->UpdateAnimatedImage(data); |
| 731 | FakeAnimationDriver driver; |
| 732 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 733 | controller_->UpdateStateFromDrivers(); |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 734 | |
| 735 | // Go uptill the second frame during the second iteration. |
| 736 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0); |
| 737 | LoopOnceNoDelay(data.paint_image_id, frames, 2u, 1); |
| 738 | |
| 739 | // Reset the animation. |
| 740 | data.reset_animation_sequence_id++; |
| 741 | controller_->UpdateAnimatedImage(data); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 742 | controller_->UpdateStateFromDrivers(); |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 743 | |
| 744 | // It should start again from the first frame and do 3 loops. |
| 745 | for (int i = 0; i < 3; ++i) { |
| 746 | bool restarting = i == 0; |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 747 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i, {}, |
| 748 | restarting); |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 749 | } |
| 750 | |
| 751 | // No invalidation should be pending. |
| 752 | invalidation_count_ = 0; |
| 753 | base::RunLoop().RunUntilIdle(); |
| 754 | EXPECT_EQ(invalidation_count_, 0); |
| 755 | |
| 756 | // Same image used again in a recording. There shouldn't be an invalidation |
| 757 | // since the reset sequence has already been synchronized. |
| 758 | controller_->UpdateAnimatedImage(data); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 759 | controller_->UpdateStateFromDrivers(); |
Khushal | 3e58f9b7 | 2017-09-26 05:28:55 | [diff] [blame] | 760 | base::RunLoop().RunUntilIdle(); |
| 761 | EXPECT_EQ(invalidation_count_, 0); |
| 762 | |
| 763 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 764 | } |
| 765 | |
sohan | 1d625d9 | 2018-03-12 21:57:08 | [diff] [blame] | 766 | TEST_F(ImageAnimationControllerTest, ResetAnimationStateMapOnNavigation) { |
| 767 | std::vector<FrameMetadata> first_image_frames = { |
| 768 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 769 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 770 | DiscardableImageMap::AnimatedImageMetadata first_data( |
| 771 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, |
| 772 | first_image_frames, kAnimationLoopOnce, 0); |
| 773 | controller_->UpdateAnimatedImage(first_data); |
| 774 | FakeAnimationDriver first_driver; |
| 775 | controller_->RegisterAnimationDriver(first_data.paint_image_id, |
| 776 | &first_driver); |
| 777 | |
| 778 | std::vector<FrameMetadata> second_image_frames = { |
| 779 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(5)), |
| 780 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 781 | DiscardableImageMap::AnimatedImageMetadata second_data( |
| 782 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, |
| 783 | second_image_frames, kAnimationLoopOnce, 0); |
| 784 | controller_->UpdateAnimatedImage(second_data); |
| 785 | FakeAnimationDriver second_driver; |
| 786 | controller_->RegisterAnimationDriver(second_data.paint_image_id, |
| 787 | &second_driver); |
| 788 | |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 789 | controller_->AnimateForSyncTree(BeginFrameArgs()); |
sohan | 1d625d9 | 2018-03-12 21:57:08 | [diff] [blame] | 790 | |
| 791 | controller_->UnregisterAnimationDriver(first_data.paint_image_id, |
| 792 | &first_driver); |
| 793 | EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 2u); |
| 794 | |
| 795 | // Fake navigation and activation. |
| 796 | controller_->set_did_navigate(); |
| 797 | controller_->DidActivate(); |
| 798 | |
| 799 | // Animation state map entries without drivers will be purged on navigation. |
| 800 | EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 1u); |
| 801 | |
| 802 | controller_->UnregisterAnimationDriver(second_data.paint_image_id, |
| 803 | &second_driver); |
| 804 | |
| 805 | EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 1u); |
| 806 | } |
| 807 | |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 808 | TEST_F(ImageAnimationControllerTest, ImageWithNonVsyncAlignedDurations) { |
| 809 | interval_ = base::TimeDelta::FromMilliseconds(1); |
| 810 | std::vector<FrameMetadata> frames = { |
| 811 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(2.5)), |
| 812 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(3.76)), |
| 813 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(4.27))}; |
| 814 | DiscardableImageMap::AnimatedImageMetadata data( |
| 815 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, 3, |
| 816 | 0u); |
| 817 | controller_->UpdateAnimatedImage(data); |
| 818 | FakeAnimationDriver driver; |
| 819 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
| 820 | controller_->UpdateStateFromDrivers(); |
| 821 | |
| 822 | std::vector<base::TimeDelta> expected_delays = { |
| 823 | base::TimeDelta::FromMilliseconds(2), |
Khushal | b43b61c | 2019-03-01 23:20:18 | [diff] [blame] | 824 | base::TimeDelta::FromMilliseconds(4), |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 825 | base::TimeDelta::FromMilliseconds(4)}; |
| 826 | LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0, |
| 827 | expected_delays); |
| 828 | |
| 829 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 830 | } |
| 831 | |
| 832 | TEST_F(ImageAnimationControllerTest, ImageWithLessThanIntervalDurations) { |
| 833 | interval_ = base::TimeDelta::FromMilliseconds(1); |
| 834 | std::vector<FrameMetadata> frames = { |
| 835 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(0.5)), |
| 836 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(0.43)), |
| 837 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(0.76)), |
| 838 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(0.74)), |
| 839 | }; |
| 840 | frames.push_back(FrameMetadata(true, interval_ - frames.back().duration)); |
| 841 | DiscardableImageMap::AnimatedImageMetadata data( |
| 842 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, |
| 843 | kAnimationLoopOnce, 0u); |
| 844 | controller_->UpdateAnimatedImage(data); |
| 845 | FakeAnimationDriver driver; |
| 846 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
| 847 | controller_->UpdateStateFromDrivers(); |
| 848 | |
Khushal | b43b61c | 2019-03-01 23:20:18 | [diff] [blame] | 849 | // Animation starts at 10s, we jump directly to the third frame. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 850 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 851 | auto invalidated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
| 852 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 853 | WhichTree::PENDING_TREE), |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 854 | 2u); |
| 855 | controller_->DidActivate(); |
| 856 | |
| 857 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 858 | } |
| 859 | |
| 860 | TEST_F(ImageAnimationControllerTest, ImplFramesWhileInvalidationPending) { |
| 861 | std::vector<FrameMetadata> frames = { |
| 862 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(2.5)), |
| 863 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(3.76)), |
| 864 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(4.27))}; |
| 865 | DiscardableImageMap::AnimatedImageMetadata data( |
| 866 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, 3, |
| 867 | 0u); |
| 868 | controller_->UpdateAnimatedImage(data); |
| 869 | FakeAnimationDriver driver; |
| 870 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
| 871 | controller_->UpdateStateFromDrivers(); |
| 872 | |
| 873 | // Send the impl frame for invalidating the current image such that an |
| 874 | // invalidation request is pending. |
| 875 | task_runner_->VerifyDelay(base::TimeDelta()); |
| 876 | RunFrameRequestAndInvalidation(); |
| 877 | |
| 878 | // No new task since an invalidation is expected. |
| 879 | controller_->WillBeginImplFrame(BeginFrameArgs()); |
| 880 | EXPECT_FALSE(task_runner_->has_delay()); |
| 881 | |
| 882 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 883 | } |
| 884 | |
| 885 | TEST_F(ImageAnimationControllerTest, MissedBeginFrameAfterRequest) { |
| 886 | std::vector<FrameMetadata> frames = { |
| 887 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(2.5)), |
| 888 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(3.76)), |
| 889 | FrameMetadata(true, base::TimeDelta::FromMillisecondsD(4.27))}; |
| 890 | DiscardableImageMap::AnimatedImageMetadata data( |
| 891 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, 3, |
| 892 | 0u); |
| 893 | controller_->UpdateAnimatedImage(data); |
| 894 | FakeAnimationDriver driver; |
| 895 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
| 896 | controller_->UpdateStateFromDrivers(); |
| 897 | |
| 898 | // There should be a frame request with no delay to start the animation. |
| 899 | task_runner_->VerifyDelay(base::TimeDelta()); |
| 900 | |
| 901 | // Run the pending frame request. |
| 902 | begin_frame_count_ = 0; |
| 903 | base::RunLoop().RunUntilIdle(); |
| 904 | EXPECT_EQ(begin_frame_count_, 1); |
| 905 | |
| 906 | // Pretend that we got an impl frame for the previous vsync. |
| 907 | controller_->WillBeginImplFrame(BeginFrameArgs(now_ - interval_)); |
| 908 | |
| 909 | // We should get another request for an impl frame. |
| 910 | EXPECT_EQ(begin_frame_count_, 2); |
| 911 | |
| 912 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 913 | } |
| 914 | |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 915 | class ImageAnimationControllerNoResyncTest |
| 916 | : public ImageAnimationControllerTest { |
| 917 | protected: |
| 918 | bool GetEnableImageAnimationResync() const override { return false; } |
| 919 | }; |
| 920 | |
| 921 | TEST_F(ImageAnimationControllerNoResyncTest, NoSyncCutoffAfterIdle) { |
| 922 | std::vector<FrameMetadata> frames = { |
| 923 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 924 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3))}; |
| 925 | |
| 926 | DiscardableImageMap::AnimatedImageMetadata data( |
| 927 | PaintImage::GetNextId(), PaintImage::CompletionState::DONE, frames, |
| 928 | kAnimationLoopInfinite, 0); |
| 929 | controller_->UpdateAnimatedImage(data); |
| 930 | FakeAnimationDriver driver; |
| 931 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 932 | controller_->UpdateStateFromDrivers(); |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 933 | |
| 934 | // Advance the first frame. |
| 935 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 936 | RunFrameRequestAndInvalidation(); |
| 937 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 938 | EXPECT_EQ(animated_images.size(), 0u); |
| 939 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 940 | WhichTree::PENDING_TREE), |
| 941 | 0u); |
| 942 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 943 | WhichTree::ACTIVE_TREE), |
| 944 | 0u); |
| 945 | controller_->DidActivate(); |
| 946 | |
| 947 | // Invalidation request for the second frame. |
| 948 | task_runner_->VerifyDelay(frames[0].duration); |
| 949 | |
| 950 | // Advance the time by 10 min (divisible by animation duration) and first |
| 951 | // frame duration. |
| 952 | AdvanceNow(base::TimeDelta::FromMinutes(10) + frames[0].duration); |
| 953 | |
| 954 | // Animate again, it should not restart from the start. Should display second |
| 955 | // animation frame. |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 956 | controller_->WillBeginImplFrame(BeginFrameArgs()); |
| 957 | animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 958 | EXPECT_EQ(animated_images.size(), 1u); |
| 959 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 960 | WhichTree::PENDING_TREE), |
| 961 | 1u); |
| 962 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 963 | WhichTree::ACTIVE_TREE), |
| 964 | 0u); |
| 965 | controller_->DidActivate(); |
| 966 | |
| 967 | // New invalidation request since the desired invalidation time changed. |
| 968 | task_runner_->VerifyDelay(frames[1].duration); |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 969 | |
| 970 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 971 | } |
| 972 | |
| 973 | TEST_F(ImageAnimationControllerNoResyncTest, SkipsLoopsAfterFirstIteration) { |
| 974 | std::vector<FrameMetadata> frames = { |
| 975 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), |
| 976 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), |
| 977 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(4)), |
| 978 | FrameMetadata(true, base::TimeDelta::FromMilliseconds(5))}; |
| 979 | |
| 980 | DiscardableImageMap::AnimatedImageMetadata data( |
| 981 | PaintImage::GetNextId(), PaintImage::CompletionState::PARTIALLY_DONE, |
| 982 | frames, kAnimationLoopInfinite, 0); |
| 983 | controller_->UpdateAnimatedImage(data); |
| 984 | FakeAnimationDriver driver; |
| 985 | controller_->RegisterAnimationDriver(data.paint_image_id, &driver); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 986 | controller_->UpdateStateFromDrivers(); |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 987 | |
| 988 | // Perform the first loop while the image is partially loaded, until the third |
| 989 | // frame. |
| 990 | LoopOnceNoDelay(data.paint_image_id, frames, 3u, 0); |
| 991 | |
| 992 | // The invalidation has been scheduled with a delay for the third frame's |
| 993 | // duration. |
| 994 | task_runner_->VerifyDelay(frames[2].duration); |
| 995 | |
| 996 | // |now_| is set to the desired time for the fourth frame. Advance further so |
| 997 | // we reach the time for the second frame. |
| 998 | AdvanceNow(frames[3].duration + frames[0].duration); |
| 999 | |
| 1000 | // Finish the image load. |
| 1001 | data.completion_state = PaintImage::CompletionState::DONE; |
| 1002 | controller_->UpdateAnimatedImage(data); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 1003 | controller_->UpdateStateFromDrivers(); |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 1004 | |
| 1005 | // Invalidation is scheduled immediately because we are way past the desired |
| 1006 | // time. We skip frames even after the image is loaded. |
| 1007 | task_runner_->VerifyDelay(base::TimeDelta()); |
Khushal | a60ea1a | 2019-02-23 03:51:30 | [diff] [blame] | 1008 | auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); |
Eric Seckler | 62877094 | 2018-03-02 23:20:21 | [diff] [blame] | 1009 | EXPECT_EQ(animated_images.size(), 1u); |
| 1010 | EXPECT_EQ(animated_images.count(data.paint_image_id), 1u); |
| 1011 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 1012 | WhichTree::PENDING_TREE), |
| 1013 | 1u); |
| 1014 | EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, |
| 1015 | WhichTree::ACTIVE_TREE), |
| 1016 | 2u); |
| 1017 | controller_->UnregisterAnimationDriver(data.paint_image_id, &driver); |
| 1018 | } |
| 1019 | |
Khushal | fdacdc9 | 2017-09-22 22:40:52 | [diff] [blame] | 1020 | } // namespace cc |