summaryrefslogtreecommitdiff
path: root/image/DecodedSurfaceProvider.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commitad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299 (patch)
tree10027f336435511475e392454359edea8e25895d /image/DecodedSurfaceProvider.cpp
parent15477ed9af4859dacb069040b5d4de600803d3bc (diff)
downloadaura-central-ad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'image/DecodedSurfaceProvider.cpp')
-rw-r--r--image/DecodedSurfaceProvider.cpp224
1 files changed, 224 insertions, 0 deletions
diff --git a/image/DecodedSurfaceProvider.cpp b/image/DecodedSurfaceProvider.cpp
new file mode 100644
index 000000000..45b4849d2
--- /dev/null
+++ b/image/DecodedSurfaceProvider.cpp
@@ -0,0 +1,224 @@
+/* -*- 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