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/. */
8 #include "AnimationFrameBuffer.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
};
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
);
32 return frame
.forget();
35 static bool ReinitForRecycle(RawAccessFrameRef
& aFrame
) {
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
);
54 status
= aQueue
.Insert(CreateEmptyFrame());
55 bool restartDecoder
= aQueue
.AdvanceTo(aQueue
.Size() - 1);
56 EXPECT_FALSE(restartDecoder
);
58 if (status
== AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE
) {
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
,
77 // Determine the frame index where we just inserted the frame.
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;
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
;
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());
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));
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
{
189 ImageAnimationFrameBuffer() {}
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);
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());
277 firstFrame
= std::move(frame
);
278 EXPECT_EQ(size_t(0), buffer
.Displayed());
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
;
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;
323 EXPECT_TRUE(buffer
.Get(i
, false) != nullptr);
325 restartDecoder
= buffer
.AdvanceTo(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
;
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());
397 EXPECT_EQ(kStartFrame
, buffer
.Displayed());
398 EXPECT_EQ(size_t(0), buffer
.PendingAdvance());
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.
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
);
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
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)),
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)),
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)),
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.
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());