Bug 1490079 [wpt PR 12936] - Mark tools/wpt Windows test failures as xfail, a=testonly
[gecko.git] / image / AnimationSurfaceProvider.cpp
blobd60e1861c879f6079759cb841ff8106077ad9049
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 "nsProxyRelease.h"
11 #include "DecodePool.h"
12 #include "Decoder.h"
14 using namespace mozilla::gfx;
16 namespace mozilla {
17 namespace image {
19 AnimationSurfaceProvider::AnimationSurfaceProvider(NotNull<RasterImage*> aImage,
20 const SurfaceKey& aSurfaceKey,
21 NotNull<Decoder*> aDecoder,
22 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")
30 MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
31 "Use MetadataDecodingTask for metadata decodes");
32 MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
33 "Use DecodedSurfaceProvider for single-frame image decodes");
35 // We still produce paletted surfaces for GIF which means the frames are
36 // smaller than one would expect for APNG. This may be removed if/when
37 // bug 1337111 lands and it is enabled by default.
38 size_t pixelSize = aDecoder->GetType() == DecoderType::GIF
39 ? sizeof(uint8_t) : 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.Initialize(threshold, batch, aCurrentFrame);
52 AnimationSurfaceProvider::~AnimationSurfaceProvider()
54 DropImageReference();
57 void
58 AnimationSurfaceProvider::DropImageReference()
60 if (!mImage) {
61 return; // Nothing to do.
64 // RasterImage objects need to be destroyed on the main thread.
65 NS_ReleaseOnMainThreadSystemGroup("AnimationSurfaceProvider::mImage",
66 mImage.forget());
69 void
70 AnimationSurfaceProvider::Reset()
72 // We want to go back to the beginning.
73 bool mayDiscard;
74 bool restartDecoder;
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 // Recreate the decoder so we can regenerate the frames again.
99 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
100 MOZ_ASSERT(mDecoder);
102 MutexAutoLock lock2(mFramesMutex);
103 restartDecoder = mFrames.Reset();
106 if (restartDecoder) {
107 DecodePool::Singleton()->AsyncRun(this);
111 void
112 AnimationSurfaceProvider::Advance(size_t aFrame)
114 bool restartDecoder;
117 // Typical advancement of a frame.
118 MutexAutoLock lock(mFramesMutex);
119 restartDecoder = mFrames.AdvanceTo(aFrame);
122 if (restartDecoder) {
123 DecodePool::Singleton()->AsyncRun(this);
127 DrawableFrameRef
128 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);
138 if (!frame) {
139 return DrawableFrameRef();
142 return frame->DrawableRef();
145 RawAccessFrameRef
146 AnimationSurfaceProvider::RawAccessRef(size_t aFrame)
148 MutexAutoLock lock(mFramesMutex);
150 if (Availability().IsPlaceholder()) {
151 MOZ_ASSERT_UNREACHABLE("Calling RawAccessRef() on a placeholder");
152 return RawAccessFrameRef();
155 imgFrame* frame = mFrames.Get(aFrame);
156 if (!frame) {
157 return RawAccessFrameRef();
160 return frame->RawAccessRef(/* aOnlyFinished */ true);
163 bool
164 AnimationSurfaceProvider::IsFinished() const
166 MutexAutoLock lock(mFramesMutex);
168 if (Availability().IsPlaceholder()) {
169 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
170 return false;
173 if (mFrames.Frames().IsEmpty()) {
174 MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no frames");
175 return false;
178 // As long as we have at least one finished frame, we're finished.
179 return mFrames.Frames()[0]->IsFinished();
182 bool
183 AnimationSurfaceProvider::IsFullyDecoded() const
185 MutexAutoLock lock(mFramesMutex);
186 return mFrames.SizeKnown() && !mFrames.MayDiscard();
189 size_t
190 AnimationSurfaceProvider::LogicalSizeInBytes() const
192 // When decoding animated images, we need at most three live surfaces: the
193 // composited surface, the previous composited surface for
194 // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding
195 // into. The composited surfaces are always BGRA. Although the surface we're
196 // decoding into may be paletted, and may be smaller than the real size of the
197 // image, we assume the worst case here.
198 // XXX(seth): Note that this is actually not accurate yet; we're storing the
199 // full sequence of frames, not just the three live surfaces mentioned above.
200 // Unfortunately there's no way to know in advance how many frames an
201 // animation has, so we really can't do better here. This will become correct
202 // once bug 1289954 is complete.
203 IntSize size = GetSurfaceKey().Size();
204 return 3 * size.width * size.height * sizeof(uint32_t);
207 void
208 AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
209 size_t& aHeapSizeOut,
210 size_t& aNonHeapSizeOut,
211 size_t& aExtHandlesOut)
213 // Note that the surface cache lock is already held here, and then we acquire
214 // mFramesMutex. For this method, this ordering is unavoidable, which means
215 // that we must be careful to always use the same ordering elsewhere.
216 MutexAutoLock lock(mFramesMutex);
218 for (const RawAccessFrameRef& frame : mFrames.Frames()) {
219 if (frame) {
220 frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
221 aNonHeapSizeOut, aExtHandlesOut);
226 void
227 AnimationSurfaceProvider::Run()
229 MutexAutoLock lock(mDecodingMutex);
231 if (!mDecoder) {
232 MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
233 return;
236 while (true) {
237 // Run the decoder.
238 LexerResult result = mDecoder->Decode(WrapNotNull(this));
240 if (result.is<TerminalState>()) {
241 // We may have a new frame now, but it's not guaranteed - a decoding
242 // failure or truncated data may mean that no new frame got produced.
243 // Since we're not sure, rather than call CheckForNewFrameAtYield() here
244 // we call CheckForNewFrameAtTerminalState(), which handles both of these
245 // possibilities.
246 bool continueDecoding = CheckForNewFrameAtTerminalState();
247 FinishDecoding();
249 // Even if it is the last frame, we may not have enough frames buffered
250 // ahead of the current. If we are shutting down, we want to ensure we
251 // release the thread as soon as possible. The animation may advance even
252 // during shutdown, which keeps us decoding, and thus blocking the decode
253 // pool during teardown.
254 if (!mDecoder || !continueDecoding ||
255 DecodePool::Singleton()->IsShuttingDown()) {
256 return;
259 // Restart from the very beginning because the decoder was recreated.
260 continue;
263 // Notify for the progress we've made so far.
264 if (mImage && mDecoder->HasProgress()) {
265 NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
268 if (result == LexerResult(Yield::NEED_MORE_DATA)) {
269 // We can't make any more progress right now. The decoder itself will ensure
270 // that we get reenqueued when more data is available; just return for now.
271 return;
274 // There's new output available - a new frame! Grab it. If we don't need any
275 // more for the moment we can break out of the loop. If we are shutting
276 // down, we want to ensure we release the thread as soon as possible. The
277 // animation may advance even during shutdown, which keeps us decoding, and
278 // thus blocking the decode pool during teardown.
279 MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
280 if (!CheckForNewFrameAtYield() ||
281 DecodePool::Singleton()->IsShuttingDown()) {
282 return;
287 bool
288 AnimationSurfaceProvider::CheckForNewFrameAtYield()
290 mDecodingMutex.AssertCurrentThreadOwns();
291 MOZ_ASSERT(mDecoder);
293 bool justGotFirstFrame = false;
294 bool continueDecoding;
297 MutexAutoLock lock(mFramesMutex);
299 // Try to get the new frame from the decoder.
300 RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
301 MOZ_ASSERT(mDecoder->HasFrameToTake());
302 mDecoder->ClearHasFrameToTake();
304 if (!frame) {
305 MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
306 return true;
309 // We should've gotten a different frame than last time.
310 MOZ_ASSERT_IF(!mFrames.Frames().IsEmpty(),
311 mFrames.Frames().LastElement().get() != frame.get());
313 // Append the new frame to the list.
314 continueDecoding = mFrames.Insert(std::move(frame));
316 // We only want to handle the first frame if it is the first pass for the
317 // animation decoder. The owning image will be cleared after that.
318 size_t frameCount = mFrames.Frames().Length();
319 if (frameCount == 1 && mImage) {
320 justGotFirstFrame = true;
324 if (justGotFirstFrame) {
325 AnnounceSurfaceAvailable();
328 return continueDecoding;
331 bool
332 AnimationSurfaceProvider::CheckForNewFrameAtTerminalState()
334 mDecodingMutex.AssertCurrentThreadOwns();
335 MOZ_ASSERT(mDecoder);
337 bool justGotFirstFrame = false;
338 bool continueDecoding;
341 MutexAutoLock lock(mFramesMutex);
343 // The decoder may or may not have a new frame for us at this point. Avoid
344 // reinserting the same frame again.
345 RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
347 // If the decoder didn't finish a new frame (ie if, after starting the
348 // frame, it got an error and aborted the frame and the rest of the decode)
349 // that means it won't be reporting it to the image or FrameAnimator so we
350 // should ignore it too, that's what HasFrameToTake tracks basically.
351 if (!mDecoder->HasFrameToTake()) {
352 frame = RawAccessFrameRef();
353 } else {
354 MOZ_ASSERT(frame);
355 mDecoder->ClearHasFrameToTake();
358 if (!frame || (!mFrames.Frames().IsEmpty() &&
359 mFrames.Frames().LastElement().get() == frame.get())) {
360 return mFrames.MarkComplete();
363 // Append the new frame to the list.
364 mFrames.Insert(std::move(frame));
365 continueDecoding = mFrames.MarkComplete();
367 // We only want to handle the first frame if it is the first pass for the
368 // animation decoder. The owning image will be cleared after that.
369 if (mFrames.Frames().Length() == 1 && mImage) {
370 justGotFirstFrame = true;
374 if (justGotFirstFrame) {
375 AnnounceSurfaceAvailable();
378 return continueDecoding;
381 void
382 AnimationSurfaceProvider::AnnounceSurfaceAvailable()
384 mFramesMutex.AssertNotCurrentThreadOwns();
385 MOZ_ASSERT(mImage);
387 // We just got the first frame; let the surface cache know. We deliberately do
388 // this outside of mFramesMutex to avoid a potential deadlock with
389 // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex
390 // and then the surface cache lock, while the memory reporting code would
391 // acquire the surface cache lock and then mFramesMutex.
392 SurfaceCache::SurfaceAvailable(WrapNotNull(this));
395 void
396 AnimationSurfaceProvider::FinishDecoding()
398 mDecodingMutex.AssertCurrentThreadOwns();
399 MOZ_ASSERT(mDecoder);
401 if (mImage) {
402 // Send notifications.
403 NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
406 // Determine if we need to recreate the decoder, in case we are discarding
407 // frames and need to loop back to the beginning.
408 bool recreateDecoder;
410 MutexAutoLock lock(mFramesMutex);
411 recreateDecoder = !mFrames.HasRedecodeError() && mFrames.MayDiscard();
414 if (recreateDecoder) {
415 mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
416 MOZ_ASSERT(mDecoder);
417 } else {
418 mDecoder = nullptr;
421 // We don't need a reference to our image anymore, either, and we don't want
422 // one. We may be stored in the surface cache for a long time after decoding
423 // finishes. If we don't drop our reference to the image, we'll end up
424 // keeping it alive as long as we remain in the surface cache, which could
425 // greatly extend the image's lifetime - in fact, if the image isn't
426 // discardable, it'd result in a leak!
427 DropImageReference();
430 bool
431 AnimationSurfaceProvider::ShouldPreferSyncRun() const
433 MutexAutoLock lock(mDecodingMutex);
434 MOZ_ASSERT(mDecoder);
436 return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
439 } // namespace image
440 } // namespace mozilla