tor-browser

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

DecodedSurfaceProvider.cpp (8088B)


      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 "DecodedSurfaceProvider.h"
      7 
      8 #include "mozilla/StaticPrefs_image.h"
      9 #include "mozilla/layers/SharedSurfacesChild.h"
     10 #include "nsProxyRelease.h"
     11 
     12 #include "Decoder.h"
     13 
     14 using namespace mozilla::gfx;
     15 using namespace mozilla::layers;
     16 
     17 namespace mozilla {
     18 namespace image {
     19 
     20 DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
     21                                               const SurfaceKey& aSurfaceKey,
     22                                               NotNull<Decoder*> aDecoder)
     23    : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
     24                       AvailabilityState::StartAsPlaceholder()),
     25      mImage(aImage.get()),
     26      mMutex("mozilla::image::DecodedSurfaceProvider"),
     27      mDecoder(aDecoder.get()) {
     28  MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
     29             "Use MetadataDecodingTask for metadata decodes");
     30  MOZ_ASSERT(mDecoder->IsFirstFrameDecode(),
     31             "Use AnimationSurfaceProvider for animation decodes");
     32 }
     33 
     34 DecodedSurfaceProvider::~DecodedSurfaceProvider() { DropImageReference(); }
     35 
     36 void DecodedSurfaceProvider::DropImageReference() {
     37  if (!mImage) {
     38    return;  // Nothing to do.
     39  }
     40 
     41  // RasterImage objects need to be destroyed on the main thread. We also need
     42  // to destroy them asynchronously, because if our surface cache entry is
     43  // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
     44  // destructor may call into the surface cache while whatever code caused us to
     45  // get evicted is holding the surface cache lock, causing deadlock.
     46  RefPtr<RasterImage> image = mImage;
     47  mImage = nullptr;
     48  SurfaceCache::ReleaseImageOnMainThread(image.forget(),
     49                                         /* aAlwaysProxy = */ true);
     50 }
     51 
     52 DrawableFrameRef DecodedSurfaceProvider::DrawableRef(size_t aFrame) {
     53  MOZ_ASSERT(aFrame == 0,
     54             "Requesting an animation frame from a DecodedSurfaceProvider?");
     55 
     56  // We depend on SurfaceCache::SurfaceAvailable() to provide synchronization
     57  // for methods that touch |mSurface|; after SurfaceAvailable() is called,
     58  // |mSurface| should be non-null and shouldn't be mutated further until we get
     59  // destroyed. That means that the assertions below are very important; we'll
     60  // end up with data races if these assumptions are violated.
     61  if (Availability().IsPlaceholder()) {
     62    MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
     63    return DrawableFrameRef();
     64  }
     65 
     66  if (!mSurface) {
     67    MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface");
     68    return DrawableFrameRef();
     69  }
     70 
     71  return mSurface->DrawableRef();
     72 }
     73 
     74 bool DecodedSurfaceProvider::IsFinished() const {
     75  // See DrawableRef() for commentary on these assertions.
     76  if (Availability().IsPlaceholder()) {
     77    MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
     78    return false;
     79  }
     80 
     81  if (!mSurface) {
     82    MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface");
     83    return false;
     84  }
     85 
     86  return mSurface->IsFinished();
     87 }
     88 
     89 void DecodedSurfaceProvider::SetLocked(bool aLocked) {
     90  // See DrawableRef() for commentary on these assertions.
     91  if (Availability().IsPlaceholder()) {
     92    MOZ_ASSERT_UNREACHABLE("Calling SetLocked() on a placeholder");
     93    return;
     94  }
     95 
     96  if (!mSurface) {
     97    MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface");
     98    return;
     99  }
    100 
    101  if (aLocked == IsLocked()) {
    102    return;  // Nothing to do.
    103  }
    104 
    105  // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep any
    106  // volatile buffer it owns in memory.
    107  mLockRef = aLocked ? mSurface->DrawableRef() : DrawableFrameRef();
    108 }
    109 
    110 size_t DecodedSurfaceProvider::LogicalSizeInBytes() const {
    111  // Single frame images are always 32bpp.
    112  IntSize size = GetSurfaceKey().Size();
    113  return size_t(size.width) * size_t(size.height) * sizeof(uint32_t);
    114 }
    115 
    116 void DecodedSurfaceProvider::Run() {
    117  MutexAutoLock lock(mMutex);
    118 
    119  if (!mDecoder || !mImage) {
    120    MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
    121    return;
    122  }
    123 
    124  // Run the decoder.
    125  LexerResult result = mDecoder->Decode(WrapNotNull(this));
    126 
    127  // If there's a new surface available, announce it to the surface cache.
    128  CheckForNewSurface();
    129 
    130  if (result.is<TerminalState>()) {
    131    FinishDecoding();
    132    return;  // We're done.
    133  }
    134 
    135  // Notify for the progress we've made so far.
    136  if (mDecoder->HasProgress()) {
    137    NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
    138  }
    139 
    140  MOZ_ASSERT(result.is<Yield>());
    141 
    142  if (result == LexerResult(Yield::NEED_MORE_DATA)) {
    143    // We can't make any more progress right now. The decoder itself will ensure
    144    // that we get reenqueued when more data is available; just return for now.
    145    return;
    146  }
    147 
    148  // Single-frame images shouldn't yield for any reason except NEED_MORE_DATA.
    149  MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
    150  mDecoder->TerminateFailure();
    151  FinishDecoding();
    152 }
    153 
    154 void DecodedSurfaceProvider::CheckForNewSurface() {
    155  mMutex.AssertCurrentThreadOwns();
    156  MOZ_ASSERT(mDecoder);
    157 
    158  if (mSurface) {
    159    // Single-frame images should produce no more than one surface, so if we
    160    // have one, it should be the same one the decoder is working on.
    161    MOZ_ASSERT(mSurface.get() == mDecoder->GetCurrentFrameRef().get(),
    162               "DecodedSurfaceProvider and Decoder have different surfaces?");
    163    return;
    164  }
    165 
    166  // We don't have a surface yet; try to get one from the decoder.
    167  mSurface = mDecoder->GetCurrentFrameRef().get();
    168  if (!mSurface) {
    169    return;  // No surface yet.
    170  }
    171 
    172  // We just got a surface for the first time; let the surface cache know.
    173  MOZ_ASSERT(mImage);
    174  SurfaceCache::SurfaceAvailable(WrapNotNull(this));
    175 }
    176 
    177 void DecodedSurfaceProvider::FinishDecoding() {
    178  mMutex.AssertCurrentThreadOwns();
    179  MOZ_ASSERT(mImage);
    180  MOZ_ASSERT(mDecoder);
    181 
    182  // Send notifications.
    183  NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
    184 
    185  // If we have a new and complete surface, we can try to prune similarly sized
    186  // surfaces if the cache supports it.
    187  if (mSurface && mSurface->IsFinished()) {
    188    SurfaceCache::PruneImage(ImageKey(mImage));
    189  }
    190 
    191  // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
    192  // our surface can never be optimized, because the decoder has a
    193  // RawAccessFrameRef to it.)
    194  mDecoder = nullptr;
    195 
    196  // We don't need a reference to our image anymore, either, and we don't want
    197  // one. We may be stored in the surface cache for a long time after decoding
    198  // finishes. If we don't drop our reference to the image, we'll end up
    199  // keeping it alive as long as we remain in the surface cache, which could
    200  // greatly extend the image's lifetime - in fact, if the image isn't
    201  // discardable, it'd result in a leak!
    202  DropImageReference();
    203 }
    204 
    205 bool DecodedSurfaceProvider::ShouldPreferSyncRun() const {
    206  return mDecoder->ShouldSyncDecode(
    207      StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
    208 }
    209 
    210 nsresult DecodedSurfaceProvider::UpdateKey(
    211    layers::RenderRootStateManager* aManager,
    212    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
    213  MOZ_ASSERT(mSurface);
    214  RefPtr<SourceSurface> surface = mSurface->GetSourceSurface();
    215  if (!surface) {
    216    return NS_ERROR_FAILURE;
    217  }
    218 
    219  return SharedSurfacesChild::Share(surface, aManager, aResources, aKey);
    220 }
    221 
    222 nsresult SimpleSurfaceProvider::UpdateKey(
    223    layers::RenderRootStateManager* aManager,
    224    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
    225  if (mDirty) {
    226    return NS_ERROR_FAILURE;
    227  }
    228 
    229  RefPtr<SourceSurface> surface = mSurface->GetSourceSurface();
    230  if (!surface) {
    231    return NS_ERROR_FAILURE;
    232  }
    233 
    234  return SharedSurfacesChild::Share(surface, aManager, aResources, aKey);
    235 }
    236 
    237 void SimpleSurfaceProvider::InvalidateSurface() { mDirty = true; }
    238 
    239 }  // namespace image
    240 }  // namespace mozilla