Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/AnimationSurfaceProvider.cpp
Line
Count
Source (jump to first uncovered line)
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 "gfxPrefs.h"
9
#include "nsProxyRelease.h"
10
11
#include "DecodePool.h"
12
#include "Decoder.h"
13
14
using namespace mozilla::gfx;
15
16
namespace mozilla {
17
namespace image {
18
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")
29
0
{
30
0
  MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
31
0
             "Use MetadataDecodingTask for metadata decodes");
32
0
  MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
33
0
             "Use DecodedSurfaceProvider for single-frame image decodes");
34
0
35
0
  // We may produce paletted surfaces for GIF which means the frames are smaller
36
0
  // than one would expect.
37
0
  size_t pixelSize = !aDecoder->ShouldBlendAnimation() &&
38
0
                     aDecoder->GetType() == DecoderType::GIF
39
0
                     ? sizeof(uint8_t) : sizeof(uint32_t);
40
0
41
0
  // Calculate how many frames we need to decode in this animation before we
42
0
  // enter decode-on-demand mode.
43
0
  IntSize frameSize = aSurfaceKey.Size();
44
0
  size_t threshold =
45
0
    (size_t(gfxPrefs::ImageAnimatedDecodeOnDemandThresholdKB()) * 1024) /
46
0
    (pixelSize * frameSize.width * frameSize.height);
47
0
  size_t batch = gfxPrefs::ImageAnimatedDecodeOnDemandBatchSize();
48
0
49
0
  mFrames.Initialize(threshold, batch, aCurrentFrame);
50
0
}
51
52
AnimationSurfaceProvider::~AnimationSurfaceProvider()
53
0
{
54
0
  DropImageReference();
55
0
}
56
57
void
58
AnimationSurfaceProvider::DropImageReference()
59
0
{
60
0
  if (!mImage) {
61
0
    return;  // Nothing to do.
62
0
  }
63
0
64
0
  // RasterImage objects need to be destroyed on the main thread.
65
0
  NS_ReleaseOnMainThreadSystemGroup("AnimationSurfaceProvider::mImage",
66
0
                                    mImage.forget());
67
0
}
68
69
void
70
AnimationSurfaceProvider::Reset()
71
0
{
72
0
  // We want to go back to the beginning.
73
0
  bool mayDiscard;
74
0
  bool restartDecoder;
75
0
76
0
  {
77
0
    MutexAutoLock lock(mFramesMutex);
78
0
79
0
    // If we have not crossed the threshold, we know we haven't discarded any
80
0
    // frames, and thus we know it is safe move our display index back to the
81
0
    // very beginning. It would be cleaner to let the frame buffer make this
82
0
    // decision inside the AnimationFrameBuffer::Reset method, but if we have
83
0
    // crossed the threshold, we need to hold onto the decoding mutex too. We
84
0
    // should avoid blocking the main thread on the decoder threads.
85
0
    mayDiscard = mFrames.MayDiscard();
86
0
    if (!mayDiscard) {
87
0
      restartDecoder = mFrames.Reset();
88
0
    }
89
0
  }
90
0
91
0
  if (mayDiscard) {
92
0
    // We are over the threshold and have started discarding old frames. In
93
0
    // that case we need to seize the decoding mutex. Thankfully we know that
94
0
    // we are in the process of decoding at most the batch size frames, so
95
0
    // this should not take too long to acquire.
96
0
    MutexAutoLock lock(mDecodingMutex);
97
0
98
0
    // Recreate the decoder so we can regenerate the frames again.
99
0
    mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
100
0
    MOZ_ASSERT(mDecoder);
101
0
102
0
    MutexAutoLock lock2(mFramesMutex);
103
0
    restartDecoder = mFrames.Reset();
104
0
  }
105
0
106
0
  if (restartDecoder) {
107
0
    DecodePool::Singleton()->AsyncRun(this);
108
0
  }
109
0
}
110
111
void
112
AnimationSurfaceProvider::Advance(size_t aFrame)
113
0
{
114
0
  bool restartDecoder;
115
0
116
0
  {
117
0
    // Typical advancement of a frame.
118
0
    MutexAutoLock lock(mFramesMutex);
119
0
    restartDecoder = mFrames.AdvanceTo(aFrame);
120
0
  }
121
0
122
0
  if (restartDecoder) {
123
0
    DecodePool::Singleton()->AsyncRun(this);
124
0
  }
125
0
}
126
127
DrawableFrameRef
128
AnimationSurfaceProvider::DrawableRef(size_t aFrame)
129
0
{
130
0
  MutexAutoLock lock(mFramesMutex);
131
0
132
0
  if (Availability().IsPlaceholder()) {
133
0
    MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
134
0
    return DrawableFrameRef();
135
0
  }
136
0
137
0
  imgFrame* frame = mFrames.Get(aFrame);
138
0
  if (!frame) {
139
0
    return DrawableFrameRef();
140
0
  }
141
0
142
0
  return frame->DrawableRef();
143
0
}
144
145
RawAccessFrameRef
146
AnimationSurfaceProvider::RawAccessRef(size_t aFrame)
147
0
{
148
0
  MutexAutoLock lock(mFramesMutex);
149
0
150
0
  if (Availability().IsPlaceholder()) {
151
0
    MOZ_ASSERT_UNREACHABLE("Calling RawAccessRef() on a placeholder");
152
0
    return RawAccessFrameRef();
153
0
  }
154
0
155
0
  imgFrame* frame = mFrames.Get(aFrame);
156
0
  if (!frame) {
157
0
    return RawAccessFrameRef();
158
0
  }
159
0
160
0
  return frame->RawAccessRef(/* aOnlyFinished */ true);
161
0
}
162
163
bool
164
AnimationSurfaceProvider::IsFinished() const
165
0
{
166
0
  MutexAutoLock lock(mFramesMutex);
167
0
168
0
  if (Availability().IsPlaceholder()) {
169
0
    MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
170
0
    return false;
171
0
  }
172
0
173
0
  if (mFrames.Frames().IsEmpty()) {
174
0
    MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no frames");
175
0
    return false;
176
0
  }
177
0
178
0
  // As long as we have at least one finished frame, we're finished.
179
0
  return mFrames.Frames()[0]->IsFinished();
180
0
}
181
182
bool
183
AnimationSurfaceProvider::IsFullyDecoded() const
184
0
{
185
0
  MutexAutoLock lock(mFramesMutex);
186
0
  return mFrames.SizeKnown() && !mFrames.MayDiscard();
187
0
}
188
189
size_t
190
AnimationSurfaceProvider::LogicalSizeInBytes() const
191
0
{
192
0
  // When decoding animated images, we need at most three live surfaces: the
193
0
  // composited surface, the previous composited surface for
194
0
  // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding
195
0
  // into. The composited surfaces are always BGRA. Although the surface we're
196
0
  // decoding into may be paletted, and may be smaller than the real size of the
197
0
  // image, we assume the worst case here.
198
0
  // XXX(seth): Note that this is actually not accurate yet; we're storing the
199
0
  // full sequence of frames, not just the three live surfaces mentioned above.
200
0
  // Unfortunately there's no way to know in advance how many frames an
201
0
  // animation has, so we really can't do better here. This will become correct
202
0
  // once bug 1289954 is complete.
203
0
  IntSize size = GetSurfaceKey().Size();
204
0
  return 3 * size.width * size.height * sizeof(uint32_t);
205
0
}
206
207
void
208
AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
209
                                                 size_t& aHeapSizeOut,
210
                                                 size_t& aNonHeapSizeOut,
211
                                                 size_t& aExtHandlesOut)
212
0
{
213
0
  // Note that the surface cache lock is already held here, and then we acquire
214
0
  // mFramesMutex. For this method, this ordering is unavoidable, which means
215
0
  // that we must be careful to always use the same ordering elsewhere.
216
0
  MutexAutoLock lock(mFramesMutex);
217
0
218
0
  for (const RawAccessFrameRef& frame : mFrames.Frames()) {
219
0
    if (frame) {
220
0
      frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
221
0
                                    aNonHeapSizeOut, aExtHandlesOut);
222
0
    }
223
0
  }
224
0
}
225
226
void
227
AnimationSurfaceProvider::Run()
228
0
{
229
0
  MutexAutoLock lock(mDecodingMutex);
230
0
231
0
  if (!mDecoder) {
232
0
    MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
233
0
    return;
234
0
  }
235
0
236
0
  while (true) {
237
0
    // Run the decoder.
238
0
    LexerResult result = mDecoder->Decode(WrapNotNull(this));
239
0
240
0
    if (result.is<TerminalState>()) {
241
0
      // We may have a new frame now, but it's not guaranteed - a decoding
242
0
      // failure or truncated data may mean that no new frame got produced.
243
0
      // Since we're not sure, rather than call CheckForNewFrameAtYield() here
244
0
      // we call CheckForNewFrameAtTerminalState(), which handles both of these
245
0
      // possibilities.
246
0
      bool continueDecoding = CheckForNewFrameAtTerminalState();
247
0
      FinishDecoding();
248
0
249
0
      // Even if it is the last frame, we may not have enough frames buffered
250
0
      // ahead of the current. If we are shutting down, we want to ensure we
251
0
      // release the thread as soon as possible. The animation may advance even
252
0
      // during shutdown, which keeps us decoding, and thus blocking the decode
253
0
      // pool during teardown.
254
0
      if (!mDecoder || !continueDecoding ||
255
0
          DecodePool::Singleton()->IsShuttingDown()) {
256
0
        return;
257
0
      }
258
0
259
0
      // Restart from the very beginning because the decoder was recreated.
260
0
      continue;
261
0
    }
262
0
263
0
    // Notify for the progress we've made so far.
264
0
    if (mImage && mDecoder->HasProgress()) {
265
0
      NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
266
0
    }
267
0
268
0
    if (result == LexerResult(Yield::NEED_MORE_DATA)) {
269
0
      // We can't make any more progress right now. The decoder itself will ensure
270
0
      // that we get reenqueued when more data is available; just return for now.
271
0
      return;
272
0
    }
273
0
274
0
    // There's new output available - a new frame! Grab it. If we don't need any
275
0
    // more for the moment we can break out of the loop. If we are shutting
276
0
    // down, we want to ensure we release the thread as soon as possible. The
277
0
    // animation may advance even during shutdown, which keeps us decoding, and
278
0
    // thus blocking the decode pool during teardown.
279
0
    MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
280
0
    if (!CheckForNewFrameAtYield() ||
281
0
        DecodePool::Singleton()->IsShuttingDown()) {
282
0
      return;
283
0
    }
284
0
  }
285
0
}
286
287
bool
288
AnimationSurfaceProvider::CheckForNewFrameAtYield()
289
0
{
290
0
  mDecodingMutex.AssertCurrentThreadOwns();
291
0
  MOZ_ASSERT(mDecoder);
292
0
293
0
  bool justGotFirstFrame = false;
294
0
  bool continueDecoding;
295
0
296
0
  {
297
0
    MutexAutoLock lock(mFramesMutex);
298
0
299
0
    // Try to get the new frame from the decoder.
300
0
    RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
301
0
    MOZ_ASSERT(mDecoder->HasFrameToTake());
302
0
    mDecoder->ClearHasFrameToTake();
303
0
304
0
    if (!frame) {
305
0
      MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
306
0
      return true;
307
0
    }
308
0
309
0
    // We should've gotten a different frame than last time.
310
0
    MOZ_ASSERT_IF(!mFrames.Frames().IsEmpty(),
311
0
                  mFrames.Frames().LastElement().get() != frame.get());
312
0
313
0
    // Append the new frame to the list.
314
0
    continueDecoding = mFrames.Insert(std::move(frame));
315
0
316
0
    // We only want to handle the first frame if it is the first pass for the
317
0
    // animation decoder. The owning image will be cleared after that.
318
0
    size_t frameCount = mFrames.Frames().Length();
319
0
    if (frameCount == 1 && mImage) {
320
0
      justGotFirstFrame = true;
321
0
    }
322
0
  }
323
0
324
0
  if (justGotFirstFrame) {
325
0
    AnnounceSurfaceAvailable();
326
0
  }
327
0
328
0
  return continueDecoding;
329
0
}
330
331
bool
332
AnimationSurfaceProvider::CheckForNewFrameAtTerminalState()
333
0
{
334
0
  mDecodingMutex.AssertCurrentThreadOwns();
335
0
  MOZ_ASSERT(mDecoder);
336
0
337
0
  bool justGotFirstFrame = false;
338
0
  bool continueDecoding;
339
0
340
0
  {
341
0
    MutexAutoLock lock(mFramesMutex);
342
0
343
0
    // The decoder may or may not have a new frame for us at this point. Avoid
344
0
    // reinserting the same frame again.
345
0
    RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
346
0
347
0
    // If the decoder didn't finish a new frame (ie if, after starting the
348
0
    // frame, it got an error and aborted the frame and the rest of the decode)
349
0
    // that means it won't be reporting it to the image or FrameAnimator so we
350
0
    // should ignore it too, that's what HasFrameToTake tracks basically.
351
0
    if (!mDecoder->HasFrameToTake()) {
352
0
      frame = RawAccessFrameRef();
353
0
    } else {
354
0
      MOZ_ASSERT(frame);
355
0
      mDecoder->ClearHasFrameToTake();
356
0
    }
357
0
358
0
    if (!frame || (!mFrames.Frames().IsEmpty() &&
359
0
                   mFrames.Frames().LastElement().get() == frame.get())) {
360
0
      return mFrames.MarkComplete();
361
0
    }
362
0
363
0
    // Append the new frame to the list.
364
0
    mFrames.Insert(std::move(frame));
365
0
    continueDecoding = mFrames.MarkComplete();
366
0
367
0
    // We only want to handle the first frame if it is the first pass for the
368
0
    // animation decoder. The owning image will be cleared after that.
369
0
    if (mFrames.Frames().Length() == 1 && mImage) {
370
0
      justGotFirstFrame = true;
371
0
    }
372
0
  }
373
0
374
0
  if (justGotFirstFrame) {
375
0
    AnnounceSurfaceAvailable();
376
0
  }
377
0
378
0
  return continueDecoding;
379
0
}
380
381
void
382
AnimationSurfaceProvider::AnnounceSurfaceAvailable()
383
0
{
384
0
  mFramesMutex.AssertNotCurrentThreadOwns();
385
0
  MOZ_ASSERT(mImage);
386
0
387
0
  // We just got the first frame; let the surface cache know. We deliberately do
388
0
  // this outside of mFramesMutex to avoid a potential deadlock with
389
0
  // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex
390
0
  // and then the surface cache lock, while the memory reporting code would
391
0
  // acquire the surface cache lock and then mFramesMutex.
392
0
  SurfaceCache::SurfaceAvailable(WrapNotNull(this));
393
0
}
394
395
void
396
AnimationSurfaceProvider::FinishDecoding()
397
0
{
398
0
  mDecodingMutex.AssertCurrentThreadOwns();
399
0
  MOZ_ASSERT(mDecoder);
400
0
401
0
  if (mImage) {
402
0
    // Send notifications.
403
0
    NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
404
0
  }
405
0
406
0
  // Determine if we need to recreate the decoder, in case we are discarding
407
0
  // frames and need to loop back to the beginning.
408
0
  bool recreateDecoder;
409
0
  {
410
0
    MutexAutoLock lock(mFramesMutex);
411
0
    recreateDecoder = !mFrames.HasRedecodeError() && mFrames.MayDiscard();
412
0
  }
413
0
414
0
  if (recreateDecoder) {
415
0
    mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
416
0
    MOZ_ASSERT(mDecoder);
417
0
  } else {
418
0
    mDecoder = nullptr;
419
0
  }
420
0
421
0
  // We don't need a reference to our image anymore, either, and we don't want
422
0
  // one. We may be stored in the surface cache for a long time after decoding
423
0
  // finishes. If we don't drop our reference to the image, we'll end up
424
0
  // keeping it alive as long as we remain in the surface cache, which could
425
0
  // greatly extend the image's lifetime - in fact, if the image isn't
426
0
  // discardable, it'd result in a leak!
427
0
  DropImageReference();
428
0
}
429
430
bool
431
AnimationSurfaceProvider::ShouldPreferSyncRun() const
432
0
{
433
0
  MutexAutoLock lock(mDecodingMutex);
434
0
  MOZ_ASSERT(mDecoder);
435
0
436
0
  return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
437
0
}
438
439
} // namespace image
440
} // namespace mozilla