TestAnimationFrameBuffer.cpp (34816B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include <utility> 7 8 #include "AnimationFrameBuffer.h" 9 #include "Common.h" 10 #include "gtest/gtest.h" 11 12 using namespace mozilla; 13 using namespace mozilla::image; 14 15 static already_AddRefed<imgFrame> CreateEmptyFrame( 16 const gfx::IntSize& aSize = gfx::IntSize(1, 1), 17 const gfx::IntRect& aFrameRect = gfx::IntRect(0, 0, 1, 1), 18 bool aCanRecycle = true) { 19 RefPtr<imgFrame> frame = new imgFrame(); 20 AnimationParams animParams{aFrameRect, FrameTimeout::Forever(), 21 /* aFrameNum */ 1, BlendMethod::OVER, 22 DisposalMethod::NOT_SPECIFIED}; 23 nsresult rv = 24 frame->InitForDecoder(aSize, mozilla::gfx::SurfaceFormat::OS_RGBA, false, 25 Some(animParams), aCanRecycle); 26 EXPECT_NS_SUCCEEDED(rv); 27 RawAccessFrameRef frameRef = frame->RawAccessRef(); 28 // Normally the blend animation filter would set the dirty rect, but since 29 // we aren't producing an actual animation here, we need to fake it. 30 frame->SetDirtyRect(aFrameRect); 31 frame->Finish(); 32 return frame.forget(); 33 } 34 35 static bool ReinitForRecycle(RawAccessFrameRef& aFrame) { 36 if (!aFrame) { 37 return false; 38 } 39 40 AnimationParams animParams{aFrame->GetRect(), FrameTimeout::Forever(), 41 /* aFrameNum */ 1, BlendMethod::OVER, 42 DisposalMethod::NOT_SPECIFIED}; 43 return NS_SUCCEEDED(aFrame->InitForDecoderRecycle(animParams)); 44 } 45 46 static void PrepareForDiscardingQueue(AnimationFrameRetainedBuffer& aQueue) { 47 ASSERT_EQ(size_t(0), aQueue.Size()); 48 ASSERT_LT(size_t(1), aQueue.Batch()); 49 50 AnimationFrameBuffer::InsertStatus status = aQueue.Insert(CreateEmptyFrame()); 51 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status); 52 53 while (true) { 54 status = aQueue.Insert(CreateEmptyFrame()); 55 bool restartDecoder = aQueue.AdvanceTo(aQueue.Size() - 1); 56 EXPECT_FALSE(restartDecoder); 57 58 if (status == AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE) { 59 break; 60 } 61 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status); 62 } 63 64 EXPECT_EQ(aQueue.Threshold(), aQueue.Size()); 65 } 66 67 static void VerifyDiscardingQueueContents( 68 AnimationFrameDiscardingQueue& aQueue) { 69 auto frames = aQueue.Display(); 70 for (auto i : frames) { 71 EXPECT_TRUE(i != nullptr); 72 } 73 } 74 75 static void VerifyInsertInternal(AnimationFrameBuffer& aQueue, 76 imgFrame* aFrame) { 77 // Determine the frame index where we just inserted the frame. 78 size_t frameIndex; 79 if (aQueue.MayDiscard()) { 80 const AnimationFrameDiscardingQueue& queue = 81 *static_cast<AnimationFrameDiscardingQueue*>(&aQueue); 82 frameIndex = queue.PendingInsert() == 0 ? queue.Size() - 1 83 : queue.PendingInsert() - 1; 84 } else { 85 ASSERT_FALSE(aQueue.SizeKnown()); 86 frameIndex = aQueue.Size() - 1; 87 } 88 89 // Make sure we can get the frame from that index. 90 RefPtr<imgFrame> frame = aQueue.Get(frameIndex, false); 91 EXPECT_EQ(aFrame, frame.get()); 92 } 93 94 static void VerifyAdvance(AnimationFrameBuffer& aQueue, size_t aExpectedFrame, 95 bool aExpectedRestartDecoder) { 96 RefPtr<imgFrame> oldFrame; 97 size_t totalRecycled; 98 if (aQueue.IsRecycling()) { 99 AnimationFrameRecyclingQueue& queue = 100 *static_cast<AnimationFrameRecyclingQueue*>(&aQueue); 101 oldFrame = queue.Get(queue.Displayed(), false); 102 totalRecycled = queue.Recycle().size(); 103 } 104 105 bool restartDecoder = aQueue.AdvanceTo(aExpectedFrame); 106 EXPECT_EQ(aExpectedRestartDecoder, restartDecoder); 107 108 if (aQueue.IsRecycling()) { 109 const AnimationFrameRecyclingQueue& queue = 110 *static_cast<AnimationFrameRecyclingQueue*>(&aQueue); 111 EXPECT_FALSE(queue.Recycle().back().mDirtyRect.IsEmpty()); 112 EXPECT_TRUE( 113 queue.Recycle().back().mDirtyRect.Contains(oldFrame->GetDirtyRect())); 114 EXPECT_EQ(totalRecycled + 1, queue.Recycle().size()); 115 EXPECT_EQ(oldFrame.get(), queue.Recycle().back().mFrame.get()); 116 } 117 } 118 119 static void VerifyInsertAndAdvance( 120 AnimationFrameBuffer& aQueue, size_t aExpectedFrame, 121 AnimationFrameBuffer::InsertStatus aExpectedStatus) { 122 // Insert the decoded frame. 123 RefPtr<imgFrame> frame = CreateEmptyFrame(); 124 AnimationFrameBuffer::InsertStatus status = 125 aQueue.Insert(RefPtr<imgFrame>(frame)); 126 EXPECT_EQ(aExpectedStatus, status); 127 EXPECT_TRUE(aQueue.IsLastInsertedFrame(frame)); 128 VerifyInsertInternal(aQueue, frame); 129 130 // Advance the display frame. 131 bool expectedRestartDecoder = 132 aExpectedStatus == AnimationFrameBuffer::InsertStatus::YIELD; 133 VerifyAdvance(aQueue, aExpectedFrame, expectedRestartDecoder); 134 } 135 136 static void VerifyMarkComplete( 137 AnimationFrameBuffer& aQueue, bool aExpectedContinue, 138 const gfx::IntRect& aRefreshArea = gfx::IntRect(0, 0, 1, 1)) { 139 if (aQueue.IsRecycling() && !aQueue.SizeKnown()) { 140 const AnimationFrameRecyclingQueue& queue = 141 *static_cast<AnimationFrameRecyclingQueue*>(&aQueue); 142 EXPECT_EQ(queue.FirstFrame()->GetRect(), queue.FirstFrameRefreshArea()); 143 } 144 145 bool keepDecoding = aQueue.MarkComplete(aRefreshArea); 146 EXPECT_EQ(aExpectedContinue, keepDecoding); 147 148 if (aQueue.IsRecycling()) { 149 const AnimationFrameRecyclingQueue& queue = 150 *static_cast<AnimationFrameRecyclingQueue*>(&aQueue); 151 EXPECT_EQ(aRefreshArea, queue.FirstFrameRefreshArea()); 152 } 153 } 154 155 static void VerifyInsert(AnimationFrameBuffer& aQueue, 156 AnimationFrameBuffer::InsertStatus aExpectedStatus) { 157 RefPtr<imgFrame> frame = CreateEmptyFrame(); 158 AnimationFrameBuffer::InsertStatus status = 159 aQueue.Insert(RefPtr<imgFrame>(frame)); 160 EXPECT_EQ(aExpectedStatus, status); 161 EXPECT_TRUE(aQueue.IsLastInsertedFrame(frame)); 162 VerifyInsertInternal(aQueue, frame); 163 } 164 165 static void VerifyReset(AnimationFrameBuffer& aQueue, bool aExpectedContinue, 166 const imgFrame* aFirstFrame) { 167 bool keepDecoding = aQueue.Reset(); 168 EXPECT_EQ(aExpectedContinue, keepDecoding); 169 EXPECT_EQ(aQueue.Batch() * 2, aQueue.PendingDecode()); 170 EXPECT_EQ(aFirstFrame, aQueue.Get(0, true)); 171 172 if (!aQueue.MayDiscard()) { 173 const AnimationFrameRetainedBuffer& queue = 174 *static_cast<AnimationFrameRetainedBuffer*>(&aQueue); 175 EXPECT_EQ(aFirstFrame, queue.Frames()[0].get()); 176 EXPECT_EQ(aFirstFrame, aQueue.Get(0, false)); 177 } else { 178 const AnimationFrameDiscardingQueue& queue = 179 *static_cast<AnimationFrameDiscardingQueue*>(&aQueue); 180 EXPECT_EQ(size_t(0), queue.PendingInsert()); 181 EXPECT_EQ(size_t(0), queue.Display().size()); 182 EXPECT_EQ(aFirstFrame, queue.FirstFrame()); 183 EXPECT_EQ(nullptr, aQueue.Get(0, false)); 184 } 185 } 186 187 class ImageAnimationFrameBuffer : public ::testing::Test { 188 public: 189 ImageAnimationFrameBuffer() {} 190 191 private: 192 AutoInitializeImageLib mInit; 193 }; 194 195 TEST_F(ImageAnimationFrameBuffer, RetainedInitialState) { 196 const size_t kThreshold = 800; 197 const size_t kBatch = 100; 198 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0); 199 200 EXPECT_EQ(kThreshold, buffer.Threshold()); 201 EXPECT_EQ(kBatch, buffer.Batch()); 202 EXPECT_EQ(size_t(0), buffer.Displayed()); 203 EXPECT_EQ(kBatch * 2, buffer.PendingDecode()); 204 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 205 EXPECT_FALSE(buffer.MayDiscard()); 206 EXPECT_FALSE(buffer.SizeKnown()); 207 EXPECT_EQ(size_t(0), buffer.Size()); 208 } 209 210 TEST_F(ImageAnimationFrameBuffer, ThresholdTooSmall) { 211 const size_t kThreshold = 0; 212 const size_t kBatch = 10; 213 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0); 214 215 EXPECT_EQ(kBatch * 2 + 1, buffer.Threshold()); 216 EXPECT_EQ(kBatch, buffer.Batch()); 217 EXPECT_EQ(kBatch * 2, buffer.PendingDecode()); 218 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 219 } 220 221 TEST_F(ImageAnimationFrameBuffer, BatchTooSmall) { 222 const size_t kThreshold = 10; 223 const size_t kBatch = 0; 224 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0); 225 226 EXPECT_EQ(kThreshold, buffer.Threshold()); 227 EXPECT_EQ(size_t(1), buffer.Batch()); 228 EXPECT_EQ(size_t(2), buffer.PendingDecode()); 229 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 230 } 231 232 TEST_F(ImageAnimationFrameBuffer, BatchTooBig) { 233 const size_t kThreshold = 50; 234 const size_t kBatch = SIZE_MAX; 235 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0); 236 237 // The rounding is important here (e.g. SIZE_MAX/4 * 2 != SIZE_MAX/2). 238 EXPECT_EQ(SIZE_MAX / 4, buffer.Batch()); 239 EXPECT_EQ(buffer.Batch() * 2 + 1, buffer.Threshold()); 240 EXPECT_EQ(buffer.Batch() * 2, buffer.PendingDecode()); 241 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 242 } 243 244 TEST_F(ImageAnimationFrameBuffer, FinishUnderBatchAndThreshold) { 245 const size_t kThreshold = 30; 246 const size_t kBatch = 10; 247 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0); 248 const auto& frames = buffer.Frames(); 249 250 EXPECT_EQ(kBatch * 2, buffer.PendingDecode()); 251 252 RefPtr<imgFrame> firstFrame; 253 for (size_t i = 0; i < 5; ++i) { 254 RefPtr<imgFrame> frame = CreateEmptyFrame(); 255 auto status = buffer.Insert(RefPtr<imgFrame>(frame)); 256 EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE); 257 EXPECT_FALSE(buffer.SizeKnown()); 258 EXPECT_EQ(buffer.Size(), i + 1); 259 260 if (i == 4) { 261 EXPECT_EQ(size_t(15), buffer.PendingDecode()); 262 bool keepDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1)); 263 EXPECT_FALSE(keepDecoding); 264 EXPECT_TRUE(buffer.SizeKnown()); 265 EXPECT_EQ(size_t(0), buffer.PendingDecode()); 266 EXPECT_FALSE(buffer.HasRedecodeError()); 267 } 268 269 EXPECT_FALSE(buffer.MayDiscard()); 270 271 imgFrame* gotFrame = buffer.Get(i, false); 272 EXPECT_EQ(frame.get(), gotFrame); 273 ASSERT_EQ(i + 1, frames.Length()); 274 EXPECT_EQ(frame.get(), frames[i].get()); 275 276 if (i == 0) { 277 firstFrame = std::move(frame); 278 EXPECT_EQ(size_t(0), buffer.Displayed()); 279 } else { 280 EXPECT_EQ(i - 1, buffer.Displayed()); 281 bool restartDecoder = buffer.AdvanceTo(i); 282 EXPECT_FALSE(restartDecoder); 283 EXPECT_EQ(i, buffer.Displayed()); 284 } 285 286 gotFrame = buffer.Get(0, false); 287 EXPECT_EQ(firstFrame.get(), gotFrame); 288 } 289 290 // Loop again over the animation and make sure it is still all there. 291 for (size_t i = 0; i < frames.Length(); ++i) { 292 EXPECT_TRUE(buffer.Get(i, false) != nullptr); 293 294 bool restartDecoder = buffer.AdvanceTo(i); 295 EXPECT_FALSE(restartDecoder); 296 } 297 } 298 299 TEST_F(ImageAnimationFrameBuffer, FinishMultipleBatchesUnderThreshold) { 300 const size_t kThreshold = 30; 301 const size_t kBatch = 2; 302 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0); 303 const auto& frames = buffer.Frames(); 304 305 EXPECT_EQ(kBatch * 2, buffer.PendingDecode()); 306 307 // Add frames until it tells us to stop. 308 AnimationFrameBuffer::InsertStatus status; 309 do { 310 status = buffer.Insert(CreateEmptyFrame()); 311 EXPECT_FALSE(buffer.SizeKnown()); 312 EXPECT_FALSE(buffer.MayDiscard()); 313 } while (status == AnimationFrameBuffer::InsertStatus::CONTINUE); 314 315 EXPECT_EQ(size_t(0), buffer.PendingDecode()); 316 EXPECT_EQ(size_t(4), frames.Length()); 317 EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::YIELD); 318 319 // Progress through the animation until it lets us decode again. 320 bool restartDecoder = false; 321 size_t i = 0; 322 do { 323 EXPECT_TRUE(buffer.Get(i, false) != nullptr); 324 if (i > 0) { 325 restartDecoder = buffer.AdvanceTo(i); 326 } 327 ++i; 328 } while (!restartDecoder); 329 330 EXPECT_EQ(size_t(2), buffer.PendingDecode()); 331 EXPECT_EQ(size_t(2), buffer.Displayed()); 332 333 // Add the last frame. 334 status = buffer.Insert(CreateEmptyFrame()); 335 EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE); 336 bool keepDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1)); 337 EXPECT_FALSE(keepDecoding); 338 EXPECT_TRUE(buffer.SizeKnown()); 339 EXPECT_EQ(size_t(0), buffer.PendingDecode()); 340 EXPECT_EQ(size_t(5), frames.Length()); 341 EXPECT_FALSE(buffer.HasRedecodeError()); 342 343 // Finish progressing through the animation. 344 for (; i < frames.Length(); ++i) { 345 EXPECT_TRUE(buffer.Get(i, false) != nullptr); 346 restartDecoder = buffer.AdvanceTo(i); 347 EXPECT_FALSE(restartDecoder); 348 } 349 350 // Loop again over the animation and make sure it is still all there. 351 for (i = 0; i < frames.Length(); ++i) { 352 EXPECT_TRUE(buffer.Get(i, false) != nullptr); 353 restartDecoder = buffer.AdvanceTo(i); 354 EXPECT_FALSE(restartDecoder); 355 } 356 357 // Loop to the third frame and then reset the animation. 358 for (i = 0; i < 3; ++i) { 359 EXPECT_TRUE(buffer.Get(i, false) != nullptr); 360 restartDecoder = buffer.AdvanceTo(i); 361 EXPECT_FALSE(restartDecoder); 362 } 363 364 // Since we are below the threshold, we can reset the get index only. 365 // Nothing else should have changed. 366 restartDecoder = buffer.Reset(); 367 EXPECT_FALSE(restartDecoder); 368 for (i = 0; i < 5; ++i) { 369 EXPECT_TRUE(buffer.Get(i, false) != nullptr); 370 } 371 EXPECT_EQ(size_t(0), buffer.PendingDecode()); 372 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 373 EXPECT_EQ(size_t(0), buffer.Displayed()); 374 } 375 376 TEST_F(ImageAnimationFrameBuffer, StartAfterBeginning) { 377 const size_t kThreshold = 30; 378 const size_t kBatch = 2; 379 const size_t kStartFrame = 7; 380 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, kStartFrame); 381 382 EXPECT_EQ(kStartFrame, buffer.PendingAdvance()); 383 384 // Add frames until it tells us to stop. It should be later than before, 385 // because it auto-advances until its displayed frame is kStartFrame. 386 AnimationFrameBuffer::InsertStatus status; 387 size_t i = 0; 388 do { 389 status = buffer.Insert(CreateEmptyFrame()); 390 EXPECT_FALSE(buffer.SizeKnown()); 391 EXPECT_FALSE(buffer.MayDiscard()); 392 393 if (i <= kStartFrame) { 394 EXPECT_EQ(i, buffer.Displayed()); 395 EXPECT_EQ(kStartFrame - i, buffer.PendingAdvance()); 396 } else { 397 EXPECT_EQ(kStartFrame, buffer.Displayed()); 398 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 399 } 400 401 i++; 402 } while (status == AnimationFrameBuffer::InsertStatus::CONTINUE); 403 404 EXPECT_EQ(size_t(0), buffer.PendingDecode()); 405 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 406 EXPECT_EQ(size_t(10), buffer.Size()); 407 } 408 409 TEST_F(ImageAnimationFrameBuffer, StartAfterBeginningAndReset) { 410 const size_t kThreshold = 30; 411 const size_t kBatch = 2; 412 const size_t kStartFrame = 7; 413 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, kStartFrame); 414 415 EXPECT_EQ(kStartFrame, buffer.PendingAdvance()); 416 417 // Add frames until it tells us to stop. It should be later than before, 418 // because it auto-advances until its displayed frame is kStartFrame. 419 for (size_t i = 0; i < 5; ++i) { 420 AnimationFrameBuffer::InsertStatus status = 421 buffer.Insert(CreateEmptyFrame()); 422 EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE); 423 EXPECT_FALSE(buffer.SizeKnown()); 424 EXPECT_FALSE(buffer.MayDiscard()); 425 EXPECT_EQ(i, buffer.Displayed()); 426 EXPECT_EQ(kStartFrame - i, buffer.PendingAdvance()); 427 } 428 429 // When we reset the animation, it goes back to the beginning. That means 430 // we can forget about what we were told to advance to at the start. While 431 // we have plenty of frames in our buffer, we still need one more because 432 // in the real scenario, the decoder thread is still running and it is easier 433 // to let it insert its last frame than to coordinate quitting earlier. 434 buffer.Reset(); 435 EXPECT_EQ(size_t(0), buffer.Displayed()); 436 EXPECT_EQ(size_t(1), buffer.PendingDecode()); 437 EXPECT_EQ(size_t(0), buffer.PendingAdvance()); 438 EXPECT_EQ(size_t(5), buffer.Size()); 439 } 440 441 static void TestDiscardingQueueLoop(AnimationFrameDiscardingQueue& aQueue, 442 const imgFrame* aFirstFrame, 443 size_t aThreshold, size_t aBatch, 444 size_t aStartFrame) { 445 // We should be advanced right up to the last decoded frame. 446 EXPECT_TRUE(aQueue.MayDiscard()); 447 EXPECT_FALSE(aQueue.SizeKnown()); 448 EXPECT_EQ(aBatch, aQueue.Batch()); 449 EXPECT_EQ(aThreshold, aQueue.PendingInsert()); 450 EXPECT_EQ(aThreshold, aQueue.Size()); 451 EXPECT_EQ(aFirstFrame, aQueue.FirstFrame()); 452 EXPECT_EQ(size_t(1), aQueue.Display().size()); 453 EXPECT_EQ(size_t(3), aQueue.PendingDecode()); 454 VerifyDiscardingQueueContents(aQueue); 455 456 // Make sure frames get removed as we advance. 457 VerifyInsertAndAdvance(aQueue, 5, 458 AnimationFrameBuffer::InsertStatus::CONTINUE); 459 EXPECT_EQ(size_t(1), aQueue.Display().size()); 460 VerifyInsertAndAdvance(aQueue, 6, 461 AnimationFrameBuffer::InsertStatus::CONTINUE); 462 EXPECT_EQ(size_t(1), aQueue.Display().size()); 463 464 // We actually will yield if we are recycling instead of continuing because 465 // the pending calculation is slightly different. We will actually request one 466 // less frame than we have to recycle. 467 if (aQueue.IsRecycling()) { 468 VerifyInsertAndAdvance(aQueue, 7, 469 AnimationFrameBuffer::InsertStatus::YIELD); 470 } else { 471 VerifyInsertAndAdvance(aQueue, 7, 472 AnimationFrameBuffer::InsertStatus::CONTINUE); 473 } 474 EXPECT_EQ(size_t(1), aQueue.Display().size()); 475 476 // We should get throttled if we insert too much. 477 VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::CONTINUE); 478 EXPECT_EQ(size_t(2), aQueue.Display().size()); 479 EXPECT_EQ(size_t(1), aQueue.PendingDecode()); 480 VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::YIELD); 481 EXPECT_EQ(size_t(3), aQueue.Display().size()); 482 EXPECT_EQ(size_t(0), aQueue.PendingDecode()); 483 484 // We should get restarted if we advance. 485 VerifyAdvance(aQueue, 8, true); 486 EXPECT_EQ(size_t(2), aQueue.PendingDecode()); 487 VerifyAdvance(aQueue, 9, false); 488 EXPECT_EQ(size_t(2), aQueue.PendingDecode()); 489 490 // We should continue decoding if we completed, since we are discarding. 491 VerifyMarkComplete(aQueue, true); 492 EXPECT_EQ(size_t(2), aQueue.PendingDecode()); 493 EXPECT_EQ(size_t(10), aQueue.Size()); 494 EXPECT_TRUE(aQueue.SizeKnown()); 495 EXPECT_FALSE(aQueue.HasRedecodeError()); 496 497 // Insert the first frames of the animation. 498 VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::CONTINUE); 499 VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::YIELD); 500 EXPECT_EQ(size_t(0), aQueue.PendingDecode()); 501 EXPECT_EQ(size_t(10), aQueue.Size()); 502 503 // Advance back at the beginning. The first frame should only match for 504 // display purposes. 505 VerifyAdvance(aQueue, 0, true); 506 EXPECT_EQ(size_t(2), aQueue.PendingDecode()); 507 EXPECT_TRUE(aQueue.FirstFrame() != nullptr); 508 EXPECT_TRUE(aQueue.Get(0, false) != nullptr); 509 EXPECT_NE(aQueue.FirstFrame(), aQueue.Get(0, false)); 510 EXPECT_EQ(aQueue.FirstFrame(), aQueue.Get(0, true)); 511 512 // Reiterate one more time and make it loops back. 513 VerifyInsertAndAdvance(aQueue, 1, 514 AnimationFrameBuffer::InsertStatus::CONTINUE); 515 VerifyInsertAndAdvance(aQueue, 2, AnimationFrameBuffer::InsertStatus::YIELD); 516 VerifyInsertAndAdvance(aQueue, 3, 517 AnimationFrameBuffer::InsertStatus::CONTINUE); 518 VerifyInsertAndAdvance(aQueue, 4, AnimationFrameBuffer::InsertStatus::YIELD); 519 VerifyInsertAndAdvance(aQueue, 5, 520 AnimationFrameBuffer::InsertStatus::CONTINUE); 521 VerifyInsertAndAdvance(aQueue, 6, AnimationFrameBuffer::InsertStatus::YIELD); 522 VerifyInsertAndAdvance(aQueue, 7, 523 AnimationFrameBuffer::InsertStatus::CONTINUE); 524 VerifyInsertAndAdvance(aQueue, 8, AnimationFrameBuffer::InsertStatus::YIELD); 525 526 EXPECT_EQ(size_t(10), aQueue.PendingInsert()); 527 VerifyMarkComplete(aQueue, true); 528 EXPECT_EQ(size_t(0), aQueue.PendingInsert()); 529 530 VerifyInsertAndAdvance(aQueue, 9, 531 AnimationFrameBuffer::InsertStatus::CONTINUE); 532 VerifyInsertAndAdvance(aQueue, 0, AnimationFrameBuffer::InsertStatus::YIELD); 533 VerifyInsertAndAdvance(aQueue, 1, 534 AnimationFrameBuffer::InsertStatus::CONTINUE); 535 } 536 537 TEST_F(ImageAnimationFrameBuffer, DiscardingLoop) { 538 const size_t kThreshold = 5; 539 const size_t kBatch = 2; 540 const size_t kStartFrame = 0; 541 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 542 PrepareForDiscardingQueue(retained); 543 const imgFrame* firstFrame = retained.Frames()[0].get(); 544 AnimationFrameDiscardingQueue buffer(std::move(retained)); 545 TestDiscardingQueueLoop(buffer, firstFrame, kThreshold, kBatch, kStartFrame); 546 } 547 548 TEST_F(ImageAnimationFrameBuffer, RecyclingLoop) { 549 const size_t kThreshold = 5; 550 const size_t kBatch = 2; 551 const size_t kStartFrame = 0; 552 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 553 PrepareForDiscardingQueue(retained); 554 const imgFrame* firstFrame = retained.Frames()[0].get(); 555 AnimationFrameRecyclingQueue buffer(std::move(retained)); 556 557 // We should not start with any recycled frames. 558 ASSERT_TRUE(buffer.Recycle().empty()); 559 560 TestDiscardingQueueLoop(buffer, firstFrame, kThreshold, kBatch, kStartFrame); 561 562 // All the frames we inserted should have been recycleable. 563 ASSERT_FALSE(buffer.Recycle().empty()); 564 while (!buffer.Recycle().empty()) { 565 gfx::IntRect expectedRect(0, 0, 1, 1); 566 RefPtr<imgFrame> expectedFrame = buffer.Recycle().front().mFrame; 567 EXPECT_FALSE(expectedRect.IsEmpty()); 568 EXPECT_TRUE(expectedFrame.get() != nullptr); 569 570 gfx::IntRect gotRect; 571 RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect); 572 EXPECT_EQ(expectedFrame.get(), gotFrame.get()); 573 EXPECT_EQ(expectedRect, gotRect); 574 EXPECT_TRUE(ReinitForRecycle(gotFrame)); 575 } 576 577 // Trying to pull a recycled frame when we have nothing should be safe too. 578 gfx::IntRect gotRect; 579 RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect); 580 EXPECT_TRUE(gotFrame.get() == nullptr); 581 EXPECT_FALSE(ReinitForRecycle(gotFrame)); 582 } 583 584 static void TestDiscardingQueueReset(AnimationFrameDiscardingQueue& aQueue, 585 const imgFrame* aFirstFrame, 586 size_t aThreshold, size_t aBatch, 587 size_t aStartFrame) { 588 // We should be advanced right up to the last decoded frame. 589 EXPECT_TRUE(aQueue.MayDiscard()); 590 EXPECT_FALSE(aQueue.SizeKnown()); 591 EXPECT_EQ(aBatch, aQueue.Batch()); 592 EXPECT_EQ(aThreshold, aQueue.PendingInsert()); 593 EXPECT_EQ(aThreshold, aQueue.Size()); 594 EXPECT_EQ(aFirstFrame, aQueue.FirstFrame()); 595 EXPECT_EQ(size_t(1), aQueue.Display().size()); 596 EXPECT_EQ(size_t(4), aQueue.PendingDecode()); 597 VerifyDiscardingQueueContents(aQueue); 598 599 // Reset should clear everything except the first frame. 600 VerifyReset(aQueue, false, aFirstFrame); 601 } 602 603 TEST_F(ImageAnimationFrameBuffer, DiscardingReset) { 604 const size_t kThreshold = 8; 605 const size_t kBatch = 3; 606 const size_t kStartFrame = 0; 607 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 608 PrepareForDiscardingQueue(retained); 609 const imgFrame* firstFrame = retained.Frames()[0].get(); 610 AnimationFrameDiscardingQueue buffer(std::move(retained)); 611 TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame); 612 } 613 614 TEST_F(ImageAnimationFrameBuffer, ResetBeforeDiscardingThreshold) { 615 const size_t kThreshold = 3; 616 const size_t kBatch = 1; 617 const size_t kStartFrame = 0; 618 619 // Get the starting buffer to just before the point where we need to switch 620 // to a discarding buffer, reset the animation so advancing points at the 621 // first frame, and insert the last frame to cross the threshold. 622 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 623 VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE); 624 VerifyInsertAndAdvance(retained, 1, 625 AnimationFrameBuffer::InsertStatus::YIELD); 626 bool restartDecoder = retained.Reset(); 627 EXPECT_FALSE(restartDecoder); 628 VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD); 629 630 const imgFrame* firstFrame = retained.Frames()[0].get(); 631 EXPECT_TRUE(firstFrame != nullptr); 632 AnimationFrameDiscardingQueue buffer(std::move(retained)); 633 const imgFrame* displayFirstFrame = buffer.Get(0, true); 634 const imgFrame* advanceFirstFrame = buffer.Get(0, false); 635 EXPECT_EQ(firstFrame, displayFirstFrame); 636 EXPECT_EQ(firstFrame, advanceFirstFrame); 637 } 638 639 TEST_F(ImageAnimationFrameBuffer, DiscardingTooFewFrames) { 640 const size_t kThreshold = 3; 641 const size_t kBatch = 1; 642 const size_t kStartFrame = 0; 643 644 // First get us to a discarding buffer state. 645 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 646 VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE); 647 VerifyInsertAndAdvance(retained, 1, 648 AnimationFrameBuffer::InsertStatus::YIELD); 649 VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD); 650 651 // Insert one more frame. 652 AnimationFrameDiscardingQueue buffer(std::move(retained)); 653 VerifyAdvance(buffer, 2, true); 654 VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD); 655 656 // Mark it as complete. 657 bool restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1)); 658 EXPECT_FALSE(restartDecoder); 659 EXPECT_FALSE(buffer.HasRedecodeError()); 660 661 // Insert one fewer frame than before. 662 VerifyAdvance(buffer, 3, true); 663 VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD); 664 VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD); 665 VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD); 666 667 // When we mark it as complete, it should fail due to too few frames. 668 restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1)); 669 EXPECT_TRUE(buffer.HasRedecodeError()); 670 EXPECT_EQ(size_t(0), buffer.PendingDecode()); 671 EXPECT_EQ(size_t(4), buffer.Size()); 672 } 673 674 TEST_F(ImageAnimationFrameBuffer, DiscardingTooManyFrames) { 675 const size_t kThreshold = 3; 676 const size_t kBatch = 1; 677 const size_t kStartFrame = 0; 678 679 // First get us to a discarding buffer state. 680 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 681 VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE); 682 VerifyInsertAndAdvance(retained, 1, 683 AnimationFrameBuffer::InsertStatus::YIELD); 684 VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD); 685 686 // Insert one more frame. 687 AnimationFrameDiscardingQueue buffer(std::move(retained)); 688 VerifyAdvance(buffer, 2, true); 689 VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD); 690 691 // Mark it as complete. 692 bool restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1)); 693 EXPECT_FALSE(restartDecoder); 694 EXPECT_FALSE(buffer.HasRedecodeError()); 695 696 // Advance and insert to get us back to the end on the redecode. 697 VerifyAdvance(buffer, 3, true); 698 VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD); 699 VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD); 700 VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD); 701 VerifyInsertAndAdvance(buffer, 3, AnimationFrameBuffer::InsertStatus::YIELD); 702 703 // Attempt to insert a 5th frame, it should fail. 704 RefPtr<imgFrame> frame = CreateEmptyFrame(); 705 AnimationFrameBuffer::InsertStatus status = buffer.Insert(std::move(frame)); 706 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 707 EXPECT_TRUE(buffer.HasRedecodeError()); 708 EXPECT_EQ(size_t(0), buffer.PendingDecode()); 709 EXPECT_EQ(size_t(4), buffer.Size()); 710 } 711 712 TEST_F(ImageAnimationFrameBuffer, RecyclingReset) { 713 const size_t kThreshold = 8; 714 const size_t kBatch = 3; 715 const size_t kStartFrame = 0; 716 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 717 PrepareForDiscardingQueue(retained); 718 const imgFrame* firstFrame = retained.Frames()[0].get(); 719 AnimationFrameRecyclingQueue buffer(std::move(retained)); 720 TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame); 721 } 722 723 TEST_F(ImageAnimationFrameBuffer, RecyclingResetBeforeComplete) { 724 const size_t kThreshold = 3; 725 const size_t kBatch = 1; 726 const size_t kStartFrame = 0; 727 const gfx::IntSize kImageSize(100, 100); 728 const gfx::IntRect kImageRect(gfx::IntPoint(0, 0), kImageSize); 729 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 730 731 // Get the starting buffer to just before the point where we need to switch 732 // to a discarding buffer, reset the animation so advancing points at the 733 // first frame, and insert the last frame to cross the threshold. 734 RefPtr<imgFrame> frame; 735 frame = CreateEmptyFrame(kImageSize, kImageRect, false); 736 AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame)); 737 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status); 738 739 frame = CreateEmptyFrame( 740 kImageSize, gfx::IntRect(gfx::IntPoint(10, 10), gfx::IntSize(1, 1)), 741 false); 742 status = retained.Insert(std::move(frame)); 743 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 744 745 VerifyAdvance(retained, 1, true); 746 747 frame = CreateEmptyFrame( 748 kImageSize, gfx::IntRect(gfx::IntPoint(20, 10), gfx::IntSize(1, 1)), 749 false); 750 status = retained.Insert(std::move(frame)); 751 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_YIELD, status); 752 753 AnimationFrameRecyclingQueue buffer(std::move(retained)); 754 bool restartDecoding = buffer.Reset(); 755 EXPECT_TRUE(restartDecoding); 756 757 // None of the buffers were recyclable. 758 EXPECT_FALSE(buffer.Recycle().empty()); 759 while (!buffer.Recycle().empty()) { 760 gfx::IntRect recycleRect; 761 RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect); 762 EXPECT_FALSE(frameRef); 763 } 764 765 // Reinsert the first two frames as recyclable and reset again. 766 frame = CreateEmptyFrame(kImageSize, kImageRect, true); 767 status = buffer.Insert(std::move(frame)); 768 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status); 769 770 frame = CreateEmptyFrame( 771 kImageSize, gfx::IntRect(gfx::IntPoint(10, 10), gfx::IntSize(1, 1)), 772 true); 773 status = buffer.Insert(std::move(frame)); 774 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 775 776 restartDecoding = buffer.Reset(); 777 EXPECT_TRUE(restartDecoding); 778 779 // Now both buffers should have been saved and the dirty rect replaced with 780 // the full image rect since we don't know the first frame refresh area yet. 781 EXPECT_EQ(size_t(2), buffer.Recycle().size()); 782 for (const auto& entry : buffer.Recycle()) { 783 EXPECT_EQ(kImageRect, entry.mDirtyRect); 784 } 785 } 786 787 TEST_F(ImageAnimationFrameBuffer, RecyclingRect) { 788 const size_t kThreshold = 5; 789 const size_t kBatch = 2; 790 const size_t kStartFrame = 0; 791 const gfx::IntSize kImageSize(100, 100); 792 const gfx::IntRect kImageRect(gfx::IntPoint(0, 0), kImageSize); 793 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame); 794 795 // Let's get to the recycling state while marking all of the frames as not 796 // recyclable, just like AnimationFrameBuffer / the decoders would do. 797 RefPtr<imgFrame> frame; 798 frame = CreateEmptyFrame(kImageSize, kImageRect, false); 799 AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame)); 800 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status); 801 802 frame = CreateEmptyFrame(kImageSize, kImageRect, false); 803 status = retained.Insert(std::move(frame)); 804 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status); 805 806 frame = CreateEmptyFrame(kImageSize, kImageRect, false); 807 status = retained.Insert(std::move(frame)); 808 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status); 809 810 frame = CreateEmptyFrame(kImageSize, kImageRect, false); 811 status = retained.Insert(std::move(frame)); 812 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 813 814 VerifyAdvance(retained, 1, false); 815 VerifyAdvance(retained, 2, true); 816 VerifyAdvance(retained, 3, false); 817 818 frame = CreateEmptyFrame(kImageSize, kImageRect, false); 819 status = retained.Insert(std::move(frame)); 820 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE, status); 821 822 AnimationFrameRecyclingQueue buffer(std::move(retained)); 823 824 // The first frame is now the candidate for recycling. Since it was marked as 825 // not recyclable, we should get nothing. 826 VerifyAdvance(buffer, 4, false); 827 828 gfx::IntRect recycleRect; 829 EXPECT_FALSE(buffer.Recycle().empty()); 830 RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect); 831 EXPECT_FALSE(frameRef); 832 EXPECT_TRUE(buffer.Recycle().empty()); 833 834 // Insert a recyclable partial frame. Its dirty rect shouldn't matter since 835 // the previous frame was not recyclable. 836 frame = CreateEmptyFrame(kImageSize, gfx::IntRect(0, 0, 25, 25)); 837 status = buffer.Insert(std::move(frame)); 838 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 839 840 VerifyAdvance(buffer, 5, true); 841 EXPECT_FALSE(buffer.Recycle().empty()); 842 frameRef = buffer.RecycleFrame(recycleRect); 843 EXPECT_FALSE(frameRef); 844 EXPECT_TRUE(buffer.Recycle().empty()); 845 846 // Insert a recyclable partial frame. Its dirty rect should match the recycle 847 // rect since it is the only frame in the buffer. 848 frame = CreateEmptyFrame(kImageSize, gfx::IntRect(25, 0, 50, 50)); 849 status = buffer.Insert(std::move(frame)); 850 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 851 852 VerifyAdvance(buffer, 6, true); 853 EXPECT_FALSE(buffer.Recycle().empty()); 854 frameRef = buffer.RecycleFrame(recycleRect); 855 EXPECT_TRUE(frameRef); 856 EXPECT_TRUE(ReinitForRecycle(frameRef)); 857 EXPECT_EQ(gfx::IntRect(25, 0, 50, 50), recycleRect); 858 EXPECT_TRUE(buffer.Recycle().empty()); 859 860 // Insert the last frame and mark us as complete. The next recycled frame is 861 // producing the first frame again, so we should use the first frame refresh 862 // area instead of its dirty rect. 863 frame = CreateEmptyFrame(kImageSize, gfx::IntRect(10, 10, 60, 10)); 864 status = buffer.Insert(std::move(frame)); 865 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 866 867 bool continueDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 75, 50)); 868 EXPECT_FALSE(continueDecoding); 869 870 VerifyAdvance(buffer, 7, true); 871 EXPECT_FALSE(buffer.Recycle().empty()); 872 frameRef = buffer.RecycleFrame(recycleRect); 873 EXPECT_TRUE(frameRef); 874 EXPECT_TRUE(ReinitForRecycle(frameRef)); 875 EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect); 876 EXPECT_TRUE(buffer.Recycle().empty()); 877 878 // Now let's reinsert the first frame. The recycle rect should still be the 879 // first frame refresh area instead of the dirty rect of the first frame (e.g. 880 // the full frame). 881 frame = CreateEmptyFrame(kImageSize, kImageRect, false); 882 status = buffer.Insert(std::move(frame)); 883 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status); 884 885 VerifyAdvance(buffer, 0, true); 886 EXPECT_FALSE(buffer.Recycle().empty()); 887 frameRef = buffer.RecycleFrame(recycleRect); 888 EXPECT_TRUE(frameRef); 889 EXPECT_TRUE(ReinitForRecycle(frameRef)); 890 EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect); 891 EXPECT_TRUE(buffer.Recycle().empty()); 892 }