tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AnimationSurfaceProvider.cpp (18429B)


      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 "mozilla/layers/SharedSurfacesChild.h"
     11 #include "mozilla/layers/SourceSurfaceSharedData.h"
     12 #include "nsProxyRelease.h"
     13 
     14 #include "DecodePool.h"
     15 #include "Decoder.h"
     16 
     17 using namespace mozilla::gfx;
     18 using namespace mozilla::layers;
     19 
     20 namespace mozilla {
     21 namespace image {
     22 
     23 AnimationSurfaceProvider::AnimationSurfaceProvider(
     24    NotNull<RasterImage*> aImage, const SurfaceKey& aSurfaceKey,
     25    NotNull<Decoder*> aDecoder, size_t aCurrentFrame)
     26    : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
     27                       AvailabilityState::StartAsPlaceholder()),
     28      mImage(aImage.get()),
     29      mDecodingMutex("AnimationSurfaceProvider::mDecoder"),
     30      mDecoder(aDecoder.get()),
     31      mFramesMutex("AnimationSurfaceProvider::mFrames"),
     32      mCompositedFrameRequested(false),
     33      mSharedAnimation(MakeRefPtr<SharedSurfacesAnimation>()) {
     34  MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
     35             "Use MetadataDecodingTask for metadata decodes");
     36  MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
     37             "Use DecodedSurfaceProvider for single-frame image decodes");
     38 
     39  // Calculate how many frames we need to decode in this animation before we
     40  // enter decode-on-demand mode.
     41  IntSize frameSize = aSurfaceKey.Size();
     42  size_t threshold =
     43      (size_t(StaticPrefs::image_animated_decode_on_demand_threshold_kb()) *
     44       1024) /
     45      (sizeof(uint32_t) * frameSize.width * frameSize.height);
     46  size_t batch = StaticPrefs::image_animated_decode_on_demand_batch_size();
     47 
     48  mFrames.reset(
     49      new AnimationFrameRetainedBuffer(threshold, batch, aCurrentFrame));
     50 }
     51 
     52 AnimationSurfaceProvider::~AnimationSurfaceProvider() {
     53  DropImageReference();
     54 
     55  mSharedAnimation->Destroy();
     56  if (mDecoder) {
     57    mDecoder->SetFrameRecycler(nullptr);
     58  }
     59 }
     60 
     61 void AnimationSurfaceProvider::DropImageReference() {
     62  if (!mImage) {
     63    return;  // Nothing to do.
     64  }
     65 
     66  // RasterImage objects need to be destroyed on the main thread.
     67  SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
     68 }
     69 
     70 void AnimationSurfaceProvider::Reset() {
     71  // We want to go back to the beginning.
     72  bool mayDiscard;
     73  bool restartDecoder = false;
     74 
     75  {
     76    MutexAutoLock lock(mFramesMutex);
     77 
     78    // If we have not crossed the threshold, we know we haven't discarded any
     79    // frames, and thus we know it is safe move our display index back to the
     80    // very beginning. It would be cleaner to let the frame buffer make this
     81    // decision inside the AnimationFrameBuffer::Reset method, but if we have
     82    // crossed the threshold, we need to hold onto the decoding mutex too. We
     83    // should avoid blocking the main thread on the decoder threads.
     84    mayDiscard = mFrames->MayDiscard();
     85    if (!mayDiscard) {
     86      restartDecoder = mFrames->Reset();
     87    }
     88  }
     89 
     90  if (mayDiscard) {
     91    // We are over the threshold and have started discarding old frames. In
     92    // that case we need to seize the decoding mutex. Thankfully we know that
     93    // we are in the process of decoding at most the batch size frames, so
     94    // this should not take too long to acquire.
     95    MutexAutoLock lock(mDecodingMutex);
     96 
     97    // We may have hit an error while redecoding. Because FrameAnimator is
     98    // tightly coupled to our own state, that means we would need to go through
     99    // some heroics to resume animating in those cases. The typical reason for
    100    // a redecode to fail is out of memory, and recycling should prevent most of
    101    // those errors. When image.animated.generate-full-frames has shipped
    102    // enabled on a release or two, we can simply remove the old FrameAnimator
    103    // blending code and simplify this quite a bit -- just always pop the next
    104    // full frame and timeout off the stack.
    105    if (mDecoder) {
    106      mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
    107      MOZ_ASSERT(mDecoder);
    108 
    109      MutexAutoLock lock2(mFramesMutex);
    110      restartDecoder = mFrames->Reset();
    111    } else {
    112      MOZ_ASSERT(mFrames->HasRedecodeError());
    113    }
    114  }
    115 
    116  if (restartDecoder) {
    117    DecodePool::Singleton()->AsyncRun(this);
    118  }
    119 }
    120 
    121 void AnimationSurfaceProvider::Advance(size_t aFrame) {
    122  bool restartDecoder;
    123 
    124  RefPtr<SourceSurface> surface;
    125  IntRect dirtyRect;
    126  {
    127    // Typical advancement of a frame.
    128    MutexAutoLock lock(mFramesMutex);
    129    restartDecoder = mFrames->AdvanceTo(aFrame);
    130 
    131    imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true);
    132    MOZ_ASSERT(frame);
    133    if (aFrame != 0) {
    134      dirtyRect = frame->GetDirtyRect();
    135    } else {
    136      MOZ_ASSERT(mFrames->SizeKnown());
    137      dirtyRect = mFrames->FirstFrameRefreshArea();
    138    }
    139    surface = frame->GetSourceSurface();
    140    MOZ_ASSERT(surface);
    141  }
    142 
    143  if (restartDecoder) {
    144    DecodePool::Singleton()->AsyncRun(this);
    145  }
    146 
    147  mCompositedFrameRequested = false;
    148  auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
    149  mSharedAnimation->SetCurrentFrame(sharedSurface, dirtyRect);
    150 }
    151 
    152 DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) {
    153  MutexAutoLock lock(mFramesMutex);
    154 
    155  if (Availability().IsPlaceholder()) {
    156    MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
    157    return DrawableFrameRef();
    158  }
    159 
    160  imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true);
    161  if (!frame) {
    162    return DrawableFrameRef();
    163  }
    164 
    165  return frame->DrawableRef();
    166 }
    167 
    168 already_AddRefed<imgFrame> AnimationSurfaceProvider::GetFrame(size_t aFrame) {
    169  MutexAutoLock lock(mFramesMutex);
    170 
    171  if (Availability().IsPlaceholder()) {
    172    MOZ_ASSERT_UNREACHABLE("Calling GetFrame() on a placeholder");
    173    return nullptr;
    174  }
    175 
    176  RefPtr<imgFrame> frame = mFrames->Get(aFrame, /* aForDisplay */ false);
    177  MOZ_ASSERT_IF(frame, frame->IsFinished());
    178  return frame.forget();
    179 }
    180 
    181 bool AnimationSurfaceProvider::IsFinished() const {
    182  MutexAutoLock lock(mFramesMutex);
    183 
    184  if (Availability().IsPlaceholder()) {
    185    MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
    186    return false;
    187  }
    188 
    189  return mFrames->IsFirstFrameFinished();
    190 }
    191 
    192 bool AnimationSurfaceProvider::IsFullyDecoded() const {
    193  MutexAutoLock lock(mFramesMutex);
    194  return mFrames->SizeKnown() && !mFrames->MayDiscard();
    195 }
    196 
    197 size_t AnimationSurfaceProvider::LogicalSizeInBytes() const {
    198  // When decoding animated images, we need at most three live surfaces: the
    199  // composited surface, the previous composited surface for
    200  // DisposalMethod::RESTORE_PREVIOUS, and the surface we're currently decoding
    201  // into. The composited surfaces are always BGRA. Although the surface we're
    202  // decoding into may be paletted, and may be smaller than the real size of the
    203  // image, we assume the worst case here.
    204  // XXX(seth): Note that this is actually not accurate yet; we're storing the
    205  // full sequence of frames, not just the three live surfaces mentioned above.
    206  // Unfortunately there's no way to know in advance how many frames an
    207  // animation has, so we really can't do better here. This will become correct
    208  // once bug 1289954 is complete.
    209  IntSize size = GetSurfaceKey().Size();
    210  return 3 * size.width * size.height * sizeof(uint32_t);
    211 }
    212 
    213 void AnimationSurfaceProvider::AddSizeOfExcludingThis(
    214    MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) {
    215  // Note that the surface cache lock is already held here, and then we acquire
    216  // mFramesMutex. For this method, this ordering is unavoidable, which means
    217  // that we must be careful to always use the same ordering elsewhere.
    218  MutexAutoLock lock(mFramesMutex);
    219  mFrames->AddSizeOfExcludingThis(aMallocSizeOf, aCallback);
    220 }
    221 
    222 void AnimationSurfaceProvider::Run() {
    223  MutexAutoLock lock(mDecodingMutex);
    224 
    225  if (!mDecoder) {
    226    MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
    227    return;
    228  }
    229 
    230  while (true) {
    231    // Run the decoder.
    232    LexerResult result = mDecoder->Decode(WrapNotNull(this));
    233 
    234    if (result.is<TerminalState>()) {
    235      // We may have a new frame now, but it's not guaranteed - a decoding
    236      // failure or truncated data may mean that no new frame got produced.
    237      // Since we're not sure, rather than call CheckForNewFrameAtYield() here
    238      // we call CheckForNewFrameAtTerminalState(), which handles both of these
    239      // possibilities.
    240      bool continueDecoding = CheckForNewFrameAtTerminalState();
    241      FinishDecoding();
    242 
    243      // Even if it is the last frame, we may not have enough frames buffered
    244      // ahead of the current. If we are shutting down, we want to ensure we
    245      // release the thread as soon as possible. The animation may advance even
    246      // during shutdown, which keeps us decoding, and thus blocking the decode
    247      // pool during teardown.
    248      if (!mDecoder || !continueDecoding || DecodePool::IsShuttingDown()) {
    249        return;
    250      }
    251 
    252      // Restart from the very beginning because the decoder was recreated.
    253      continue;
    254    }
    255 
    256    // If there is output available we want to change the entry in the surface
    257    // cache from a placeholder to an actual surface now before NotifyProgress
    258    // call below so that when consumers get the frame complete notification
    259    // from the NotifyProgress they can actually get a surface from the surface
    260    // cache.
    261    bool checkForNewFrameAtYieldResult = false;
    262    if (result == LexerResult(Yield::OUTPUT_AVAILABLE)) {
    263      checkForNewFrameAtYieldResult = CheckForNewFrameAtYield();
    264    }
    265 
    266    // Notify for the progress we've made so far.
    267    if (mImage && mDecoder->HasProgress()) {
    268      NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
    269    }
    270 
    271    if (result == LexerResult(Yield::NEED_MORE_DATA)) {
    272      // We can't make any more progress right now. The decoder itself will
    273      // ensure that we get reenqueued when more data is available; just return
    274      // for now.
    275      return;
    276    }
    277 
    278    // There's new output available - a new frame! Grab it. If we don't need any
    279    // more for the moment we can break out of the loop. If we are shutting
    280    // down, we want to ensure we release the thread as soon as possible. The
    281    // animation may advance even during shutdown, which keeps us decoding, and
    282    // thus blocking the decode pool during teardown.
    283    MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
    284    if (!checkForNewFrameAtYieldResult || DecodePool::IsShuttingDown()) {
    285      return;
    286    }
    287  }
    288 }
    289 
    290 bool AnimationSurfaceProvider::CheckForNewFrameAtYield() {
    291  mDecodingMutex.AssertCurrentThreadOwns();
    292  MOZ_ASSERT(mDecoder);
    293 
    294  bool justGotFirstFrame = false;
    295  bool continueDecoding = false;
    296 
    297  {
    298    MutexAutoLock lock(mFramesMutex);
    299 
    300    // Try to get the new frame from the decoder.
    301    RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
    302    MOZ_ASSERT(mDecoder->HasFrameToTake());
    303    mDecoder->ClearHasFrameToTake();
    304 
    305    if (!frame) {
    306      MOZ_ASSERT_UNREACHABLE("Decoder yielded but didn't produce a frame?");
    307      return true;
    308    }
    309 
    310    // We should've gotten a different frame than last time.
    311    MOZ_ASSERT(!mFrames->IsLastInsertedFrame(frame));
    312 
    313    // Append the new frame to the list.
    314    AnimationFrameBuffer::InsertStatus status =
    315        mFrames->Insert(std::move(frame));
    316 
    317    // If we hit a redecode error, then we actually want to stop. This happens
    318    // when we tried to insert more frames than we originally had (e.g. the
    319    // original decoder attempt hit an OOM error sooner than we did). Better to
    320    // stop the animation than to get out of sync with FrameAnimator.
    321    if (mFrames->HasRedecodeError()) {
    322      mDecoder = nullptr;
    323      return false;
    324    }
    325 
    326    switch (status) {
    327      case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
    328        continueDecoding = true;
    329        [[fallthrough]];
    330      case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD:
    331        RequestFrameDiscarding();
    332        break;
    333      case AnimationFrameBuffer::InsertStatus::CONTINUE:
    334        continueDecoding = true;
    335        break;
    336      case AnimationFrameBuffer::InsertStatus::YIELD:
    337        break;
    338      default:
    339        MOZ_ASSERT_UNREACHABLE("Unhandled insert status!");
    340        break;
    341    }
    342 
    343    // We only want to handle the first frame if it is the first pass for the
    344    // animation decoder. The owning image will be cleared after that.
    345    size_t frameCount = mFrames->Size();
    346    if (frameCount == 1 && mImage) {
    347      justGotFirstFrame = true;
    348    }
    349  }
    350 
    351  if (justGotFirstFrame) {
    352    AnnounceSurfaceAvailable();
    353  }
    354 
    355  return continueDecoding;
    356 }
    357 
    358 bool AnimationSurfaceProvider::CheckForNewFrameAtTerminalState() {
    359  mDecodingMutex.AssertCurrentThreadOwns();
    360  MOZ_ASSERT(mDecoder);
    361 
    362  bool justGotFirstFrame = false;
    363  bool continueDecoding;
    364 
    365  {
    366    MutexAutoLock lock(mFramesMutex);
    367 
    368    // The decoder may or may not have a new frame for us at this point. Avoid
    369    // reinserting the same frame again.
    370    RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
    371 
    372    // If the decoder didn't finish a new frame (ie if, after starting the
    373    // frame, it got an error and aborted the frame and the rest of the decode)
    374    // that means it won't be reporting it to the image or FrameAnimator so we
    375    // should ignore it too, that's what HasFrameToTake tracks basically.
    376    if (!mDecoder->HasFrameToTake()) {
    377      frame = nullptr;
    378    } else {
    379      MOZ_ASSERT(frame);
    380      mDecoder->ClearHasFrameToTake();
    381    }
    382 
    383    if (!frame || mFrames->IsLastInsertedFrame(frame)) {
    384      return mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
    385    }
    386 
    387    // Append the new frame to the list.
    388    AnimationFrameBuffer::InsertStatus status =
    389        mFrames->Insert(std::move(frame));
    390 
    391    // If we hit a redecode error, then we actually want to stop. This will be
    392    // fully handled in FinishDecoding.
    393    if (mFrames->HasRedecodeError()) {
    394      return false;
    395    }
    396 
    397    switch (status) {
    398      case AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE:
    399      case AnimationFrameBuffer::InsertStatus::DISCARD_YIELD:
    400        RequestFrameDiscarding();
    401        break;
    402      case AnimationFrameBuffer::InsertStatus::CONTINUE:
    403      case AnimationFrameBuffer::InsertStatus::YIELD:
    404        break;
    405      default:
    406        MOZ_ASSERT_UNREACHABLE("Unhandled insert status!");
    407        break;
    408    }
    409 
    410    continueDecoding =
    411        mFrames->MarkComplete(mDecoder->GetFirstFrameRefreshArea());
    412 
    413    // We only want to handle the first frame if it is the first pass for the
    414    // animation decoder. The owning image will be cleared after that.
    415    if (mFrames->Size() == 1 && mImage) {
    416      justGotFirstFrame = true;
    417    }
    418  }
    419 
    420  if (justGotFirstFrame) {
    421    AnnounceSurfaceAvailable();
    422  }
    423 
    424  return continueDecoding;
    425 }
    426 
    427 void AnimationSurfaceProvider::RequestFrameDiscarding() {
    428  mDecodingMutex.AssertCurrentThreadOwns();
    429  mFramesMutex.AssertCurrentThreadOwns();
    430  MOZ_ASSERT(mDecoder);
    431 
    432  if (mFrames->MayDiscard() || mFrames->IsRecycling()) {
    433    MOZ_ASSERT_UNREACHABLE("Already replaced frame queue!");
    434    return;
    435  }
    436 
    437  auto oldFrameQueue =
    438      static_cast<AnimationFrameRetainedBuffer*>(mFrames.get());
    439 
    440  MOZ_ASSERT(!mDecoder->GetFrameRecycler());
    441  if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) {
    442    mFrames.reset(new AnimationFrameRecyclingQueue(std::move(*oldFrameQueue)));
    443    mDecoder->SetFrameRecycler(this);
    444  } else {
    445    mFrames.reset(new AnimationFrameDiscardingQueue(std::move(*oldFrameQueue)));
    446  }
    447 }
    448 
    449 void AnimationSurfaceProvider::AnnounceSurfaceAvailable() {
    450  mFramesMutex.AssertNotCurrentThreadOwns();
    451  MOZ_ASSERT(mImage);
    452 
    453  // We just got the first frame; let the surface cache know. We deliberately do
    454  // this outside of mFramesMutex to avoid a potential deadlock with
    455  // AddSizeOfExcludingThis(), since otherwise we'd be acquiring mFramesMutex
    456  // and then the surface cache lock, while the memory reporting code would
    457  // acquire the surface cache lock and then mFramesMutex.
    458  SurfaceCache::SurfaceAvailable(WrapNotNull(this));
    459 }
    460 
    461 void AnimationSurfaceProvider::FinishDecoding() {
    462  mDecodingMutex.AssertCurrentThreadOwns();
    463  MOZ_ASSERT(mDecoder);
    464 
    465  if (mImage) {
    466    // Send notifications.
    467    NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
    468  }
    469 
    470  // Determine if we need to recreate the decoder, in case we are discarding
    471  // frames and need to loop back to the beginning.
    472  bool recreateDecoder;
    473  {
    474    MutexAutoLock lock(mFramesMutex);
    475    recreateDecoder = !mFrames->HasRedecodeError() && mFrames->MayDiscard();
    476  }
    477 
    478  if (recreateDecoder) {
    479    mDecoder = DecoderFactory::CloneAnimationDecoder(mDecoder);
    480    MOZ_ASSERT(mDecoder);
    481  } else {
    482    mDecoder = nullptr;
    483  }
    484 
    485  // We don't need a reference to our image anymore, either, and we don't want
    486  // one. We may be stored in the surface cache for a long time after decoding
    487  // finishes. If we don't drop our reference to the image, we'll end up
    488  // keeping it alive as long as we remain in the surface cache, which could
    489  // greatly extend the image's lifetime - in fact, if the image isn't
    490  // discardable, it'd result in a leak!
    491  DropImageReference();
    492 }
    493 
    494 bool AnimationSurfaceProvider::ShouldPreferSyncRun() const {
    495  MutexAutoLock lock(mDecodingMutex);
    496  MOZ_ASSERT(mDecoder);
    497 
    498  return mDecoder->ShouldSyncDecode(
    499      StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
    500 }
    501 
    502 RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame(
    503    gfx::IntRect& aRecycleRect) {
    504  MutexAutoLock lock(mFramesMutex);
    505  MOZ_ASSERT(mFrames->IsRecycling());
    506  return mFrames->RecycleFrame(aRecycleRect);
    507 }
    508 
    509 nsresult AnimationSurfaceProvider::UpdateKey(
    510    layers::RenderRootStateManager* aManager,
    511    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
    512  MOZ_ASSERT(NS_IsMainThread());
    513 
    514  RefPtr<SourceSurface> surface;
    515  {
    516    MutexAutoLock lock(mFramesMutex);
    517    imgFrame* frame =
    518        mFrames->Get(mFrames->Displayed(), /* aForDisplay */ true);
    519    if (!frame) {
    520      return NS_ERROR_NOT_AVAILABLE;
    521    }
    522 
    523    surface = frame->GetSourceSurface();
    524  }
    525 
    526  mCompositedFrameRequested = true;
    527  auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
    528  return mSharedAnimation->UpdateKey(sharedSurface, aManager, aResources, aKey);
    529 }
    530 
    531 }  // namespace image
    532 }  // namespace mozilla