OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include "base/bind.h" |
| 6 #include "base/run_loop.h" |
| 7 #include "base/test/test_simple_task_runner.h" |
| 8 #include "base/threading/sequenced_task_runner_handle.h" |
5 #include "cc/tiles/image_controller.h" | 9 #include "cc/tiles/image_controller.h" |
6 #include "cc/tiles/image_decode_cache.h" | 10 #include "cc/tiles/image_decode_cache.h" |
7 #include "testing/gtest/include/gtest/gtest.h" | 11 #include "testing/gtest/include/gtest/gtest.h" |
8 | 12 |
9 namespace cc { | 13 namespace cc { |
10 | 14 |
| 15 // Image decode cache with introspection! |
11 class TestableCache : public ImageDecodeCache { | 16 class TestableCache : public ImageDecodeCache { |
12 public: | 17 public: |
| 18 ~TestableCache() override { EXPECT_EQ(number_of_refs_, 0); } |
| 19 |
13 bool GetTaskForImageAndRef(const DrawImage& image, | 20 bool GetTaskForImageAndRef(const DrawImage& image, |
14 const TracingInfo& tracing_info, | 21 const TracingInfo& tracing_info, |
15 scoped_refptr<TileTask>* task) override { | 22 scoped_refptr<TileTask>* task) override { |
16 *task = nullptr; | 23 *task = task_to_use_; |
17 ++number_of_refs_; | 24 ++number_of_refs_; |
18 return true; | 25 return true; |
19 } | 26 } |
| 27 bool GetOutOfRasterDecodeTaskForImageAndRef( |
| 28 const DrawImage& image, |
| 29 scoped_refptr<TileTask>* task) override { |
| 30 *task = task_to_use_; |
| 31 ++number_of_refs_; |
| 32 return true; |
| 33 } |
20 | 34 |
21 void UnrefImage(const DrawImage& image) override { | 35 void UnrefImage(const DrawImage& image) override { |
22 ASSERT_GT(number_of_refs_, 0); | 36 ASSERT_GT(number_of_refs_, 0); |
23 --number_of_refs_; | 37 --number_of_refs_; |
24 } | 38 } |
25 DecodedDrawImage GetDecodedImageForDraw(const DrawImage& image) override { | 39 DecodedDrawImage GetDecodedImageForDraw(const DrawImage& image) override { |
26 return DecodedDrawImage(nullptr, kNone_SkFilterQuality); | 40 return DecodedDrawImage(nullptr, kNone_SkFilterQuality); |
27 } | 41 } |
28 void DrawWithImageFinished(const DrawImage& image, | 42 void DrawWithImageFinished(const DrawImage& image, |
29 const DecodedDrawImage& decoded_image) override {} | 43 const DecodedDrawImage& decoded_image) override {} |
30 void ReduceCacheUsage() override {} | 44 void ReduceCacheUsage() override {} |
31 void SetShouldAggressivelyFreeResources( | 45 void SetShouldAggressivelyFreeResources( |
32 bool aggressively_free_resources) override {} | 46 bool aggressively_free_resources) override {} |
33 | 47 |
34 int number_of_refs() const { return number_of_refs_; } | 48 int number_of_refs() const { return number_of_refs_; } |
| 49 void SetTaskToUse(scoped_refptr<TileTask> task) { task_to_use_ = task; } |
35 | 50 |
36 private: | 51 private: |
37 int number_of_refs_ = 0; | 52 int number_of_refs_ = 0; |
38 }; | 53 scoped_refptr<TileTask> task_to_use_; |
39 | 54 }; |
40 TEST(ImageControllerTest, NullCacheUnrefsImages) { | 55 |
41 TestableCache cache; | 56 // A simple class that can receive decode callbacks. |
42 ImageController controller; | 57 class DecodeClient { |
43 controller.SetImageDecodeCache(&cache); | 58 public: |
44 | 59 DecodeClient() {} |
| 60 void Callback(const base::Closure& quit_closure, |
| 61 ImageController::ImageDecodeRequestId id) { |
| 62 id_ = id; |
| 63 quit_closure.Run(); |
| 64 } |
| 65 |
| 66 ImageController::ImageDecodeRequestId id() { return id_; } |
| 67 |
| 68 private: |
| 69 ImageController::ImageDecodeRequestId id_ = 0; |
| 70 }; |
| 71 |
| 72 // A dummy task that does nothing. |
| 73 class SimpleTask : public TileTask { |
| 74 public: |
| 75 SimpleTask() : TileTask(true /* supports_concurrent_execution */) { |
| 76 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); |
| 77 } |
| 78 |
| 79 void RunOnWorkerThread() override { |
| 80 EXPECT_FALSE(HasCompleted()); |
| 81 EXPECT_FALSE(thread_checker_.CalledOnValidThread()); |
| 82 has_run_ = true; |
| 83 } |
| 84 void OnTaskCompleted() override { |
| 85 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); |
| 86 } |
| 87 |
| 88 bool has_run() { return has_run_; } |
| 89 |
| 90 private: |
| 91 ~SimpleTask() override = default; |
| 92 |
| 93 base::ThreadChecker thread_checker_; |
| 94 bool has_run_ = false; |
| 95 |
| 96 DISALLOW_COPY_AND_ASSIGN(SimpleTask); |
| 97 }; |
| 98 |
| 99 // A task that blocks until instructed otherwise. |
| 100 class BlockingTask : public TileTask { |
| 101 public: |
| 102 BlockingTask() |
| 103 : TileTask(true /* supports_concurrent_execution */), run_cv_(&lock_) { |
| 104 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); |
| 105 } |
| 106 |
| 107 void RunOnWorkerThread() override { |
| 108 EXPECT_FALSE(HasCompleted()); |
| 109 EXPECT_FALSE(thread_checker_.CalledOnValidThread()); |
| 110 base::AutoLock hold(lock_); |
| 111 if (!can_run_) |
| 112 run_cv_.Wait(); |
| 113 has_run_ = true; |
| 114 } |
| 115 |
| 116 void OnTaskCompleted() override { |
| 117 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); |
| 118 } |
| 119 |
| 120 void AllowToRun() { |
| 121 base::AutoLock hold(lock_); |
| 122 can_run_ = true; |
| 123 run_cv_.Signal(); |
| 124 } |
| 125 |
| 126 bool has_run() { return has_run_; } |
| 127 |
| 128 private: |
| 129 ~BlockingTask() override = default; |
| 130 |
| 131 base::ThreadChecker thread_checker_; |
| 132 bool has_run_ = false; |
| 133 base::Lock lock_; |
| 134 base::ConditionVariable run_cv_; |
| 135 bool can_run_ = false; |
| 136 |
| 137 DISALLOW_COPY_AND_ASSIGN(BlockingTask); |
| 138 }; |
| 139 |
| 140 // For tests that exercise image controller's thread, this is the timeout value |
| 141 // to |
| 142 // allow the worker thread to do its work. |
| 143 int kDefaultTimeoutSeconds = 10; |
| 144 |
| 145 class ImageControllerTest : public testing::Test { |
| 146 public: |
| 147 ImageControllerTest() |
| 148 : task_runner_(base::SequencedTaskRunnerHandle::Get()), |
| 149 controller_(task_runner_.get()) { |
| 150 bitmap_.allocN32Pixels(1, 1); |
| 151 image_ = SkImage::MakeFromBitmap(bitmap_); |
| 152 } |
| 153 ~ImageControllerTest() override = default; |
| 154 |
| 155 void SetUp() override { |
| 156 cache_ = TestableCache(); |
| 157 controller_.SetImageDecodeCache(&cache_); |
| 158 } |
| 159 |
| 160 base::SequencedTaskRunner* task_runner() { return task_runner_.get(); } |
| 161 ImageController* controller() { return &controller_; } |
| 162 TestableCache* cache() { return &cache_; } |
| 163 sk_sp<const SkImage> image() const { return image_; } |
| 164 |
| 165 // Timeout callback, which errors and exits the runloop. |
| 166 static void Timeout(base::RunLoop* run_loop) { |
| 167 ADD_FAILURE() << "Timeout."; |
| 168 run_loop->Quit(); |
| 169 } |
| 170 |
| 171 // Convenience method to run the run loop with a timeout. |
| 172 void RunOrTimeout(base::RunLoop* run_loop) { |
| 173 task_runner_->PostDelayedTask( |
| 174 FROM_HERE, |
| 175 base::Bind(&ImageControllerTest::Timeout, base::Unretained(run_loop)), |
| 176 base::TimeDelta::FromSeconds(kDefaultTimeoutSeconds)); |
| 177 run_loop->Run(); |
| 178 } |
| 179 |
| 180 private: |
| 181 scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| 182 TestableCache cache_; |
| 183 ImageController controller_; |
| 184 SkBitmap bitmap_; |
| 185 sk_sp<const SkImage> image_; |
| 186 }; |
| 187 |
| 188 TEST_F(ImageControllerTest, NullControllerUnrefsImages) { |
45 std::vector<DrawImage> images(10); | 189 std::vector<DrawImage> images(10); |
46 ImageDecodeCache::TracingInfo tracing_info; | 190 ImageDecodeCache::TracingInfo tracing_info; |
47 | 191 |
48 ASSERT_EQ(10u, images.size()); | 192 ASSERT_EQ(10u, images.size()); |
49 auto tasks = controller.SetPredecodeImages(std::move(images), tracing_info); | 193 auto tasks = |
| 194 controller()->SetPredecodeImages(std::move(images), tracing_info); |
50 EXPECT_EQ(0u, tasks.size()); | 195 EXPECT_EQ(0u, tasks.size()); |
51 EXPECT_EQ(10, cache.number_of_refs()); | 196 EXPECT_EQ(10, cache()->number_of_refs()); |
52 | 197 |
53 controller.SetImageDecodeCache(nullptr); | 198 controller()->SetImageDecodeCache(nullptr); |
54 EXPECT_EQ(0, cache.number_of_refs()); | 199 EXPECT_EQ(0, cache()->number_of_refs()); |
| 200 } |
| 201 |
| 202 TEST_F(ImageControllerTest, QueueImageDecode) { |
| 203 base::RunLoop run_loop; |
| 204 DecodeClient decode_client; |
| 205 EXPECT_EQ(image()->bounds().width(), 1); |
| 206 ImageController::ImageDecodeRequestId expected_id = |
| 207 controller()->QueueImageDecode( |
| 208 image(), |
| 209 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client), |
| 210 run_loop.QuitClosure())); |
| 211 RunOrTimeout(&run_loop); |
| 212 EXPECT_EQ(expected_id, decode_client.id()); |
| 213 } |
| 214 |
| 215 TEST_F(ImageControllerTest, QueueImageDecodeMultipleImages) { |
| 216 base::RunLoop run_loop; |
| 217 DecodeClient decode_client1; |
| 218 ImageController::ImageDecodeRequestId expected_id1 = |
| 219 controller()->QueueImageDecode( |
| 220 image(), |
| 221 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), |
| 222 base::Bind([] {}))); |
| 223 DecodeClient decode_client2; |
| 224 ImageController::ImageDecodeRequestId expected_id2 = |
| 225 controller()->QueueImageDecode( |
| 226 image(), |
| 227 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), |
| 228 base::Bind([] {}))); |
| 229 DecodeClient decode_client3; |
| 230 ImageController::ImageDecodeRequestId expected_id3 = |
| 231 controller()->QueueImageDecode( |
| 232 image(), |
| 233 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client3), |
| 234 run_loop.QuitClosure())); |
| 235 RunOrTimeout(&run_loop); |
| 236 EXPECT_EQ(expected_id1, decode_client1.id()); |
| 237 EXPECT_EQ(expected_id2, decode_client2.id()); |
| 238 EXPECT_EQ(expected_id3, decode_client3.id()); |
| 239 } |
| 240 |
| 241 TEST_F(ImageControllerTest, QueueImageDecodeWithTask) { |
| 242 scoped_refptr<SimpleTask> task(new SimpleTask); |
| 243 cache()->SetTaskToUse(task); |
| 244 |
| 245 base::RunLoop run_loop; |
| 246 DecodeClient decode_client; |
| 247 ImageController::ImageDecodeRequestId expected_id = |
| 248 controller()->QueueImageDecode( |
| 249 image(), |
| 250 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client), |
| 251 run_loop.QuitClosure())); |
| 252 RunOrTimeout(&run_loop); |
| 253 EXPECT_EQ(expected_id, decode_client.id()); |
| 254 EXPECT_TRUE(task->has_run()); |
| 255 EXPECT_TRUE(task->HasCompleted()); |
| 256 } |
| 257 |
| 258 TEST_F(ImageControllerTest, QueueImageDecodeMultipleImagesSameTask) { |
| 259 scoped_refptr<SimpleTask> task(new SimpleTask); |
| 260 cache()->SetTaskToUse(task); |
| 261 |
| 262 base::RunLoop run_loop; |
| 263 DecodeClient decode_client1; |
| 264 ImageController::ImageDecodeRequestId expected_id1 = |
| 265 controller()->QueueImageDecode( |
| 266 image(), |
| 267 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), |
| 268 base::Bind([] {}))); |
| 269 DecodeClient decode_client2; |
| 270 ImageController::ImageDecodeRequestId expected_id2 = |
| 271 controller()->QueueImageDecode( |
| 272 image(), |
| 273 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), |
| 274 base::Bind([] {}))); |
| 275 DecodeClient decode_client3; |
| 276 ImageController::ImageDecodeRequestId expected_id3 = |
| 277 controller()->QueueImageDecode( |
| 278 image(), |
| 279 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client3), |
| 280 run_loop.QuitClosure())); |
| 281 RunOrTimeout(&run_loop); |
| 282 EXPECT_EQ(expected_id1, decode_client1.id()); |
| 283 EXPECT_EQ(expected_id2, decode_client2.id()); |
| 284 EXPECT_EQ(expected_id3, decode_client3.id()); |
| 285 EXPECT_TRUE(task->has_run()); |
| 286 EXPECT_TRUE(task->HasCompleted()); |
| 287 } |
| 288 |
| 289 TEST_F(ImageControllerTest, QueueImageDecodeChangeControllerWithTaskQueued) { |
| 290 scoped_refptr<BlockingTask> task_one(new BlockingTask); |
| 291 cache()->SetTaskToUse(task_one); |
| 292 |
| 293 DecodeClient decode_client1; |
| 294 ImageController::ImageDecodeRequestId expected_id1 = |
| 295 controller()->QueueImageDecode( |
| 296 image(), |
| 297 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), |
| 298 base::Bind([] {}))); |
| 299 |
| 300 scoped_refptr<BlockingTask> task_two(new BlockingTask); |
| 301 cache()->SetTaskToUse(task_two); |
| 302 |
| 303 base::RunLoop run_loop; |
| 304 DecodeClient decode_client2; |
| 305 ImageController::ImageDecodeRequestId expected_id2 = |
| 306 controller()->QueueImageDecode( |
| 307 image(), |
| 308 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), |
| 309 run_loop.QuitClosure())); |
| 310 |
| 311 task_one->AllowToRun(); |
| 312 task_two->AllowToRun(); |
| 313 controller()->SetImageDecodeCache(nullptr); |
| 314 |
| 315 RunOrTimeout(&run_loop); |
| 316 |
| 317 EXPECT_TRUE(task_one->state().IsCanceled() || task_one->HasCompleted()); |
| 318 EXPECT_TRUE(task_two->state().IsCanceled() || task_two->HasCompleted()); |
| 319 EXPECT_EQ(expected_id1, decode_client1.id()); |
| 320 EXPECT_EQ(expected_id2, decode_client2.id()); |
| 321 } |
| 322 |
| 323 TEST_F(ImageControllerTest, QueueImageDecodeLocksImageForTwoFrames) { |
| 324 base::RunLoop run_loop1; |
| 325 DecodeClient decode_client1; |
| 326 ImageController::ImageDecodeRequestId expected_id1 = |
| 327 controller()->QueueImageDecode( |
| 328 image(), |
| 329 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), |
| 330 run_loop1.QuitClosure())); |
| 331 RunOrTimeout(&run_loop1); |
| 332 EXPECT_EQ(expected_id1, decode_client1.id()); |
| 333 |
| 334 // We have a ref, and it's still there after one frame finishes. |
| 335 EXPECT_EQ(1, cache()->number_of_refs()); |
| 336 controller()->NotifyFrameFinished(); |
| 337 EXPECT_EQ(1, cache()->number_of_refs()); |
| 338 |
| 339 base::RunLoop run_loop2; |
| 340 DecodeClient decode_client2; |
| 341 ImageController::ImageDecodeRequestId expected_id2 = |
| 342 controller()->QueueImageDecode( |
| 343 image(), |
| 344 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), |
| 345 run_loop2.QuitClosure())); |
| 346 RunOrTimeout(&run_loop2); |
| 347 EXPECT_EQ(expected_id2, decode_client2.id()); |
| 348 |
| 349 // Now, we have two refs. |
| 350 EXPECT_EQ(2, cache()->number_of_refs()); |
| 351 |
| 352 // When the frame finishes, the first ref is gone. |
| 353 controller()->NotifyFrameFinished(); |
| 354 EXPECT_EQ(1, cache()->number_of_refs()); |
| 355 |
| 356 // When another frame finishes, all refs are gone. |
| 357 controller()->NotifyFrameFinished(); |
| 358 EXPECT_EQ(0, cache()->number_of_refs()); |
| 359 } |
| 360 |
| 361 TEST_F(ImageControllerTest, QueueImageDecodeImageAlreadyLocked) { |
| 362 scoped_refptr<SimpleTask> task(new SimpleTask); |
| 363 cache()->SetTaskToUse(task); |
| 364 |
| 365 base::RunLoop run_loop1; |
| 366 DecodeClient decode_client1; |
| 367 ImageController::ImageDecodeRequestId expected_id1 = |
| 368 controller()->QueueImageDecode( |
| 369 image(), |
| 370 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), |
| 371 run_loop1.QuitClosure())); |
| 372 RunOrTimeout(&run_loop1); |
| 373 EXPECT_EQ(expected_id1, decode_client1.id()); |
| 374 EXPECT_TRUE(task->has_run()); |
| 375 |
| 376 cache()->SetTaskToUse(nullptr); |
| 377 base::RunLoop run_loop2; |
| 378 DecodeClient decode_client2; |
| 379 ImageController::ImageDecodeRequestId expected_id2 = |
| 380 controller()->QueueImageDecode( |
| 381 image(), |
| 382 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), |
| 383 run_loop2.QuitClosure())); |
| 384 RunOrTimeout(&run_loop2); |
| 385 EXPECT_EQ(expected_id2, decode_client2.id()); |
| 386 } |
| 387 |
| 388 TEST_F(ImageControllerTest, QueueImageDecodeLockedImageControllerChange) { |
| 389 scoped_refptr<SimpleTask> task(new SimpleTask); |
| 390 cache()->SetTaskToUse(task); |
| 391 |
| 392 base::RunLoop run_loop1; |
| 393 DecodeClient decode_client1; |
| 394 ImageController::ImageDecodeRequestId expected_id1 = |
| 395 controller()->QueueImageDecode( |
| 396 image(), |
| 397 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), |
| 398 run_loop1.QuitClosure())); |
| 399 RunOrTimeout(&run_loop1); |
| 400 EXPECT_EQ(expected_id1, decode_client1.id()); |
| 401 EXPECT_TRUE(task->has_run()); |
| 402 EXPECT_EQ(1, cache()->number_of_refs()); |
| 403 |
| 404 controller()->SetImageDecodeCache(nullptr); |
| 405 EXPECT_EQ(0, cache()->number_of_refs()); |
55 } | 406 } |
56 | 407 |
57 } // namespace cc | 408 } // namespace cc |
OLD | NEW |