123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "DecodedSurfaceProvider.h"
- #include "gfxPrefs.h"
- #include "nsProxyRelease.h"
- #include "Decoder.h"
- using namespace mozilla::gfx;
- namespace mozilla {
- namespace image {
- DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
- const SurfaceKey& aSurfaceKey,
- NotNull<Decoder*> aDecoder)
- : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
- AvailabilityState::StartAsPlaceholder())
- , mImage(aImage.get())
- , mMutex("mozilla::image::DecodedSurfaceProvider")
- , mDecoder(aDecoder.get())
- {
- MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
- "Use MetadataDecodingTask for metadata decodes");
- MOZ_ASSERT(mDecoder->IsFirstFrameDecode(),
- "Use AnimationSurfaceProvider for animation decodes");
- }
- DecodedSurfaceProvider::~DecodedSurfaceProvider()
- {
- DropImageReference();
- }
- void
- DecodedSurfaceProvider::DropImageReference()
- {
- if (!mImage) {
- return; // Nothing to do.
- }
- // RasterImage objects need to be destroyed on the main thread. We also need
- // to destroy them asynchronously, because if our surface cache entry is
- // destroyed and we were the only thing keeping |mImage| alive, RasterImage's
- // destructor may call into the surface cache while whatever code caused us to
- // get evicted is holding the surface cache lock, causing deadlock.
- RefPtr<RasterImage> image = mImage;
- mImage = nullptr;
- NS_ReleaseOnMainThread(image.forget(), /* aAlwaysProxy = */ true);
- }
- DrawableFrameRef
- DecodedSurfaceProvider::DrawableRef(size_t aFrame)
- {
- MOZ_ASSERT(aFrame == 0,
- "Requesting an animation frame from a DecodedSurfaceProvider?");
- // We depend on SurfaceCache::SurfaceAvailable() to provide synchronization
- // for methods that touch |mSurface|; after SurfaceAvailable() is called,
- // |mSurface| should be non-null and shouldn't be mutated further until we get
- // destroyed. That means that the assertions below are very important; we'll
- // end up with data races if these assumptions are violated.
- if (Availability().IsPlaceholder()) {
- MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
- return DrawableFrameRef();
- }
- if (!mSurface) {
- MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() when we have no surface");
- return DrawableFrameRef();
- }
- return mSurface->DrawableRef();
- }
- bool
- DecodedSurfaceProvider::IsFinished() const
- {
- // See DrawableRef() for commentary on these assertions.
- if (Availability().IsPlaceholder()) {
- MOZ_ASSERT_UNREACHABLE("Calling IsFinished() on a placeholder");
- return false;
- }
- if (!mSurface) {
- MOZ_ASSERT_UNREACHABLE("Calling IsFinished() when we have no surface");
- return false;
- }
- return mSurface->IsFinished();
- }
- void
- DecodedSurfaceProvider::SetLocked(bool aLocked)
- {
- // See DrawableRef() for commentary on these assertions.
- if (Availability().IsPlaceholder()) {
- MOZ_ASSERT_UNREACHABLE("Calling SetLocked() on a placeholder");
- return;
- }
- if (!mSurface) {
- MOZ_ASSERT_UNREACHABLE("Calling SetLocked() when we have no surface");
- return;
- }
- if (aLocked == IsLocked()) {
- return; // Nothing to do.
- }
- // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep any
- // volatile buffer it owns in memory.
- mLockRef = aLocked ? mSurface->DrawableRef()
- : DrawableFrameRef();
- }
- size_t
- DecodedSurfaceProvider::LogicalSizeInBytes() const
- {
- // Single frame images are always 32bpp.
- IntSize size = GetSurfaceKey().Size();
- return size.width * size.height * sizeof(uint32_t);
- }
- void
- DecodedSurfaceProvider::Run()
- {
- MutexAutoLock lock(mMutex);
- if (!mDecoder || !mImage) {
- MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
- return;
- }
- // Run the decoder.
- LexerResult result = mDecoder->Decode(WrapNotNull(this));
- // If there's a new surface available, announce it to the surface cache.
- CheckForNewSurface();
- if (result.is<TerminalState>()) {
- FinishDecoding();
- return; // We're done.
- }
- // Notify for the progress we've made so far.
- if (mDecoder->HasProgress()) {
- NotifyProgress(WrapNotNull(mImage), WrapNotNull(mDecoder));
- }
- MOZ_ASSERT(result.is<Yield>());
- if (result == LexerResult(Yield::NEED_MORE_DATA)) {
- // We can't make any more progress right now. The decoder itself will ensure
- // that we get reenqueued when more data is available; just return for now.
- return;
- }
- // Single-frame images shouldn't yield for any reason except NEED_MORE_DATA.
- MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
- mDecoder->TerminateFailure();
- FinishDecoding();
- }
- void
- DecodedSurfaceProvider::CheckForNewSurface()
- {
- mMutex.AssertCurrentThreadOwns();
- MOZ_ASSERT(mDecoder);
- if (mSurface) {
- // Single-frame images should produce no more than one surface, so if we
- // have one, it should be the same one the decoder is working on.
- MOZ_ASSERT(mSurface.get() == mDecoder->GetCurrentFrameRef().get(),
- "DecodedSurfaceProvider and Decoder have different surfaces?");
- return;
- }
- // We don't have a surface yet; try to get one from the decoder.
- mSurface = mDecoder->GetCurrentFrameRef().get();
- if (!mSurface) {
- return; // No surface yet.
- }
- // We just got a surface for the first time; let the surface cache know.
- MOZ_ASSERT(mImage);
- SurfaceCache::SurfaceAvailable(WrapNotNull(this));
- }
- void
- DecodedSurfaceProvider::FinishDecoding()
- {
- mMutex.AssertCurrentThreadOwns();
- MOZ_ASSERT(mImage);
- MOZ_ASSERT(mDecoder);
- // Send notifications.
- NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
- // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
- // our surface can never be optimized, because the decoder has a
- // RawAccessFrameRef to it.)
- mDecoder = nullptr;
- // We don't need a reference to our image anymore, either, and we don't want
- // one. We may be stored in the surface cache for a long time after decoding
- // finishes. If we don't drop our reference to the image, we'll end up
- // keeping it alive as long as we remain in the surface cache, which could
- // greatly extend the image's lifetime - in fact, if the image isn't
- // discardable, it'd result in a leak!
- DropImageReference();
- }
- bool
- DecodedSurfaceProvider::ShouldPreferSyncRun() const
- {
- return mDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime());
- }
- } // namespace image
- } // namespace mozilla
|