forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			230 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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_ReleaseOnMainThreadSystemGroup(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));
 | |
| 
 | |
|   // If we have a new and complete surface, we can try to prune similarly sized
 | |
|   // surfaces if the cache supports it.
 | |
|   if (mSurface && mSurface->IsFinished()) {
 | |
|     SurfaceCache::PruneImage(ImageKey(mImage));
 | |
|   }
 | |
| 
 | |
|   // 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
 | 
