Bug 1492664 - update funsize scripts to use TASKCLUSTER_ROOT_URL; r=sfraser
[gecko.git] / image / AnimationSurfaceProvider.cpp
blob38894c11c2c125b91ff308953db5a9319849abcf
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 "AnimationSurfaceProvider.h"
8 #include "gfxPrefs.h"
9 #include "mozilla/gfx/gfxVars.h"
10 #include "nsProxyRelease.h"
12 #include "DecodePool.h"
13 #include "Decoder.h"
15 using namespace mozilla::gfx;
17 namespace mozilla {
18 namespace image {
20 AnimationSurfaceProvider::AnimationSurfaceProvider(
21 NotNull<RasterImage*> aImage, const SurfaceKey& aSurfaceKey,
22 NotNull<Decoder*> aDecoder, size_t aCurrentFrame)
23 : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
24 AvailabilityState::StartAsPlaceholder()),
25 mImage(aImage.get()),
26 mDecodingMutex("AnimationSurfaceProvider::mDecoder"),
27 mDecoder(aDecoder.get()),
28 mFramesMutex("AnimationSurfaceProvider::mFrames") {
29 MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
30 "Use MetadataDecodingTask for metadata decodes");
31 MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
32 "Use DecodedSurfaceProvider for single-frame image decodes");
34 // We may produce paletted surfaces for GIF which means the frames are smaller
35 // than one would expect.
36 size_t pixelSize = !aDecoder->ShouldBlendAnimation() &&
37 aDecoder->GetType() == DecoderType::GIF
38 ? sizeof(uint8_t)
39 : sizeof(uint32_t);
41 // Calculate how many frames we need to decode in this animation before we
42 // enter decode-on-demand mode.
43 IntSize frameSize = aSurfaceKey.Size();
44 size_t threshold =
45 (size_t(gfxPrefs::ImageAnimatedDecodeOnDemandThresholdKB()) * 1024) /
46 (pixelSize * frameSize.width * frameSize.height);
47 size_t batch = gfxPrefs::ImageAnimatedDecodeOnDemandBatchSize();
49 mFrames.reset(
50 new AnimationFrameRetainedBuffer(threshold, batch, aCurrentFrame));
53 AnimationSurfaceProvider::~AnimationSurfaceProvider() {
54 DropImageReference();
56 if (mDecoder) {
57 mDecoder->SetFrameRecycler(nullptr);
61 void AnimationSurfaceProvider::DropImageReference() {
62 if (!mImage) {
63 return; // Nothing to do.
66 // RasterImage objects need to be destroyed on the main thread.
67 NS_ReleaseOnMainThreadSystemGroup("AnimationSurfaceProvider::mImage",
68 mImage.forget());
71 void AnimationSurfaceProvider::Reset() {
72 // We want to go back to the beginning.
73 bool mayDiscard;
74 bool restartDecoder = false;
77 MutexAutoLock lock(mFramesMutex);
79 // If we have not crossed the threshold, we know we haven't discarded any
80 // frames, and thus we know it is safe move our display index back to the
81 // very beginning. It would be cleaner to let the frame buffer make this
82 // decision inside the AnimationFrameBuffer::Reset method, but if we have
83 // crossed the threshold, we need to hold onto the decoding mutex too. We
84 // should avoid blocking the main thread on the decoder threads.
85 mayDiscard = mFrames->MayDiscard();
86 if (!mayDiscard) {
87 restartDecoder = mFrames->Reset();
91 if (mayDiscard) {
92 // We are over the threshold and have started discarding old frames. In
93 // that case we need to seize the decoding mutex. Thankfully we know that
94 // we are in the process of decoding at most the batch size frames, so
95 // this should not take too long to acquire.
96 MutexAutoLock lock(mDecodingMutex);
98 // We may have hit an error while redecoding. Because FrameAnimator is
99 // tightly coupled to our own state, that means we would need to go through
100 // some heroics to resume animating in those cases. The typical reason for
101 // a redecode to fail is out of memory, and recycling should prevent most of
102 // those errors. When image.animated.generate-full-frames has shipped
103 // enabled on a release or two, we can simply remove the old FrameAnimator
104 // blending code and simplify this quite a bit -- just always pop the next
105 // full frame and timeout off the stack.
106 if (mDecoder) {
107 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
108 MOZ_ASSERT(mDecoder);
110 MutexAutoLock lock2(mFramesMutex);
111 restartDecoder = mFrames->Reset();
112 } else {
113 MOZ_ASSERT(mFrames->HasRedecodeError());
117 if (restartDecoder) {
118 DecodePool::Singleton()->AsyncRun(this);
122 void AnimationSurfaceProvider::Advance(size_t aFrame) {
123 bool restartDecoder;
126 // Typical advancement of a frame.
127 MutexAutoLock lock(mFramesMutex);
128 restartDecoder = mFrames->AdvanceTo(aFrame);
131 if (restartDecoder) {
132 DecodePool::Singleton()->AsyncRun(this);
136 DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) {
137 MutexAutoLock lock(mFramesMutex);
139 if (Availability().IsPlaceholder()) {
140 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
141 return DrawableFrameRef();
144 imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true);
145 if (!frame) {
146 return DrawableFrameRef();
149 return frame->DrawableRef();
152 already_AddRefed<imgFrame> AnimationSurfaceProvider::GetFrame(size_t aFrame) {
153 MutexAutoLock lock(mFramesMutex);
155 if (Availability().IsPlaceholder()) {
156 MOZ_ASSERT_UNREACHABLE("Calling GetFrame() on a placeholder");
157 return nullptr;
160 RefPtr<imgFrame> frame = mFrames->Get(aFrame, /* aForDisplay */ false);
161 MOZ_ASSERT_IF(frame, frame->IsFinished());
162 return frame.forget();
165 bool AnimationSurfaceProvider::IsFinished() const {
166 MutexAutoLock lock(mFramesMutex);
168 if (Availability().IsPlaceholder()) {
169 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
170 return false;
173 return mFrames->IsFirstFrameFinished();
176 bool AnimationSurfaceProvider::IsFullyDecoded() const {
177 MutexAutoLock lock(mFramesMutex);
178 return mFrames->SizeKnown() && !mFrames->MayDiscard();
181 size_t AnimationSurfaceProvider::LogicalSizeInBytes() const {
182 // When decoding animated images, we need at most three live surfaces: the
183 // composited surface, the previous composited surface for
184 // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding
185 // into. The composited surfaces are always BGRA. Although the surface we're
186 // decoding into may be paletted, and may be smaller than the real size of the
187 // image, we assume the worst case here.
188 // XXX(seth): Note that this is actually not accurate yet; we're storing the
189 // full sequence of frames, not just the three live surfaces mentioned above.
190 // Unfortunately there's no way to know in advance how many frames an
191 // animation has, so we really can't do better here. This will become correct
192 // once bug 1289954 is complete.
193 IntSize size = GetSurfaceKey().Size();
194 return 3 * size.width * size.height * sizeof(uint32_t);
197 void AnimationSurfaceProvider::AddSizeOfExcludingThis(
198 MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) {
199 // Note that the surface cache lock is already held here, and then we acquire
200 // mFramesMutex. For this method, this ordering is unavoidable, which means
201 // that we must be careful to always use the same ordering elsewhere.
202 MutexAutoLock lock(mFramesMutex);
203 mFrames->AddSizeOfExcludingThis(aMallocSizeOf, aCallback);
206 void AnimationSurfaceProvider::Run() {
207 MutexAutoLock lock(mDecodingMutex);
209 if (!mDecoder) {
210 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
211 return;
214 while (true) {
215 // Run the decoder.
216 LexerResult result = mDecoder->Decode(WrapNotNull(this));
218 if (result.is<TerminalState>()) {
219 // We may have a new frame now, but it's not guaranteed - a decoding
220 // failure or truncated data may mean that no new frame got produced.
221 // Since we're not sure, rather than call CheckForNewFrameAtYield() here
222 // we call CheckForNewFrameAtTerminalState(), which handles both of these
223 // possibilities.
224 bool continueDecoding = CheckForNewFrameAtTerminalState();
225 FinishDecoding();
227 // Even if it is the last frame, we may not have enough frames buffered
228 // ahead of the current. If we are shutting down, we want to ensure we
229 // release the thread as soon as possible. The animation may advance even
230 // during shutdown, which keeps us decoding, and thus blocking the decode
231 // pool during teardown.
232 if (!mDecoder || !continueDecoding ||
233 DecodePool::Singleton()->IsShuttingDown()) {
234 return;
237 // Restart from the very beginning because the decoder was recreated.
238 continue;
241 // Notify for the progress we've made so far.
242 if (mImage && mDecoder->HasProgress()) {
243 NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
246 if (result == LexerResult(Yield::NEED_MORE_DATA)) {
247 // We can't make any more progress right now. The decoder itself will
248 // ensure that we get reenqueued when more data is available; just return
249 // for now.
250 return;
253 // There's new output available - a new frame! Grab it. If we don't need any
254 // more for the moment we can break out of the loop. If we are shutting
255 // down, we want to ensure we release the thread as soon as possible. The
256 // animation may advance even during shutdown, which keeps us decoding, and
257 // thus blocking the decode pool during teardown.
258 MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
259 if (!CheckForNewFrameAtYield() ||
260 DecodePool::Singleton()->IsShuttingDown()) {
261 return;
266 bool AnimationSurfaceProvider::CheckForNewFrameAtYield() {
267 mDecodingMutex.AssertCurrentThreadOwns();
268 MOZ_ASSERT(mDecoder);
270 bool justGotFirstFrame = false;
271 bool continueDecoding = false;
274 MutexAutoLock lock(mFramesMutex);
276 // Try to get the new frame from the decoder.
277 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
278 MOZ_ASSERT(mDecoder->HasFrameToTake());
279 mDecoder->ClearHasFrameToTake();
281 if (!frame) {
282 MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
283 return true;
286 // We should've gotten a different frame than last time.
287 MOZ_ASSERT(!mFrames->IsLastInsertedFrame(frame));
289 // Append the new frame to the list.
290 AnimationFrameBuffer::InsertStatus status =
291 mFrames->Insert(std::move(frame));
293 // If we hit a redecode error, then we actually want to stop. This happens
294 // when we tried to insert more frames than we originally had (e.g. the
295 // original decoder attempt hit an OOM error sooner than we did). Better to
296 // stop the animation than to get out of sync with FrameAnimator.
297 if (mFrames->HasRedecodeError()) {
298 mDecoder = nullptr;
299 return false;
302 switch (status) {
303 case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
304 continueDecoding = true;
305 MOZ_FALLTHROUGH;
306 case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD:
307 RequestFrameDiscarding();
308 break;
309 case AnimationFrameBuffer::InsertStatus::CONTINUE:
310 continueDecoding = true;
311 break;
312 case AnimationFrameBuffer::InsertStatus::YIELD:
313 break;
314 default:
315 MOZ_ASSERT_UNREACHABLE("Unhandled insert status!");
316 break;
319 // We only want to handle the first frame if it is the first pass for the
320 // animation decoder. The owning image will be cleared after that.
321 size_t frameCount = mFrames->Size();
322 if (frameCount == 1 && mImage) {
323 justGotFirstFrame = true;
327 if (justGotFirstFrame) {
328 AnnounceSurfaceAvailable();
331 return continueDecoding;
334 bool AnimationSurfaceProvider::CheckForNewFrameAtTerminalState() {
335 mDecodingMutex.AssertCurrentThreadOwns();
336 MOZ_ASSERT(mDecoder);
338 bool justGotFirstFrame = false;
339 bool continueDecoding;
342 MutexAutoLock lock(mFramesMutex);
344 // The decoder may or may not have a new frame for us at this point. Avoid
345 // reinserting the same frame again.
346 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
348 // If the decoder didn't finish a new frame (ie if, after starting the
349 // frame, it got an error and aborted the frame and the rest of the decode)
350 // that means it won't be reporting it to the image or FrameAnimator so we
351 // should ignore it too, that's what HasFrameToTake tracks basically.
352 if (!mDecoder->HasFrameToTake()) {
353 frame = nullptr;
354 } else {
355 MOZ_ASSERT(frame);
356 mDecoder->ClearHasFrameToTake();
359 if (!frame || mFrames->IsLastInsertedFrame(frame)) {
360 return mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
363 // Append the new frame to the list.
364 AnimationFrameBuffer::InsertStatus status =
365 mFrames->Insert(std::move(frame));
367 // If we hit a redecode error, then we actually want to stop. This will be
368 // fully handled in FinishDecoding.
369 if (mFrames->HasRedecodeError()) {
370 return false;
373 switch (status) {
374 case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
375 case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD:
376 RequestFrameDiscarding();
377 break;
378 case AnimationFrameBuffer::InsertStatus::CONTINUE:
379 case AnimationFrameBuffer::InsertStatus::YIELD:
380 break;
381 default:
382 MOZ_ASSERT_UNREACHABLE("Unhandled insert status!");
383 break;
386 continueDecoding =
387 mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
389 // We only want to handle the first frame if it is the first pass for the
390 // animation decoder. The owning image will be cleared after that.
391 if (mFrames->Size() == 1 && mImage) {
392 justGotFirstFrame = true;
396 if (justGotFirstFrame) {
397 AnnounceSurfaceAvailable();
400 return continueDecoding;
403 void AnimationSurfaceProvider::RequestFrameDiscarding() {
404 mDecodingMutex.AssertCurrentThreadOwns();
405 mFramesMutex.AssertCurrentThreadOwns();
406 MOZ_ASSERT(mDecoder);
408 if (mFrames->MayDiscard() || mFrames->IsRecycling()) {
409 MOZ_ASSERT_UNREACHABLE("Already replaced frame queue!");
410 return;
413 auto oldFrameQueue =
414 static_cast<AnimationFrameRetainedBuffer*>(mFrames.get());
416 // We only recycle if it is a full frame. Partial frames may be sized
417 // differently from each other. We do not support recycling with WebRender
418 // and shared surfaces at this time as there is additional synchronization
419 // required to know when it is safe to recycle.
420 MOZ_ASSERT(!mDecoder->GetFrameRecycler());
421 if (gfxPrefs::ImageAnimatedDecodeOnDemandRecycle() &&
422 mDecoder->ShouldBlendAnimation()) {
423 mFrames.reset(new AnimationFrameRecyclingQueue(std::move(*oldFrameQueue)));
424 mDecoder->SetFrameRecycler(this);
425 } else {
426 mFrames.reset(new AnimationFrameDiscardingQueue(std::move(*oldFrameQueue)));
430 void AnimationSurfaceProvider::AnnounceSurfaceAvailable() {
431 mFramesMutex.AssertNotCurrentThreadOwns();
432 MOZ_ASSERT(mImage);
434 // We just got the first frame; let the surface cache know. We deliberately do
435 // this outside of mFramesMutex to avoid a potential deadlock with
436 // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex
437 // and then the surface cache lock, while the memory reporting code would
438 // acquire the surface cache lock and then mFramesMutex.
439 SurfaceCache::SurfaceAvailable(WrapNotNull(this));
442 void AnimationSurfaceProvider::FinishDecoding() {
443 mDecodingMutex.AssertCurrentThreadOwns();
444 MOZ_ASSERT(mDecoder);
446 if (mImage) {
447 // Send notifications.
448 NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
451 // Determine if we need to recreate the decoder, in case we are discarding
452 // frames and need to loop back to the beginning.
453 bool recreateDecoder;
455 MutexAutoLock lock(mFramesMutex);
456 recreateDecoder = !mFrames->HasRedecodeError() && mFrames->MayDiscard();
459 if (recreateDecoder) {
460 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
461 MOZ_ASSERT(mDecoder);
462 } else {
463 mDecoder = nullptr;
466 // We don't need a reference to our image anymore, either, and we don't want
467 // one. We may be stored in the surface cache for a long time after decoding
468 // finishes. If we don't drop our reference to the image, we'll end up
469 // keeping it alive as long as we remain in the surface cache, which could
470 // greatly extend the image's lifetime - in fact, if the image isn't
471 // discardable, it'd result in a leak!
472 DropImageReference();
475 bool AnimationSurfaceProvider::ShouldPreferSyncRun() const {
476 MutexAutoLock lock(mDecodingMutex);
477 MOZ_ASSERT(mDecoder);
479 return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
482 RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame(
483 gfx::IntRect& aRecycleRect) {
484 MutexAutoLock lock(mFramesMutex);
485 MOZ_ASSERT(mFrames->IsRecycling());
486 return mFrames->RecycleFrame(aRecycleRect);
489 } // namespace image
490 } // namespace mozilla