1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "AnimationSurfaceProvider.h"
7 
8 #include "mozilla/StaticPrefs_image.h"
9 #include "mozilla/gfx/gfxVars.h"
10 #include "nsProxyRelease.h"
11 
12 #include "DecodePool.h"
13 #include "Decoder.h"
14 
15 using namespace mozilla::gfx;
16 
17 namespace mozilla {
18 namespace image {
19 
AnimationSurfaceProvider(NotNull<RasterImage * > aImage,const SurfaceKey & aSurfaceKey,NotNull<Decoder * > aDecoder,size_t aCurrentFrame)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");
33 
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();
42 
43   mFrames.reset(
44       new AnimationFrameRetainedBuffer(threshold, batch, aCurrentFrame));
45 }
46 
~AnimationSurfaceProvider()47 AnimationSurfaceProvider::~AnimationSurfaceProvider() {
48   DropImageReference();
49 
50   if (mDecoder) {
51     mDecoder->SetFrameRecycler(nullptr);
52   }
53 }
54 
DropImageReference()55 void AnimationSurfaceProvider::DropImageReference() {
56   if (!mImage) {
57     return;  // Nothing to do.
58   }
59 
60   // RasterImage objects need to be destroyed on the main thread.
61   NS_ReleaseOnMainThread("AnimationSurfaceProvider::mImage", mImage.forget());
62 }
63 
Reset()64 void AnimationSurfaceProvider::Reset() {
65   // We want to go back to the beginning.
66   bool mayDiscard;
67   bool restartDecoder = false;
68 
69   {
70     MutexAutoLock lock(mFramesMutex);
71 
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();
81     }
82   }
83 
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);
90 
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);
102 
103       MutexAutoLock lock2(mFramesMutex);
104       restartDecoder = mFrames->Reset();
105     } else {
106       MOZ_ASSERT(mFrames->HasRedecodeError());
107     }
108   }
109 
110   if (restartDecoder) {
111     DecodePool::Singleton()->AsyncRun(this);
112   }
113 }
114 
Advance(size_t aFrame)115 void AnimationSurfaceProvider::Advance(size_t aFrame) {
116   bool restartDecoder;
117 
118   {
119     // Typical advancement of a frame.
120     MutexAutoLock lock(mFramesMutex);
121     restartDecoder = mFrames->AdvanceTo(aFrame);
122   }
123 
124   if (restartDecoder) {
125     DecodePool::Singleton()->AsyncRun(this);
126   }
127 }
128 
DrawableRef(size_t aFrame)129 DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) {
130   MutexAutoLock lock(mFramesMutex);
131 
132   if (Availability().IsPlaceholder()) {
133     MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
134     return DrawableFrameRef();
135   }
136 
137   imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true);
138   if (!frame) {
139     return DrawableFrameRef();
140   }
141 
142   return frame->DrawableRef();
143 }
144 
GetFrame(size_t aFrame)145 already_AddRefed<imgFrame> AnimationSurfaceProvider::GetFrame(size_t aFrame) {
146   MutexAutoLock lock(mFramesMutex);
147 
148   if (Availability().IsPlaceholder()) {
149     MOZ_ASSERT_UNREACHABLE("Calling GetFrame() on a placeholder");
150     return nullptr;
151   }
152 
153   RefPtr<imgFrame> frame = mFrames->Get(aFrame, /* aForDisplay */ false);
154   MOZ_ASSERT_IF(frame, frame->IsFinished());
155   return frame.forget();
156 }
157 
IsFinished() const158 bool AnimationSurfaceProvider::IsFinished() const {
159   MutexAutoLock lock(mFramesMutex);
160 
161   if (Availability().IsPlaceholder()) {
162     MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
163     return false;
164   }
165 
166   return mFrames->IsFirstFrameFinished();
167 }
168 
IsFullyDecoded() const169 bool AnimationSurfaceProvider::IsFullyDecoded() const {
170   MutexAutoLock lock(mFramesMutex);
171   return mFrames->SizeKnown() && !mFrames->MayDiscard();
172 }
173 
LogicalSizeInBytes() const174 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);
188 }
189 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,const AddSizeOfCb & aCallback)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);
197 }
198 
Run()199 void AnimationSurfaceProvider::Run() {
200   MutexAutoLock lock(mDecodingMutex);
201 
202   if (!mDecoder) {
203     MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
204     return;
205   }
206 
207   while (true) {
208     // Run the decoder.
209     LexerResult result = mDecoder->Decode(WrapNotNull(this));
210 
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();
219 
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;
228       }
229 
230       // Restart from the very beginning because the decoder was recreated.
231       continue;
232     }
233 
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();
242     }
243 
244     // Notify for the progress we've made so far.
245     if (mImage && mDecoder->HasProgress()) {
246       NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
247     }
248 
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;
254     }
255 
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;
265     }
266   }
267 }
268 
CheckForNewFrameAtYield()269 bool AnimationSurfaceProvider::CheckForNewFrameAtYield() {
270   mDecodingMutex.AssertCurrentThreadOwns();
271   MOZ_ASSERT(mDecoder);
272 
273   bool justGotFirstFrame = false;
274   bool continueDecoding = false;
275 
276   {
277     MutexAutoLock lock(mFramesMutex);
278 
279     // Try to get the new frame from the decoder.
280     RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
281     MOZ_ASSERT(mDecoder->HasFrameToTake());
282     mDecoder->ClearHasFrameToTake();
283 
284     if (!frame) {
285       MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
286       return true;
287     }
288 
289     // We should've gotten a different frame than last time.
290     MOZ_ASSERT(!mFrames->IsLastInsertedFrame(frame));
291 
292     // Append the new frame to the list.
293     AnimationFrameBuffer::InsertStatus status =
294         mFrames->Insert(std::move(frame));
295 
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;
303     }
304 
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;
320     }
321 
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;
327     }
328   }
329 
330   if (justGotFirstFrame) {
331     AnnounceSurfaceAvailable();
332   }
333 
334   return continueDecoding;
335 }
336 
CheckForNewFrameAtTerminalState()337 bool AnimationSurfaceProvider::CheckForNewFrameAtTerminalState() {
338   mDecodingMutex.AssertCurrentThreadOwns();
339   MOZ_ASSERT(mDecoder);
340 
341   bool justGotFirstFrame = false;
342   bool continueDecoding;
343 
344   {
345     MutexAutoLock lock(mFramesMutex);
346 
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();
350 
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();
360     }
361 
362     if (!frame || mFrames->IsLastInsertedFrame(frame)) {
363       return mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
364     }
365 
366     // Append the new frame to the list.
367     AnimationFrameBuffer::InsertStatus status =
368         mFrames->Insert(std::move(frame));
369 
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;
374     }
375 
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;
387     }
388 
389     continueDecoding =
390         mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
391 
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;
396     }
397   }
398 
399   if (justGotFirstFrame) {
400     AnnounceSurfaceAvailable();
401   }
402 
403   return continueDecoding;
404 }
405 
RequestFrameDiscarding()406 void AnimationSurfaceProvider::RequestFrameDiscarding() {
407   mDecodingMutex.AssertCurrentThreadOwns();
408   mFramesMutex.AssertCurrentThreadOwns();
409   MOZ_ASSERT(mDecoder);
410 
411   if (mFrames->MayDiscard() || mFrames->IsRecycling()) {
412     MOZ_ASSERT_UNREACHABLE("Already replaced frame queue!");
413     return;
414   }
415 
416   auto oldFrameQueue =
417       static_cast<AnimationFrameRetainedBuffer*>(mFrames.get());
418 
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)));
425   }
426 }
427 
AnnounceSurfaceAvailable()428 void AnimationSurfaceProvider::AnnounceSurfaceAvailable() {
429   mFramesMutex.AssertNotCurrentThreadOwns();
430   MOZ_ASSERT(mImage);
431 
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));
438 }
439 
FinishDecoding()440 void AnimationSurfaceProvider::FinishDecoding() {
441   mDecodingMutex.AssertCurrentThreadOwns();
442   MOZ_ASSERT(mDecoder);
443 
444   if (mImage) {
445     // Send notifications.
446     NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
447   }
448 
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;
452   {
453     MutexAutoLock lock(mFramesMutex);
454     recreateDecoder = !mFrames->HasRedecodeError() && mFrames->MayDiscard();
455   }
456 
457   if (recreateDecoder) {
458     mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
459     MOZ_ASSERT(mDecoder);
460   } else {
461     mDecoder = nullptr;
462   }
463 
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();
471 }
472 
ShouldPreferSyncRun() const473 bool AnimationSurfaceProvider::ShouldPreferSyncRun() const {
474   MutexAutoLock lock(mDecodingMutex);
475   MOZ_ASSERT(mDecoder);
476 
477   return mDecoder->ShouldSyncDecode(
478       StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
479 }
480 
RecycleFrame(gfx::IntRect & aRecycleRect)481 RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame(
482     gfx::IntRect& aRecycleRect) {
483   MutexAutoLock lock(mFramesMutex);
484   MOZ_ASSERT(mFrames->IsRecycling());
485   return mFrames->RecycleFrame(aRecycleRect);
486 }
487 
488 }  // namespace image
489 }  // namespace mozilla
490