Bug 1669129 - [devtools] Enable devtools.overflow.debugging.enabled. r=jdescottes
[gecko.git] / image / AnimationSurfaceProvider.cpp
blob4dff7293fe2574add8930544d8d8143c44df3264
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 "mozilla/StaticPrefs_image.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 // Calculate how many frames we need to decode in this animation before we
35 // enter decode-on-demand mode.
36 IntSize frameSize = aSurfaceKey.Size();
37 size_t threshold =
38 (size_t(StaticPrefs::image_animated_decode_on_demand_threshold_kb()) *
39 1024) /
40 (sizeof(uint32_t) * frameSize.width * frameSize.height);
41 size_t batch = StaticPrefs::image_animated_decode_on_demand_batch_size();
43 mFrames.reset(
44 new AnimationFrameRetainedBuffer(threshold, batch, aCurrentFrame));
47 AnimationSurfaceProvider::~AnimationSurfaceProvider() {
48 DropImageReference();
50 if (mDecoder) {
51 mDecoder->SetFrameRecycler(nullptr);
55 void AnimationSurfaceProvider::DropImageReference() {
56 if (!mImage) {
57 return; // Nothing to do.
60 // RasterImage objects need to be destroyed on the main thread.
61 SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
64 void AnimationSurfaceProvider::Reset() {
65 // We want to go back to the beginning.
66 bool mayDiscard;
67 bool restartDecoder = false;
70 MutexAutoLock lock(mFramesMutex);
72 // If we have not crossed the threshold, we know we haven't discarded any
73 // frames, and thus we know it is safe move our display index back to the
74 // very beginning. It would be cleaner to let the frame buffer make this
75 // decision inside the AnimationFrameBuffer::Reset method, but if we have
76 // crossed the threshold, we need to hold onto the decoding mutex too. We
77 // should avoid blocking the main thread on the decoder threads.
78 mayDiscard = mFrames->MayDiscard();
79 if (!mayDiscard) {
80 restartDecoder = mFrames->Reset();
84 if (mayDiscard) {
85 // We are over the threshold and have started discarding old frames. In
86 // that case we need to seize the decoding mutex. Thankfully we know that
87 // we are in the process of decoding at most the batch size frames, so
88 // this should not take too long to acquire.
89 MutexAutoLock lock(mDecodingMutex);
91 // We may have hit an error while redecoding. Because FrameAnimator is
92 // tightly coupled to our own state, that means we would need to go through
93 // some heroics to resume animating in those cases. The typical reason for
94 // a redecode to fail is out of memory, and recycling should prevent most of
95 // those errors. When image.animated.generate-full-frames has shipped
96 // enabled on a release or two, we can simply remove the old FrameAnimator
97 // blending code and simplify this quite a bit -- just always pop the next
98 // full frame and timeout off the stack.
99 if (mDecoder) {
100 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
101 MOZ_ASSERT(mDecoder);
103 MutexAutoLock lock2(mFramesMutex);
104 restartDecoder = mFrames->Reset();
105 } else {
106 MOZ_ASSERT(mFrames->HasRedecodeError());
110 if (restartDecoder) {
111 DecodePool::Singleton()->AsyncRun(this);
115 void AnimationSurfaceProvider::Advance(size_t aFrame) {
116 bool restartDecoder;
119 // Typical advancement of a frame.
120 MutexAutoLock lock(mFramesMutex);
121 restartDecoder = mFrames->AdvanceTo(aFrame);
124 if (restartDecoder) {
125 DecodePool::Singleton()->AsyncRun(this);
129 DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) {
130 MutexAutoLock lock(mFramesMutex);
132 if (Availability().IsPlaceholder()) {
133 MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
134 return DrawableFrameRef();
137 imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true);
138 if (!frame) {
139 return DrawableFrameRef();
142 return frame->DrawableRef();
145 already_AddRefed<imgFrame> AnimationSurfaceProvider::GetFrame(size_t aFrame) {
146 MutexAutoLock lock(mFramesMutex);
148 if (Availability().IsPlaceholder()) {
149 MOZ_ASSERT_UNREACHABLE("Calling GetFrame() on a placeholder");
150 return nullptr;
153 RefPtr<imgFrame> frame = mFrames->Get(aFrame, /* aForDisplay */ false);
154 MOZ_ASSERT_IF(frame, frame->IsFinished());
155 return frame.forget();
158 bool AnimationSurfaceProvider::IsFinished() const {
159 MutexAutoLock lock(mFramesMutex);
161 if (Availability().IsPlaceholder()) {
162 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
163 return false;
166 return mFrames->IsFirstFrameFinished();
169 bool AnimationSurfaceProvider::IsFullyDecoded() const {
170 MutexAutoLock lock(mFramesMutex);
171 return mFrames->SizeKnown() && !mFrames->MayDiscard();
174 size_t AnimationSurfaceProvider::LogicalSizeInBytes() const {
175 // When decoding animated images, we need at most three live surfaces: the
176 // composited surface, the previous composited surface for
177 // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding
178 // into. The composited surfaces are always BGRA. Although the surface we're
179 // decoding into may be paletted, and may be smaller than the real size of the
180 // image, we assume the worst case here.
181 // XXX(seth): Note that this is actually not accurate yet; we're storing the
182 // full sequence of frames, not just the three live surfaces mentioned above.
183 // Unfortunately there's no way to know in advance how many frames an
184 // animation has, so we really can't do better here. This will become correct
185 // once bug 1289954 is complete.
186 IntSize size = GetSurfaceKey().Size();
187 return 3 * size.width * size.height * sizeof(uint32_t);
190 void AnimationSurfaceProvider::AddSizeOfExcludingThis(
191 MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) {
192 // Note that the surface cache lock is already held here, and then we acquire
193 // mFramesMutex. For this method, this ordering is unavoidable, which means
194 // that we must be careful to always use the same ordering elsewhere.
195 MutexAutoLock lock(mFramesMutex);
196 mFrames->AddSizeOfExcludingThis(aMallocSizeOf, aCallback);
199 void AnimationSurfaceProvider::Run() {
200 MutexAutoLock lock(mDecodingMutex);
202 if (!mDecoder) {
203 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
204 return;
207 while (true) {
208 // Run the decoder.
209 LexerResult result = mDecoder->Decode(WrapNotNull(this));
211 if (result.is<TerminalState>()) {
212 // We may have a new frame now, but it's not guaranteed - a decoding
213 // failure or truncated data may mean that no new frame got produced.
214 // Since we're not sure, rather than call CheckForNewFrameAtYield() here
215 // we call CheckForNewFrameAtTerminalState(), which handles both of these
216 // possibilities.
217 bool continueDecoding = CheckForNewFrameAtTerminalState();
218 FinishDecoding();
220 // Even if it is the last frame, we may not have enough frames buffered
221 // ahead of the current. If we are shutting down, we want to ensure we
222 // release the thread as soon as possible. The animation may advance even
223 // during shutdown, which keeps us decoding, and thus blocking the decode
224 // pool during teardown.
225 if (!mDecoder || !continueDecoding ||
226 DecodePool::Singleton()->IsShuttingDown()) {
227 return;
230 // Restart from the very beginning because the decoder was recreated.
231 continue;
234 // If there is output available we want to change the entry in the surface
235 // cache from a placeholder to an actual surface now before NotifyProgress
236 // call below so that when consumers get the frame complete notification
237 // from the NotifyProgress they can actually get a surface from the surface
238 // cache.
239 bool checkForNewFrameAtYieldResult = false;
240 if (result == LexerResult(Yield::OUTPUT_AVAILABLE)) {
241 checkForNewFrameAtYieldResult = CheckForNewFrameAtYield();
244 // Notify for the progress we've made so far.
245 if (mImage && mDecoder->HasProgress()) {
246 NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
249 if (result == LexerResult(Yield::NEED_MORE_DATA)) {
250 // We can't make any more progress right now. The decoder itself will
251 // ensure that we get reenqueued when more data is available; just return
252 // for now.
253 return;
256 // There's new output available - a new frame! Grab it. If we don't need any
257 // more for the moment we can break out of the loop. If we are shutting
258 // down, we want to ensure we release the thread as soon as possible. The
259 // animation may advance even during shutdown, which keeps us decoding, and
260 // thus blocking the decode pool during teardown.
261 MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
262 if (!checkForNewFrameAtYieldResult ||
263 DecodePool::Singleton()->IsShuttingDown()) {
264 return;
269 bool AnimationSurfaceProvider::CheckForNewFrameAtYield() {
270 mDecodingMutex.AssertCurrentThreadOwns();
271 MOZ_ASSERT(mDecoder);
273 bool justGotFirstFrame = false;
274 bool continueDecoding = false;
277 MutexAutoLock lock(mFramesMutex);
279 // Try to get the new frame from the decoder.
280 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
281 MOZ_ASSERT(mDecoder->HasFrameToTake());
282 mDecoder->ClearHasFrameToTake();
284 if (!frame) {
285 MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
286 return true;
289 // We should've gotten a different frame than last time.
290 MOZ_ASSERT(!mFrames->IsLastInsertedFrame(frame));
292 // Append the new frame to the list.
293 AnimationFrameBuffer::InsertStatus status =
294 mFrames->Insert(std::move(frame));
296 // If we hit a redecode error, then we actually want to stop. This happens
297 // when we tried to insert more frames than we originally had (e.g. the
298 // original decoder attempt hit an OOM error sooner than we did). Better to
299 // stop the animation than to get out of sync with FrameAnimator.
300 if (mFrames->HasRedecodeError()) {
301 mDecoder = nullptr;
302 return false;
305 switch (status) {
306 case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
307 continueDecoding = true;
308 [[fallthrough]];
309 case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD:
310 RequestFrameDiscarding();
311 break;
312 case AnimationFrameBuffer::InsertStatus::CONTINUE:
313 continueDecoding = true;
314 break;
315 case AnimationFrameBuffer::InsertStatus::YIELD:
316 break;
317 default:
318 MOZ_ASSERT_UNREACHABLE("Unhandled insert status!");
319 break;
322 // We only want to handle the first frame if it is the first pass for the
323 // animation decoder. The owning image will be cleared after that.
324 size_t frameCount = mFrames->Size();
325 if (frameCount == 1 && mImage) {
326 justGotFirstFrame = true;
330 if (justGotFirstFrame) {
331 AnnounceSurfaceAvailable();
334 return continueDecoding;
337 bool AnimationSurfaceProvider::CheckForNewFrameAtTerminalState() {
338 mDecodingMutex.AssertCurrentThreadOwns();
339 MOZ_ASSERT(mDecoder);
341 bool justGotFirstFrame = false;
342 bool continueDecoding;
345 MutexAutoLock lock(mFramesMutex);
347 // The decoder may or may not have a new frame for us at this point. Avoid
348 // reinserting the same frame again.
349 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
351 // If the decoder didn't finish a new frame (ie if, after starting the
352 // frame, it got an error and aborted the frame and the rest of the decode)
353 // that means it won't be reporting it to the image or FrameAnimator so we
354 // should ignore it too, that's what HasFrameToTake tracks basically.
355 if (!mDecoder->HasFrameToTake()) {
356 frame = nullptr;
357 } else {
358 MOZ_ASSERT(frame);
359 mDecoder->ClearHasFrameToTake();
362 if (!frame || mFrames->IsLastInsertedFrame(frame)) {
363 return mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
366 // Append the new frame to the list.
367 AnimationFrameBuffer::InsertStatus status =
368 mFrames->Insert(std::move(frame));
370 // If we hit a redecode error, then we actually want to stop. This will be
371 // fully handled in FinishDecoding.
372 if (mFrames->HasRedecodeError()) {
373 return false;
376 switch (status) {
377 case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
378 case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD:
379 RequestFrameDiscarding();
380 break;
381 case AnimationFrameBuffer::InsertStatus::CONTINUE:
382 case AnimationFrameBuffer::InsertStatus::YIELD:
383 break;
384 default:
385 MOZ_ASSERT_UNREACHABLE("Unhandled insert status!");
386 break;
389 continueDecoding =
390 mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
392 // We only want to handle the first frame if it is the first pass for the
393 // animation decoder. The owning image will be cleared after that.
394 if (mFrames->Size() == 1 && mImage) {
395 justGotFirstFrame = true;
399 if (justGotFirstFrame) {
400 AnnounceSurfaceAvailable();
403 return continueDecoding;
406 void AnimationSurfaceProvider::RequestFrameDiscarding() {
407 mDecodingMutex.AssertCurrentThreadOwns();
408 mFramesMutex.AssertCurrentThreadOwns();
409 MOZ_ASSERT(mDecoder);
411 if (mFrames->MayDiscard() || mFrames->IsRecycling()) {
412 MOZ_ASSERT_UNREACHABLE("Already replaced frame queue!");
413 return;
416 auto oldFrameQueue =
417 static_cast<AnimationFrameRetainedBuffer*>(mFrames.get());
419 MOZ_ASSERT(!mDecoder->GetFrameRecycler());
420 if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) {
421 mFrames.reset(new AnimationFrameRecyclingQueue(std::move(*oldFrameQueue)));
422 mDecoder->SetFrameRecycler(this);
423 } else {
424 mFrames.reset(new AnimationFrameDiscardingQueue(std::move(*oldFrameQueue)));
428 void AnimationSurfaceProvider::AnnounceSurfaceAvailable() {
429 mFramesMutex.AssertNotCurrentThreadOwns();
430 MOZ_ASSERT(mImage);
432 // We just got the first frame; let the surface cache know. We deliberately do
433 // this outside of mFramesMutex to avoid a potential deadlock with
434 // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex
435 // and then the surface cache lock, while the memory reporting code would
436 // acquire the surface cache lock and then mFramesMutex.
437 SurfaceCache::SurfaceAvailable(WrapNotNull(this));
440 void AnimationSurfaceProvider::FinishDecoding() {
441 mDecodingMutex.AssertCurrentThreadOwns();
442 MOZ_ASSERT(mDecoder);
444 if (mImage) {
445 // Send notifications.
446 NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
449 // Determine if we need to recreate the decoder, in case we are discarding
450 // frames and need to loop back to the beginning.
451 bool recreateDecoder;
453 MutexAutoLock lock(mFramesMutex);
454 recreateDecoder = !mFrames->HasRedecodeError() && mFrames->MayDiscard();
457 if (recreateDecoder) {
458 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
459 MOZ_ASSERT(mDecoder);
460 } else {
461 mDecoder = nullptr;
464 // We don't need a reference to our image anymore, either, and we don't want
465 // one. We may be stored in the surface cache for a long time after decoding
466 // finishes. If we don't drop our reference to the image, we'll end up
467 // keeping it alive as long as we remain in the surface cache, which could
468 // greatly extend the image's lifetime - in fact, if the image isn't
469 // discardable, it'd result in a leak!
470 DropImageReference();
473 bool AnimationSurfaceProvider::ShouldPreferSyncRun() const {
474 MutexAutoLock lock(mDecodingMutex);
475 MOZ_ASSERT(mDecoder);
477 return mDecoder->ShouldSyncDecode(
478 StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
481 RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame(
482 gfx::IntRect& aRecycleRect) {
483 MutexAutoLock lock(mFramesMutex);
484 MOZ_ASSERT(mFrames->IsRecycling());
485 return mFrames->RecycleFrame(aRecycleRect);
488 } // namespace image
489 } // namespace mozilla