mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 08:42:13 +00:00
231 lines
6.9 KiB
C++
231 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_t(size.width) * size_t(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
|