Bug 1885565 - Part 1: Add mozac_ic_avatar_circle_24 to ui-icons r=android-reviewers...
[gecko.git] / image / test / gtest / TestAnimationFrameBuffer.cpp
blob78186e5066513c6fad2de05fc060d1a25a4f5fd8
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/. */
6 #include <utility>
8 #include "AnimationFrameBuffer.h"
9 #include "Common.h"
10 #include "gtest/gtest.h"
12 using namespace mozilla;
13 using namespace mozilla::image;
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();
35 static bool ReinitForRecycle(RawAccessFrameRef& aFrame) {
36 if (!aFrame) {
37 return false;
40 AnimationParams animParams{aFrame->GetRect(), FrameTimeout::Forever(),
41 /* aFrameNum */ 1, BlendMethod::OVER,
42 DisposalMethod::NOT_SPECIFIED};
43 return NS_SUCCEEDED(aFrame->InitForDecoderRecycle(animParams));
46 static void PrepareForDiscardingQueue(AnimationFrameRetainedBuffer& aQueue) {
47 ASSERT_EQ(size_t(0), aQueue.Size());
48 ASSERT_LT(size_t(1), aQueue.Batch());
50 AnimationFrameBuffer::InsertStatus status = aQueue.Insert(CreateEmptyFrame());
51 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
53 while (true) {
54 status = aQueue.Insert(CreateEmptyFrame());
55 bool restartDecoder = aQueue.AdvanceTo(aQueue.Size() - 1);
56 EXPECT_FALSE(restartDecoder);
58 if (status == AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE) {
59 break;
61 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
64 EXPECT_EQ(aQueue.Threshold(), aQueue.Size());
67 static void VerifyDiscardingQueueContents(
68 AnimationFrameDiscardingQueue& aQueue) {
69 auto frames = aQueue.Display();
70 for (auto i : frames) {
71 EXPECT_TRUE(i != nullptr);
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;
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());
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();
105 bool restartDecoder = aQueue.AdvanceTo(aExpectedFrame);
106 EXPECT_EQ(aExpectedRestartDecoder, restartDecoder);
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());
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);
130 // Advance the display frame.
131 bool expectedRestartDecoder =
132 aExpectedStatus == AnimationFrameBuffer::InsertStatus::YIELD;
133 VerifyAdvance(aQueue, aExpectedFrame, expectedRestartDecoder);
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());
145 bool keepDecoding = aQueue.MarkComplete(aRefreshArea);
146 EXPECT_EQ(aExpectedContinue, keepDecoding);
148 if (aQueue.IsRecycling()) {
149 const AnimationFrameRecyclingQueue& queue =
150 *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
151 EXPECT_EQ(aRefreshArea, queue.FirstFrameRefreshArea());
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);
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));
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));
187 class ImageAnimationFrameBuffer : public ::testing::Test {
188 public:
189 ImageAnimationFrameBuffer() {}
191 private:
192 AutoInitializeImageLib mInit;
195 TEST_F(ImageAnimationFrameBuffer, RetainedInitialState) {
196 const size_t kThreshold = 800;
197 const size_t kBatch = 100;
198 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
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());
210 TEST_F(ImageAnimationFrameBuffer, ThresholdTooSmall) {
211 const size_t kThreshold = 0;
212 const size_t kBatch = 10;
213 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
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());
221 TEST_F(ImageAnimationFrameBuffer, BatchTooSmall) {
222 const size_t kThreshold = 10;
223 const size_t kBatch = 0;
224 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
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());
232 TEST_F(ImageAnimationFrameBuffer, BatchTooBig) {
233 const size_t kThreshold = 50;
234 const size_t kBatch = SIZE_MAX;
235 AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
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());
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();
250 EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
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);
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());
269 EXPECT_FALSE(buffer.MayDiscard());
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());
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());
286 gotFrame = buffer.Get(0, false);
287 EXPECT_EQ(firstFrame.get(), gotFrame);
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);
294 bool restartDecoder = buffer.AdvanceTo(i);
295 EXPECT_FALSE(restartDecoder);
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();
305 EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
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);
315 EXPECT_EQ(size_t(0), buffer.PendingDecode());
316 EXPECT_EQ(size_t(4), frames.Length());
317 EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::YIELD);
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);
327 ++i;
328 } while (!restartDecoder);
330 EXPECT_EQ(size_t(2), buffer.PendingDecode());
331 EXPECT_EQ(size_t(2), buffer.Displayed());
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());
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);
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);
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);
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);
371 EXPECT_EQ(size_t(0), buffer.PendingDecode());
372 EXPECT_EQ(size_t(0), buffer.PendingAdvance());
373 EXPECT_EQ(size_t(0), buffer.Displayed());
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);
382 EXPECT_EQ(kStartFrame, buffer.PendingAdvance());
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());
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());
401 i++;
402 } while (status == AnimationFrameBuffer::InsertStatus::CONTINUE);
404 EXPECT_EQ(size_t(0), buffer.PendingDecode());
405 EXPECT_EQ(size_t(0), buffer.PendingAdvance());
406 EXPECT_EQ(size_t(10), buffer.Size());
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);
415 EXPECT_EQ(kStartFrame, buffer.PendingAdvance());
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());
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());
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);
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());
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);
474 EXPECT_EQ(size_t(1), aQueue.Display().size());
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());
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());
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());
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());
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));
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);
526 EXPECT_EQ(size_t(10), aQueue.PendingInsert());
527 VerifyMarkComplete(aQueue, true);
528 EXPECT_EQ(size_t(0), aQueue.PendingInsert());
530 VerifyInsertAndAdvance(aQueue, 9,
531 AnimationFrameBuffer::InsertStatus::CONTINUE);
532 VerifyInsertAndAdvance(aQueue, 0, AnimationFrameBuffer::InsertStatus::YIELD);
533 VerifyInsertAndAdvance(aQueue, 1,
534 AnimationFrameBuffer::InsertStatus::CONTINUE);
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);
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));
557 // We should not start with any recycled frames.
558 ASSERT_TRUE(buffer.Recycle().empty());
560 TestDiscardingQueueLoop(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
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);
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));
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));
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);
599 // Reset should clear everything except the first frame.
600 VerifyReset(aQueue, false, aFirstFrame);
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);
614 TEST_F(ImageAnimationFrameBuffer, ResetBeforeDiscardingThreshold) {
615 const size_t kThreshold = 3;
616 const size_t kBatch = 1;
617 const size_t kStartFrame = 0;
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);
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);
639 TEST_F(ImageAnimationFrameBuffer, DiscardingTooFewFrames) {
640 const size_t kThreshold = 3;
641 const size_t kBatch = 1;
642 const size_t kStartFrame = 0;
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);
651 // Insert one more frame.
652 AnimationFrameDiscardingQueue buffer(std::move(retained));
653 VerifyAdvance(buffer, 2, true);
654 VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);
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());
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);
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());
674 TEST_F(ImageAnimationFrameBuffer, DiscardingTooManyFrames) {
675 const size_t kThreshold = 3;
676 const size_t kBatch = 1;
677 const size_t kStartFrame = 0;
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);
686 // Insert one more frame.
687 AnimationFrameDiscardingQueue buffer(std::move(retained));
688 VerifyAdvance(buffer, 2, true);
689 VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);
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());
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);
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());
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);
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);
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);
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);
745 VerifyAdvance(retained, 1, true);
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);
753 AnimationFrameRecyclingQueue buffer(std::move(retained));
754 bool restartDecoding = buffer.Reset();
755 EXPECT_TRUE(restartDecoding);
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_TRUE(frameRef);
763 EXPECT_FALSE(ReinitForRecycle(frameRef));
766 // Reinsert the first two frames as recyclable and reset again.
767 frame = CreateEmptyFrame(kImageSize, kImageRect, true);
768 status = buffer.Insert(std::move(frame));
769 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
771 frame = CreateEmptyFrame(
772 kImageSize, gfx::IntRect(gfx::IntPoint(10, 10), gfx::IntSize(1, 1)),
773 true);
774 status = buffer.Insert(std::move(frame));
775 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
777 restartDecoding = buffer.Reset();
778 EXPECT_TRUE(restartDecoding);
780 // Now both buffers should have been saved and the dirty rect replaced with
781 // the full image rect since we don't know the first frame refresh area yet.
782 EXPECT_EQ(size_t(2), buffer.Recycle().size());
783 for (const auto& entry : buffer.Recycle()) {
784 EXPECT_EQ(kImageRect, entry.mDirtyRect);
788 TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
789 const size_t kThreshold = 5;
790 const size_t kBatch = 2;
791 const size_t kStartFrame = 0;
792 const gfx::IntSize kImageSize(100, 100);
793 const gfx::IntRect kImageRect(gfx::IntPoint(0, 0), kImageSize);
794 AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
796 // Let's get to the recycling state while marking all of the frames as not
797 // recyclable, just like AnimationFrameBuffer / the decoders would do.
798 RefPtr<imgFrame> frame;
799 frame = CreateEmptyFrame(kImageSize, kImageRect, false);
800 AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame));
801 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
803 frame = CreateEmptyFrame(kImageSize, kImageRect, false);
804 status = retained.Insert(std::move(frame));
805 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
807 frame = CreateEmptyFrame(kImageSize, kImageRect, false);
808 status = retained.Insert(std::move(frame));
809 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
811 frame = CreateEmptyFrame(kImageSize, kImageRect, false);
812 status = retained.Insert(std::move(frame));
813 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
815 VerifyAdvance(retained, 1, false);
816 VerifyAdvance(retained, 2, true);
817 VerifyAdvance(retained, 3, false);
819 frame = CreateEmptyFrame(kImageSize, kImageRect, false);
820 status = retained.Insert(std::move(frame));
821 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE, status);
823 AnimationFrameRecyclingQueue buffer(std::move(retained));
825 // The first frame is now the candidate for recycling. Since it was marked as
826 // not recyclable, we should get nothing.
827 VerifyAdvance(buffer, 4, false);
829 gfx::IntRect recycleRect;
830 EXPECT_FALSE(buffer.Recycle().empty());
831 RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
832 EXPECT_TRUE(frameRef);
833 EXPECT_FALSE(ReinitForRecycle(frameRef));
834 EXPECT_TRUE(buffer.Recycle().empty());
836 // Insert a recyclable partial frame. Its dirty rect shouldn't matter since
837 // the previous frame was not recyclable.
838 frame = CreateEmptyFrame(kImageSize, gfx::IntRect(0, 0, 25, 25));
839 status = buffer.Insert(std::move(frame));
840 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
842 VerifyAdvance(buffer, 5, true);
843 EXPECT_FALSE(buffer.Recycle().empty());
844 frameRef = buffer.RecycleFrame(recycleRect);
845 EXPECT_TRUE(frameRef);
846 EXPECT_FALSE(ReinitForRecycle(frameRef));
847 EXPECT_TRUE(buffer.Recycle().empty());
849 // Insert a recyclable partial frame. Its dirty rect should match the recycle
850 // rect since it is the only frame in the buffer.
851 frame = CreateEmptyFrame(kImageSize, gfx::IntRect(25, 0, 50, 50));
852 status = buffer.Insert(std::move(frame));
853 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
855 VerifyAdvance(buffer, 6, true);
856 EXPECT_FALSE(buffer.Recycle().empty());
857 frameRef = buffer.RecycleFrame(recycleRect);
858 EXPECT_TRUE(frameRef);
859 EXPECT_TRUE(ReinitForRecycle(frameRef));
860 EXPECT_EQ(gfx::IntRect(25, 0, 50, 50), recycleRect);
861 EXPECT_TRUE(buffer.Recycle().empty());
863 // Insert the last frame and mark us as complete. The next recycled frame is
864 // producing the first frame again, so we should use the first frame refresh
865 // area instead of its dirty rect.
866 frame = CreateEmptyFrame(kImageSize, gfx::IntRect(10, 10, 60, 10));
867 status = buffer.Insert(std::move(frame));
868 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
870 bool continueDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 75, 50));
871 EXPECT_FALSE(continueDecoding);
873 VerifyAdvance(buffer, 7, true);
874 EXPECT_FALSE(buffer.Recycle().empty());
875 frameRef = buffer.RecycleFrame(recycleRect);
876 EXPECT_TRUE(frameRef);
877 EXPECT_TRUE(ReinitForRecycle(frameRef));
878 EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect);
879 EXPECT_TRUE(buffer.Recycle().empty());
881 // Now let's reinsert the first frame. The recycle rect should still be the
882 // first frame refresh area instead of the dirty rect of the first frame (e.g.
883 // the full frame).
884 frame = CreateEmptyFrame(kImageSize, kImageRect, false);
885 status = buffer.Insert(std::move(frame));
886 EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
888 VerifyAdvance(buffer, 0, true);
889 EXPECT_FALSE(buffer.Recycle().empty());
890 frameRef = buffer.RecycleFrame(recycleRect);
891 EXPECT_TRUE(frameRef);
892 EXPECT_TRUE(ReinitForRecycle(frameRef));
893 EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect);
894 EXPECT_TRUE(buffer.Recycle().empty());