LCOV - code coverage report
Current view: top level - image - AnimationSurfaceProvider.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 76 107 71.0 %
Date: 2017-07-14 16:53:18 Functions: 9 14 64.3 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "Decoder.h"
      12             : 
      13             : using namespace mozilla::gfx;
      14             : 
      15             : namespace mozilla {
      16             : namespace image {
      17             : 
      18           2 : AnimationSurfaceProvider::AnimationSurfaceProvider(NotNull<RasterImage*> aImage,
      19             :                                                    const SurfaceKey& aSurfaceKey,
      20           2 :                                                    NotNull<Decoder*> aDecoder)
      21           2 :   : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
      22             :                      AvailabilityState::StartAsPlaceholder())
      23           2 :   , mImage(aImage.get())
      24             :   , mDecodingMutex("AnimationSurfaceProvider::mDecoder")
      25           2 :   , mDecoder(aDecoder.get())
      26           8 :   , mFramesMutex("AnimationSurfaceProvider::mFrames")
      27             : {
      28           2 :   MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
      29             :              "Use MetadataDecodingTask for metadata decodes");
      30           2 :   MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
      31             :              "Use DecodedSurfaceProvider for single-frame image decodes");
      32           2 : }
      33             : 
      34           0 : AnimationSurfaceProvider::~AnimationSurfaceProvider()
      35             : {
      36           0 :   DropImageReference();
      37           0 : }
      38             : 
      39             : void
      40           2 : AnimationSurfaceProvider::DropImageReference()
      41             : {
      42           2 :   if (!mImage) {
      43           0 :     return;  // Nothing to do.
      44             :   }
      45             : 
      46             :   // RasterImage objects need to be destroyed on the main thread. We also need
      47             :   // to destroy them asynchronously, because if our surface cache entry is
      48             :   // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
      49             :   // destructor may call into the surface cache while whatever code caused us to
      50             :   // get evicted is holding the surface cache lock, causing deadlock.
      51           4 :   RefPtr<RasterImage> image = mImage;
      52           2 :   mImage = nullptr;
      53           2 :   NS_ReleaseOnMainThreadSystemGroup(image.forget(), /* aAlwaysProxy = */ true);
      54             : }
      55             : 
      56             : DrawableFrameRef
      57         156 : AnimationSurfaceProvider::DrawableRef(size_t aFrame)
      58             : {
      59         312 :   MutexAutoLock lock(mFramesMutex);
      60             : 
      61         156 :   if (Availability().IsPlaceholder()) {
      62           0 :     MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
      63             :     return DrawableFrameRef();
      64             :   }
      65             : 
      66         156 :   if (mFrames.IsEmpty()) {
      67           0 :     MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no frames");
      68             :     return DrawableFrameRef();
      69             :   }
      70             : 
      71             :   // If we don't have that frame, return an empty frame ref.
      72         156 :   if (aFrame >= mFrames.Length()) {
      73           0 :     return DrawableFrameRef();
      74             :   }
      75             : 
      76             :   // We've got the requested frame. Return it.
      77         156 :   MOZ_ASSERT(mFrames[aFrame]);
      78         156 :   return mFrames[aFrame]->DrawableRef();
      79             : }
      80             : 
      81             : bool
      82           0 : AnimationSurfaceProvider::IsFinished() const
      83             : {
      84           0 :   MutexAutoLock lock(mFramesMutex);
      85             : 
      86           0 :   if (Availability().IsPlaceholder()) {
      87           0 :     MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
      88             :     return false;
      89             :   }
      90             : 
      91           0 :   if (mFrames.IsEmpty()) {
      92           0 :     MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no frames");
      93             :     return false;
      94             :   }
      95             : 
      96             :   // As long as we have at least one finished frame, we're finished.
      97           0 :   return mFrames[0]->IsFinished();
      98             : }
      99             : 
     100             : size_t
     101          10 : AnimationSurfaceProvider::LogicalSizeInBytes() const
     102             : {
     103             :   // When decoding animated images, we need at most three live surfaces: the
     104             :   // composited surface, the previous composited surface for
     105             :   // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding
     106             :   // into. The composited surfaces are always BGRA. Although the surface we're
     107             :   // decoding into may be paletted, and may be smaller than the real size of the
     108             :   // image, we assume the worst case here.
     109             :   // XXX(seth): Note that this is actually not accurate yet; we're storing the
     110             :   // full sequence of frames, not just the three live surfaces mentioned above.
     111             :   // Unfortunately there's no way to know in advance how many frames an
     112             :   // animation has, so we really can't do better here. This will become correct
     113             :   // once bug 1289954 is complete.
     114          10 :   IntSize size = GetSurfaceKey().Size();
     115          10 :   return 3 * size.width * size.height * sizeof(uint32_t);
     116             : }
     117             : 
     118             : void
     119           0 : AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
     120             :                                                  size_t& aHeapSizeOut,
     121             :                                                  size_t& aNonHeapSizeOut,
     122             :                                                  size_t& aSharedHandlesOut)
     123             : {
     124             :   // Note that the surface cache lock is already held here, and then we acquire
     125             :   // mFramesMutex. For this method, this ordering is unavoidable, which means
     126             :   // that we must be careful to always use the same ordering elsewhere.
     127           0 :   MutexAutoLock lock(mFramesMutex);
     128             : 
     129           0 :   for (const RawAccessFrameRef& frame : mFrames) {
     130           0 :     frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
     131           0 :                                   aNonHeapSizeOut, aSharedHandlesOut);
     132             :   }
     133           0 : }
     134             : 
     135             : void
     136           2 : AnimationSurfaceProvider::Run()
     137             : {
     138           4 :   MutexAutoLock lock(mDecodingMutex);
     139             : 
     140           2 :   if (!mDecoder || !mImage) {
     141           0 :     MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
     142             :     return;
     143             :   }
     144             : 
     145             :   while (true) {
     146             :     // Run the decoder.
     147          70 :     LexerResult result = mDecoder->Decode(WrapNotNull(this));
     148             : 
     149          36 :     if (result.is<TerminalState>()) {
     150             :       // We may have a new frame now, but it's not guaranteed - a decoding
     151             :       // failure or truncated data may mean that no new frame got produced.
     152             :       // Since we're not sure, rather than call CheckForNewFrameAtYield() here
     153             :       // we call CheckForNewFrameAtTerminalState(), which handles both of these
     154             :       // possibilities.
     155           2 :       CheckForNewFrameAtTerminalState();
     156             : 
     157             :       // We're done!
     158           2 :       FinishDecoding();
     159           2 :       return;
     160             :     }
     161             : 
     162             :     // Notify for the progress we've made so far.
     163          34 :     if (mDecoder->HasProgress()) {
     164          34 :       NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
     165             :     }
     166             : 
     167          34 :     if (result == LexerResult(Yield::NEED_MORE_DATA)) {
     168             :       // We can't make any more progress right now. The decoder itself will ensure
     169             :       // that we get reenqueued when more data is available; just return for now.
     170           0 :       return;
     171             :     }
     172             : 
     173             :     // There's new output available - a new frame! Grab it.
     174          34 :     MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
     175          34 :     CheckForNewFrameAtYield();
     176          34 :   }
     177             : }
     178             : 
     179             : void
     180          34 : AnimationSurfaceProvider::CheckForNewFrameAtYield()
     181             : {
     182          34 :   mDecodingMutex.AssertCurrentThreadOwns();
     183          34 :   MOZ_ASSERT(mDecoder);
     184             : 
     185          34 :   bool justGotFirstFrame = false;
     186             : 
     187             :   {
     188          68 :     MutexAutoLock lock(mFramesMutex);
     189             : 
     190             :     // Try to get the new frame from the decoder.
     191          68 :     RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
     192          34 :     if (!frame) {
     193           0 :       MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
     194             :       return;
     195             :     }
     196             : 
     197             :     // We should've gotten a different frame than last time.
     198          34 :     MOZ_ASSERT_IF(!mFrames.IsEmpty(),
     199             :                   mFrames.LastElement().get() != frame.get());
     200             : 
     201             :     // Append the new frame to the list.
     202          34 :     mFrames.AppendElement(Move(frame));
     203             : 
     204          34 :     if (mFrames.Length() == 1) {
     205           2 :       justGotFirstFrame = true;
     206             :     }
     207             :   }
     208             : 
     209          34 :   if (justGotFirstFrame) {
     210           2 :     AnnounceSurfaceAvailable();
     211             :   }
     212          34 : }
     213             : 
     214             : void
     215           2 : AnimationSurfaceProvider::CheckForNewFrameAtTerminalState()
     216             : {
     217           2 :   mDecodingMutex.AssertCurrentThreadOwns();
     218           2 :   MOZ_ASSERT(mDecoder);
     219             : 
     220           2 :   bool justGotFirstFrame = false;
     221             : 
     222             :   {
     223           4 :     MutexAutoLock lock(mFramesMutex);
     224             : 
     225           4 :     RawAccessFrameRef frame = mDecoder->GetCurrentFrameRef();
     226           2 :     if (!frame) {
     227           0 :       return;
     228             :     }
     229             : 
     230           2 :     if (!mFrames.IsEmpty() && mFrames.LastElement().get() == frame.get()) {
     231           0 :       return;  // We already have this one.
     232             :     }
     233             : 
     234             :     // Append the new frame to the list.
     235           2 :     mFrames.AppendElement(Move(frame));
     236             : 
     237           2 :     if (mFrames.Length() == 1) {
     238           0 :       justGotFirstFrame = true;
     239             :     }
     240             :   }
     241             : 
     242           2 :   if (justGotFirstFrame) {
     243           0 :     AnnounceSurfaceAvailable();
     244             :   }
     245             : }
     246             : 
     247             : void
     248           2 : AnimationSurfaceProvider::AnnounceSurfaceAvailable()
     249             : {
     250           2 :   mFramesMutex.AssertNotCurrentThreadOwns();
     251           2 :   MOZ_ASSERT(mImage);
     252             : 
     253             :   // We just got the first frame; let the surface cache know. We deliberately do
     254             :   // this outside of mFramesMutex to avoid a potential deadlock with
     255             :   // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex
     256             :   // and then the surface cache lock, while the memory reporting code would
     257             :   // acquire the surface cache lock and then mFramesMutex.
     258           2 :   SurfaceCache::SurfaceAvailable(WrapNotNull(this));
     259           2 : }
     260             : 
     261             : void
     262           2 : AnimationSurfaceProvider::FinishDecoding()
     263             : {
     264           2 :   mDecodingMutex.AssertCurrentThreadOwns();
     265           2 :   MOZ_ASSERT(mImage);
     266           2 :   MOZ_ASSERT(mDecoder);
     267             : 
     268             :   // Send notifications.
     269           2 :   NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
     270             : 
     271             :   // Destroy our decoder; we don't need it anymore.
     272           2 :   mDecoder = nullptr;
     273             : 
     274             :   // We don't need a reference to our image anymore, either, and we don't want
     275             :   // one. We may be stored in the surface cache for a long time after decoding
     276             :   // finishes. If we don't drop our reference to the image, we'll end up
     277             :   // keeping it alive as long as we remain in the surface cache, which could
     278             :   // greatly extend the image's lifetime - in fact, if the image isn't
     279             :   // discardable, it'd result in a leak!
     280           2 :   DropImageReference();
     281           2 : }
     282             : 
     283             : bool
     284           0 : AnimationSurfaceProvider::ShouldPreferSyncRun() const
     285             : {
     286           0 :   MutexAutoLock lock(mDecodingMutex);
     287           0 :   MOZ_ASSERT(mDecoder);
     288             : 
     289           0 :   return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
     290             : }
     291             : 
     292             : } // namespace image
     293             : } // namespace mozilla

Generated by: LCOV version 1.13