mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1081012 - Move DecodePool and related helpers out of RasterImage. r=tn
This commit is contained in:
parent
affbe22f4e
commit
f950251326
512
image/src/DecodePool.cpp
Normal file
512
image/src/DecodePool.cpp
Normal file
@ -0,0 +1,512 @@
|
||||
/* -*- 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 "DecodePool.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
#include "gfxPrefs.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "RasterImage.h"
|
||||
|
||||
using std::max;
|
||||
using std::min;
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helper runnables.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class NotifyProgressWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Called by the DecodePool when it's done some significant portion of
|
||||
* decoding, so that progress can be recorded and notifications can be sent.
|
||||
*/
|
||||
static void Dispatch(RasterImage* aImage)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> worker = new NotifyProgressWorker(aImage);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
|
||||
mImage->FinishedSomeDecoding(ShutdownReason::DONE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit NotifyProgressWorker(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
{ }
|
||||
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
};
|
||||
|
||||
class FrameNeededWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Called when an off-main-thread decoder needs a new frame to be allocated on
|
||||
* the main thread.
|
||||
*
|
||||
* After allocating the new frame, the worker will call RequestDecode to
|
||||
* continue decoding.
|
||||
*/
|
||||
static void Dispatch(RasterImage* aImage)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> worker = new FrameNeededWorker(aImage);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// If we got a synchronous decode in the mean time, we don't need to do
|
||||
// anything.
|
||||
if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
|
||||
rv = mImage->mDecoder->AllocateFrame();
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
|
||||
// By definition, we're not done decoding, so enqueue us for more decoding.
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit FrameNeededWorker(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
{ }
|
||||
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
};
|
||||
|
||||
class DecodeWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DecodeWorker(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
|
||||
// If we were interrupted, we shouldn't do any work.
|
||||
if (mImage->mDecodeStatus == DecodeStatus::STOPPED) {
|
||||
NotifyProgressWorker::Dispatch(mImage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If someone came along and synchronously decoded us, there's nothing for us to do.
|
||||
if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
|
||||
NotifyProgressWorker::Dispatch(mImage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we're a decode job that's been enqueued since a previous decode that
|
||||
// still needs a new frame, we can't do anything. Wait until the
|
||||
// FrameNeededWorker enqueues another frame.
|
||||
if (mImage->mDecoder->NeedsNewFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mImage->mDecodeStatus = DecodeStatus::ACTIVE;
|
||||
|
||||
size_t oldByteCount = mImage->mDecoder->BytesDecoded();
|
||||
|
||||
// Multithreaded decoding can be disabled. If we've done so, we don't want
|
||||
// to monopolize the main thread, and will allow a timeout.
|
||||
DecodeUntil type = NS_IsMainThread() ? DecodeUntil::TIME
|
||||
: DecodeUntil::DONE_BYTES;
|
||||
|
||||
size_t maxBytes = mImage->mSourceData.Length() -
|
||||
mImage->mDecoder->BytesDecoded();
|
||||
DecodePool::Singleton()->DecodeSomeOfImage(mImage, DecodeStrategy::ASYNC,
|
||||
type, maxBytes);
|
||||
|
||||
size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
|
||||
|
||||
mImage->mDecodeStatus = DecodeStatus::WORK_DONE;
|
||||
|
||||
if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
|
||||
// The decoder needs a new frame. Enqueue an event to get it; that event
|
||||
// will enqueue another decode request when it's done.
|
||||
FrameNeededWorker::Dispatch(mImage);
|
||||
} else if (mImage->mDecoder &&
|
||||
!mImage->mError &&
|
||||
!mImage->mPendingError &&
|
||||
!mImage->IsDecodeFinished() &&
|
||||
bytesDecoded < maxBytes &&
|
||||
bytesDecoded > 0) {
|
||||
// We aren't finished decoding, and we have more data, so add this request
|
||||
// to the back of the list.
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
} else {
|
||||
// Nothing more for us to do - let everyone know what happened.
|
||||
NotifyProgressWorker::Dispatch(mImage);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~DecodeWorker()
|
||||
{
|
||||
if (gfxPrefs::ImageMTDecodingEnabled()) {
|
||||
// Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
|
||||
if (mainThread) {
|
||||
// Handle ambiguous nsISupports inheritance
|
||||
RasterImage* rawImg = nullptr;
|
||||
mImage.swap(rawImg);
|
||||
DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
};
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
|
||||
class RIDThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITHREADPOOLLISTENER
|
||||
|
||||
RIDThreadPoolListener() { }
|
||||
|
||||
private:
|
||||
~RIDThreadPoolListener() { }
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(RIDThreadPoolListener, nsIThreadPoolListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
RIDThreadPoolListener::OnThreadCreated()
|
||||
{
|
||||
if (IsNuwaProcess()) {
|
||||
NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RIDThreadPoolListener::OnThreadShuttingDown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DecodePool implementation.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
|
||||
|
||||
NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
|
||||
|
||||
/* static */ DecodePool*
|
||||
DecodePool::Singleton()
|
||||
{
|
||||
if (!sSingleton) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sSingleton = new DecodePool();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
DecodePool::GetEventTarget()
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
DecodePool::DecodePool()
|
||||
: mThreadPoolMutex("Thread Pool")
|
||||
{
|
||||
if (gfxPrefs::ImageMTDecodingEnabled()) {
|
||||
mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
|
||||
if (mThreadPool) {
|
||||
mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
|
||||
int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
|
||||
uint32_t limit;
|
||||
if (prefLimit <= 0) {
|
||||
limit = max(PR_GetNumberOfProcessors(), 2) - 1;
|
||||
} else {
|
||||
limit = static_cast<uint32_t>(prefLimit);
|
||||
}
|
||||
|
||||
mThreadPool->SetThreadLimit(limit);
|
||||
mThreadPool->SetIdleThreadLimit(limit);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
mThreadPool->SetListener(new RIDThreadPoolListener());
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
|
||||
if (obsSvc) {
|
||||
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecodePool::~DecodePool()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
|
||||
{
|
||||
MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
|
||||
|
||||
nsCOMPtr<nsIThreadPool> threadPool;
|
||||
|
||||
{
|
||||
MutexAutoLock threadPoolLock(mThreadPoolMutex);
|
||||
threadPool = mThreadPool;
|
||||
mThreadPool = nullptr;
|
||||
}
|
||||
|
||||
if (threadPool) {
|
||||
threadPool->Shutdown();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DecodePool::RequestDecode(RasterImage* aImage)
|
||||
{
|
||||
MOZ_ASSERT(aImage->mDecoder);
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If we're currently waiting on a new frame for this image, we can't do any
|
||||
// decoding.
|
||||
if (!aImage->mDecoder->NeedsNewFrame()) {
|
||||
if (aImage->mDecodeStatus == DecodeStatus::PENDING ||
|
||||
aImage->mDecodeStatus == DecodeStatus::ACTIVE) {
|
||||
// The image is already in our list of images to decode, or currently being
|
||||
// decoded, so we don't have to do anything else.
|
||||
return;
|
||||
}
|
||||
|
||||
aImage->mDecodeStatus = DecodeStatus::PENDING;
|
||||
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aImage);
|
||||
|
||||
MutexAutoLock threadPoolLock(mThreadPoolMutex);
|
||||
if (!gfxPrefs::ImageMTDecodingEnabled() || !mThreadPool) {
|
||||
NS_DispatchToMainThread(worker);
|
||||
} else {
|
||||
mThreadPool->Dispatch(worker, nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DecodePool::DecodeABitOf(RasterImage* aImage, DecodeStrategy aStrategy)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
||||
aImage->FinishedSomeDecoding();
|
||||
}
|
||||
|
||||
DecodeSomeOfImage(aImage, aStrategy);
|
||||
|
||||
aImage->FinishedSomeDecoding();
|
||||
|
||||
// If the decoder needs a new frame, enqueue an event to get it; that event
|
||||
// will enqueue another decode request when it's done.
|
||||
if (aImage->mDecoder && aImage->mDecoder->NeedsNewFrame()) {
|
||||
FrameNeededWorker::Dispatch(aImage);
|
||||
} else {
|
||||
// If we aren't yet finished decoding and we have more data in hand, add
|
||||
// this request to the back of the priority list.
|
||||
if (aImage->mDecoder &&
|
||||
!aImage->mError &&
|
||||
!aImage->IsDecodeFinished() &&
|
||||
aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded()) {
|
||||
RequestDecode(aImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
DecodePool::StopDecoding(RasterImage* aImage)
|
||||
{
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If we haven't got a decode request, we're not currently decoding. (Having
|
||||
// a decode request doesn't imply we *are* decoding, though.)
|
||||
aImage->mDecodeStatus = DecodeStatus::STOPPED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecodePool::DecodeUntilSizeAvailable(RasterImage* aImage)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter lock(aImage->mDecodingMonitor);
|
||||
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
||||
nsresult rv = aImage->FinishedSomeDecoding();
|
||||
if (NS_FAILED(rv)) {
|
||||
aImage->DoError();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// We use DecodeStrategy::ASYNC here because we just want to get the size
|
||||
// information here and defer the rest of the work.
|
||||
nsresult rv =
|
||||
DecodeSomeOfImage(aImage, DecodeStrategy::ASYNC, DecodeUntil::SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If the decoder needs a new frame, enqueue an event to get it; that event
|
||||
// will enqueue another decode request when it's done.
|
||||
if (aImage->mDecoder && aImage->mDecoder->NeedsNewFrame()) {
|
||||
FrameNeededWorker::Dispatch(aImage);
|
||||
} else {
|
||||
rv = aImage->FinishedSomeDecoding();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DecodePool::DecodeSomeOfImage(RasterImage* aImage,
|
||||
DecodeStrategy aStrategy,
|
||||
DecodeUntil aDecodeUntil /* = DecodeUntil::TIME */,
|
||||
uint32_t bytesToDecode /* = 0 */)
|
||||
{
|
||||
MOZ_ASSERT(aImage->mInitialized, "Worker active for uninitialized container");
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If an error is flagged, it probably happened while we were waiting
|
||||
// in the event queue.
|
||||
if (aImage->mError) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If there is an error worker pending (say because the main thread has enqueued
|
||||
// another decode request for us before processing the error worker) then bail out.
|
||||
if (aImage->mPendingError) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If mDecoded or we don't have a decoder, we must have finished already (for
|
||||
// example, a synchronous decode request came while the worker was pending).
|
||||
if (!aImage->mDecoder || aImage->mDecoded) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aImage->mDecoder->NeedsNewFrame()) {
|
||||
if (aStrategy == DecodeStrategy::SYNC) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aImage->mDecoder->AllocateFrame();
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Decoder> decoderKungFuDeathGrip = aImage->mDecoder;
|
||||
|
||||
uint32_t maxBytes;
|
||||
if (aImage->mDecoder->IsSizeDecode()) {
|
||||
// Decode all available data if we're a size decode; they're cheap, and we
|
||||
// want them to be more or less synchronous.
|
||||
maxBytes = aImage->mSourceData.Length();
|
||||
} else {
|
||||
// We're only guaranteed to decode this many bytes, so in particular,
|
||||
// gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
|
||||
// to read the size from most images.
|
||||
maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
|
||||
}
|
||||
|
||||
if (bytesToDecode == 0) {
|
||||
bytesToDecode = aImage->mSourceData.Length() - aImage->mDecoder->BytesDecoded();
|
||||
}
|
||||
|
||||
TimeStamp deadline = TimeStamp::Now() +
|
||||
TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
|
||||
|
||||
// We keep decoding chunks until:
|
||||
// * we don't have any data left to decode,
|
||||
// * the decode completes,
|
||||
// * we're an DecodeUntil::SIZE decode and we get the size, or
|
||||
// * we run out of time.
|
||||
// We also try to decode at least one "chunk" if we've allocated a new frame,
|
||||
// even if we have no more data to send to the decoder.
|
||||
while ((aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded() &&
|
||||
bytesToDecode > 0 &&
|
||||
!aImage->IsDecodeFinished() &&
|
||||
!(aDecodeUntil == DecodeUntil::SIZE && aImage->mHasSize) &&
|
||||
!aImage->mDecoder->NeedsNewFrame()) ||
|
||||
aImage->mDecoder->NeedsToFlushData()) {
|
||||
uint32_t chunkSize = min(bytesToDecode, maxBytes);
|
||||
nsresult rv = aImage->DecodeSomeData(chunkSize, aStrategy);
|
||||
if (NS_FAILED(rv)) {
|
||||
aImage->DoError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
bytesToDecode -= chunkSize;
|
||||
|
||||
// Yield if we've been decoding for too long. We check this _after_ decoding
|
||||
// a chunk to ensure that we don't yield without doing any decoding.
|
||||
if (aDecodeUntil == DecodeUntil::TIME && TimeStamp::Now() >= deadline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
151
image/src/DecodePool.h
Normal file
151
image/src/DecodePool.h
Normal file
@ -0,0 +1,151 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
/**
|
||||
* DecodePool manages the threads used for decoding raster images.
|
||||
*/
|
||||
|
||||
#ifndef MOZILLA_IMAGELIB_DECODEPOOL_H_
|
||||
#define MOZILLA_IMAGELIB_DECODEPOOL_H_
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include <mozilla/TypedEnum.h>
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIObserver.h"
|
||||
|
||||
class nsIThreadPool;
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
class Decoder;
|
||||
class RasterImage;
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DecodeStrategy, uint8_t)
|
||||
// DecodeStrategy::SYNC requests a synchronous decode, which will continue
|
||||
// decoding frames as long as it has more source data. It returns to the
|
||||
// caller only once decoding is complete (or until it needs more source data
|
||||
// before continuing). Because DecodeStrategy::SYNC can involve allocating new
|
||||
// imgFrames, it can only be run on the main thread.
|
||||
SYNC,
|
||||
|
||||
// DecodeStrategy::ASYNC requests an asynchronous decode, which will continue
|
||||
// decoding until it either finishes a frame or runs out of source data.
|
||||
// Because DecodeStrategy::ASYNC does not allocate new imgFrames, it can be
|
||||
// safely run off the main thread. (And hence workers in the decode pool
|
||||
// always use it.)
|
||||
ASYNC
|
||||
MOZ_END_ENUM_CLASS(DecodeStrategy)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t)
|
||||
INACTIVE,
|
||||
PENDING,
|
||||
ACTIVE,
|
||||
WORK_DONE,
|
||||
STOPPED
|
||||
MOZ_END_ENUM_CLASS(DecodeStatus)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DecodeUntil, uint8_t)
|
||||
TIME,
|
||||
SIZE,
|
||||
DONE_BYTES
|
||||
MOZ_END_ENUM_CLASS(DecodeUntil)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(ShutdownReason, uint8_t)
|
||||
DONE,
|
||||
NOT_NEEDED,
|
||||
FATAL_ERROR
|
||||
MOZ_END_ENUM_CLASS(ShutdownReason)
|
||||
|
||||
|
||||
/**
|
||||
* DecodePool is a singleton class we use when decoding large images.
|
||||
*
|
||||
* When we wish to decode an image larger than
|
||||
* image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
|
||||
* for the image. This adds the image to a queue of pending requests and posts
|
||||
* the DecodePool singleton to the event queue, if it's not already pending
|
||||
* there.
|
||||
*
|
||||
* When the DecodePool is run from the event queue, it decodes the image (and
|
||||
* all others it's managing) in chunks, periodically yielding control back to
|
||||
* the event loop.
|
||||
*/
|
||||
class DecodePool : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static DecodePool* Singleton();
|
||||
|
||||
/**
|
||||
* Ask the DecodePool to asynchronously decode this image.
|
||||
*/
|
||||
void RequestDecode(RasterImage* aImage);
|
||||
|
||||
/**
|
||||
* Decode aImage for a short amount of time, and post the remainder to the
|
||||
* queue.
|
||||
*/
|
||||
void DecodeABitOf(RasterImage* aImage, DecodeStrategy aStrategy);
|
||||
|
||||
/**
|
||||
* Ask the DecodePool to stop decoding this image. Internally, we also
|
||||
* call this function when we finish decoding an image.
|
||||
*
|
||||
* Since the DecodePool keeps raw pointers to RasterImages, make sure you
|
||||
* call this before a RasterImage is destroyed!
|
||||
*/
|
||||
static void StopDecoding(RasterImage* aImage);
|
||||
|
||||
/**
|
||||
* Synchronously decode the beginning of the image until we run out of
|
||||
* bytes or we get the image's size. Note that this done on a best-effort
|
||||
* basis; if the size is burried too deep in the image, we'll give up.
|
||||
*
|
||||
* @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note
|
||||
* that we return NS_OK even when the size was not found.)
|
||||
*/
|
||||
nsresult DecodeUntilSizeAvailable(RasterImage* aImage);
|
||||
|
||||
/**
|
||||
* Returns an event target interface to the thread pool; primarily for
|
||||
* OnDataAvailable delivery off main thread.
|
||||
*
|
||||
* @return An nsIEventTarget interface to mThreadPool.
|
||||
*/
|
||||
already_AddRefed<nsIEventTarget> GetEventTarget();
|
||||
|
||||
/**
|
||||
* Decode some chunks of the given image. If aDecodeUntil is SIZE,
|
||||
* decode until we have the image's size, then stop. If bytesToDecode is
|
||||
* non-0, at most bytesToDecode bytes will be decoded. if aDecodeUntil is
|
||||
* DONE_BYTES, decode until all bytesToDecode bytes are decoded.
|
||||
*/
|
||||
nsresult DecodeSomeOfImage(RasterImage* aImage,
|
||||
DecodeStrategy aStrategy,
|
||||
DecodeUntil aDecodeUntil = DecodeUntil::TIME,
|
||||
uint32_t bytesToDecode = 0);
|
||||
|
||||
private:
|
||||
DecodePool();
|
||||
virtual ~DecodePool();
|
||||
|
||||
static StaticRefPtr<DecodePool> sSingleton;
|
||||
|
||||
// mThreadPoolMutex protects mThreadPool. For all RasterImages R,
|
||||
// R::mDecodingMonitor must be acquired before mThreadPoolMutex
|
||||
// if both are acquired; the other order may cause deadlock.
|
||||
Mutex mThreadPoolMutex;
|
||||
nsCOMPtr<nsIThreadPool> mThreadPool;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_IMAGELIB_DECODEPOOL_H_
|
@ -1,36 +0,0 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
/** @file
|
||||
* An enumeration used by RasterImage and Decoder to specify which 'strategy' to
|
||||
* use for image decoding - synchronous or asynchronous.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_imagelib_DecodeStrategy_h_
|
||||
#define mozilla_imagelib_DecodeStrategy_h_
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
enum DecodeStrategy {
|
||||
// DECODE_SYNC requests a synchronous decode, which will continue decoding
|
||||
// frames as long as it has more source data. It returns to the caller only
|
||||
// once decoding is complete (or until it needs more source data before
|
||||
// continuing). Because DECODE_SYNC can involve allocating new imgFrames, it
|
||||
// can only be run on the main thread.
|
||||
DECODE_SYNC,
|
||||
|
||||
// DECODE_ASYNC requests an asynchronous decode, which will continue decoding
|
||||
// until it either finishes a frame or runs out of source data. Because
|
||||
// DECODE_ASYNC does not allocate new imgFrames, it can be safely run off the
|
||||
// main thread. (And hence workers in the decode pool always use it.)
|
||||
DECODE_ASYNC
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -96,7 +96,7 @@ Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
|
||||
PROFILER_LABEL("ImageDecoder", "Write",
|
||||
js::ProfileEntry::Category::GRAPHICS);
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
|
||||
MOZ_ASSERT(NS_IsMainThread() || aStrategy == DecodeStrategy::ASYNC);
|
||||
|
||||
// We're strict about decoder errors
|
||||
MOZ_ASSERT(!HasDecoderError(),
|
||||
@ -129,12 +129,14 @@ Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
|
||||
|
||||
// If we're a synchronous decoder and we need a new frame to proceed, let's
|
||||
// create one and call it again.
|
||||
while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) {
|
||||
nsresult rv = AllocateFrame();
|
||||
if (aStrategy == DecodeStrategy::SYNC) {
|
||||
while (NeedsNewFrame() && !HasDataError()) {
|
||||
nsresult rv = AllocateFrame();
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Tell the decoder to use the data it saved when it asked for a new frame.
|
||||
WriteInternal(nullptr, 0, aStrategy);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Use the data we saved when we asked for a new frame.
|
||||
WriteInternal(nullptr, 0, aStrategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +145,7 @@ Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
|
||||
}
|
||||
|
||||
void
|
||||
Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
|
||||
Decoder::Finish(ShutdownReason aReason)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -180,7 +182,7 @@ Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
|
||||
}
|
||||
|
||||
bool usable = !HasDecoderError();
|
||||
if (aShutdownIntent != RasterImage::eShutdownIntent_NotNeeded && !HasDecoderError()) {
|
||||
if (aReason != ShutdownReason::NOT_NEEDED && !HasDecoderError()) {
|
||||
// If we only have a data error, we're usable if we have at least one complete frame.
|
||||
if (GetCompleteFrameCount() == 0) {
|
||||
usable = false;
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "RasterImage.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "DecodeStrategy.h"
|
||||
#include "DecodePool.h"
|
||||
#include "ImageMetadata.h"
|
||||
#include "Orientation.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -62,7 +62,7 @@ public:
|
||||
*
|
||||
* Notifications Sent: TODO
|
||||
*/
|
||||
void Finish(RasterImage::eShutdownIntent aShutdownIntent);
|
||||
void Finish(ShutdownReason aReason);
|
||||
|
||||
/**
|
||||
* Informs the shared decoder that all the data has been written.
|
||||
|
@ -20,9 +20,6 @@
|
||||
#include "ImageRegion.h"
|
||||
#include "Layers.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "SurfaceCache.h"
|
||||
#include "FrameAnimator.h"
|
||||
|
||||
@ -51,10 +48,6 @@
|
||||
#include "gfxPrefs.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
@ -296,7 +289,6 @@ private:
|
||||
ScaleState mState;
|
||||
};
|
||||
|
||||
/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
|
||||
static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
|
||||
|
||||
#ifndef DEBUG
|
||||
@ -1545,7 +1537,7 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
||||
// write the data directly to the decoder. (If we haven't gotten the size,
|
||||
// we'll queue up the data and write it out when we do.)
|
||||
if (!StoringSourceData() && mHasSize) {
|
||||
rv = WriteToDecoder(aBuffer, aCount, DECODE_SYNC);
|
||||
rv = WriteToDecoder(aBuffer, aCount, DecodeStrategy::SYNC);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
|
||||
rv = FinishedSomeDecoding();
|
||||
@ -1665,7 +1657,7 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus, bo
|
||||
// We just recorded OnStopRequest; we need to inform our listeners.
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
|
||||
FinishedSomeDecoding(eShutdownIntent_Done,
|
||||
FinishedSomeDecoding(ShutdownReason::DONE,
|
||||
LoadCompleteProgress(aLastPart, mError, finalStatus));
|
||||
}
|
||||
|
||||
@ -1745,6 +1737,12 @@ RasterImage::OnNewSourceData()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIEventTarget>
|
||||
RasterImage::GetEventTarget()
|
||||
{
|
||||
return DecodePool::Singleton()->GetEventTarget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::SetSourceSizeHint(uint32_t sizeHint)
|
||||
{
|
||||
@ -1989,20 +1987,17 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
|
||||
// state. It is an error to call this function when there is no initialized
|
||||
// decoder.
|
||||
//
|
||||
// aIntent specifies the intent of the shutdown. If aIntent is
|
||||
// eShutdownIntent_Done, an error is flagged if we didn't get what we should
|
||||
// have out of the decode. If aIntent is eShutdownIntent_NotNeeded, we don't
|
||||
// check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
|
||||
// aReason specifies why the shutdown is happening. If aReason is
|
||||
// ShutdownReason::DONE, an error is flagged if we didn't get what we should
|
||||
// have out of the decode. If aReason is ShutdownReason::NOT_NEEDED, we don't
|
||||
// check this. If aReason is ShutdownReason::FATAL_ERROR, we shut down in error
|
||||
// mode.
|
||||
nsresult
|
||||
RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
||||
RasterImage::ShutdownDecoder(ShutdownReason aReason)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// Ensure that our intent is valid
|
||||
NS_ABORT_IF_FALSE((aIntent >= 0) && (aIntent < eShutdownIntent_AllCount),
|
||||
"Invalid shutdown intent");
|
||||
|
||||
// Ensure that the decoder is initialized
|
||||
NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
|
||||
|
||||
@ -2015,7 +2010,7 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
||||
nsRefPtr<Decoder> decoder = mDecoder;
|
||||
mDecoder = nullptr;
|
||||
|
||||
decoder->Finish(aIntent);
|
||||
decoder->Finish(aReason);
|
||||
|
||||
// Unlock the last frame (if we have any). Our invariant is that, while we
|
||||
// have a decoder open, the last frame is always locked.
|
||||
@ -2036,12 +2031,8 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
||||
|
||||
// We just shut down the decoder. If we didn't get what we want, but expected
|
||||
// to, flag an error
|
||||
bool failed = false;
|
||||
if (wasSizeDecode && !mHasSize)
|
||||
failed = true;
|
||||
if (!wasSizeDecode && !mDecoded)
|
||||
failed = true;
|
||||
if ((aIntent == eShutdownIntent_Done) && failed) {
|
||||
bool succeeded = wasSizeDecode ? mHasSize : mDecoded;
|
||||
if ((aReason == ShutdownReason::DONE) && !succeeded) {
|
||||
DoError();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -2223,7 +2214,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
||||
// If we have a size decode open, interrupt it and shut it down; or if
|
||||
// the decoder has different flags than what we need
|
||||
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
|
||||
nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
||||
nsresult rv = FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
@ -2250,7 +2241,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
||||
PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf",
|
||||
js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
|
||||
|
||||
DecodePool::Singleton()->DecodeABitOf(this, DECODE_SYNC);
|
||||
DecodePool::Singleton()->DecodeABitOf(this, DecodeStrategy::SYNC);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2309,7 +2300,7 @@ RasterImage::SyncDecode()
|
||||
// If we have a decoder open with different flags than what we need, shut it
|
||||
// down
|
||||
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
|
||||
nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
||||
nsresult rv = FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
|
||||
if (mDecoded) {
|
||||
@ -2337,7 +2328,7 @@ RasterImage::SyncDecode()
|
||||
|
||||
// Write everything we have
|
||||
rv = DecodeSomeData(mSourceData.Length() - mDecoder->BytesDecoded(),
|
||||
DECODE_SYNC);
|
||||
DecodeStrategy::SYNC);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
|
||||
rv = FinishedSomeDecoding();
|
||||
@ -2652,7 +2643,7 @@ RasterImage::UnlockImage()
|
||||
("RasterImage[0x%p] canceling decode because image "
|
||||
"is now unlocked.", this));
|
||||
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
|
||||
FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
||||
FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
|
||||
ForceDiscard();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2773,7 +2764,7 @@ RasterImage::DoError()
|
||||
|
||||
// If we're mid-decode, shut down the decoder.
|
||||
if (mDecoder) {
|
||||
FinishedSomeDecoding(eShutdownIntent_Error);
|
||||
FinishedSomeDecoding(ShutdownReason::FATAL_ERROR);
|
||||
}
|
||||
|
||||
// Put the container in an error state.
|
||||
@ -2859,7 +2850,7 @@ RasterImage::GetFramesNotified(uint32_t *aFramesNotified)
|
||||
|
||||
nsresult
|
||||
RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
|
||||
eShutdownIntent aIntent,
|
||||
ShutdownReason aReason,
|
||||
bool aDone,
|
||||
bool aWasSize)
|
||||
{
|
||||
@ -2867,7 +2858,7 @@ RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
|
||||
|
||||
// If we were a size decode and a full decode was requested, now's the time.
|
||||
if (NS_SUCCEEDED(aStatus) &&
|
||||
aIntent == eShutdownIntent_Done &&
|
||||
aReason == ShutdownReason::DONE &&
|
||||
aDone &&
|
||||
aWasSize &&
|
||||
mWantFullDecode) {
|
||||
@ -2885,7 +2876,7 @@ RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */,
|
||||
RasterImage::FinishedSomeDecoding(ShutdownReason aReason /* = ShutdownReason::DONE */,
|
||||
Progress aProgress /* = NoProgress */)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@ -2917,7 +2908,7 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
||||
|
||||
// If the decode finished, or we're specifically being told to shut down,
|
||||
// tell the image and shut down the decoder.
|
||||
if (image->IsDecodeFinished() || aIntent != eShutdownIntent_Done) {
|
||||
if (image->IsDecodeFinished() || aReason != ShutdownReason::DONE) {
|
||||
done = true;
|
||||
|
||||
// Hold on to a reference to the decoder until we're done with it
|
||||
@ -2942,7 +2933,7 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
||||
|
||||
// We need to shut down the decoder first, in order to ensure all
|
||||
// decoding routines have been finished.
|
||||
rv = image->ShutdownDecoder(aIntent);
|
||||
rv = image->ShutdownDecoder(aReason);
|
||||
if (NS_FAILED(rv)) {
|
||||
image->DoError();
|
||||
}
|
||||
@ -2994,7 +2985,7 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
||||
}
|
||||
}
|
||||
|
||||
return RequestDecodeIfNeeded(rv, aIntent, done, wasSize);
|
||||
return RequestDecodeIfNeeded(rv, aReason, done, wasSize);
|
||||
}
|
||||
|
||||
already_AddRefed<imgIContainer>
|
||||
@ -3004,447 +2995,6 @@ RasterImage::Unwrap()
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(RasterImage::DecodePool,
|
||||
nsIObserver)
|
||||
|
||||
/* static */ RasterImage::DecodePool*
|
||||
RasterImage::DecodePool::Singleton()
|
||||
{
|
||||
if (!sSingleton) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sSingleton = new DecodePool();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
RasterImage::DecodePool::GetEventTarget()
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
|
||||
class RIDThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITHREADPOOLLISTENER
|
||||
|
||||
RIDThreadPoolListener() {}
|
||||
~RIDThreadPoolListener() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(RIDThreadPoolListener, nsIThreadPoolListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
RIDThreadPoolListener::OnThreadCreated()
|
||||
{
|
||||
if (IsNuwaProcess()) {
|
||||
NuwaMarkCurrentThread((void (*)(void *))nullptr, nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RIDThreadPoolListener::OnThreadShuttingDown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
RasterImage::DecodePool::DecodePool()
|
||||
: mThreadPoolMutex("Thread Pool")
|
||||
{
|
||||
if (gfxPrefs::ImageMTDecodingEnabled()) {
|
||||
mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
|
||||
if (mThreadPool) {
|
||||
mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
|
||||
int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
|
||||
uint32_t limit;
|
||||
if (prefLimit <= 0) {
|
||||
limit = std::max(PR_GetNumberOfProcessors(), 2) - 1;
|
||||
} else {
|
||||
limit = static_cast<uint32_t>(prefLimit);
|
||||
}
|
||||
|
||||
mThreadPool->SetThreadLimit(limit);
|
||||
mThreadPool->SetIdleThreadLimit(limit);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
mThreadPool->SetListener(new RIDThreadPoolListener());
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
|
||||
if (obsSvc) {
|
||||
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RasterImage::DecodePool::~DecodePool()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::DecodePool::Observe(nsISupports *subject, const char *topic,
|
||||
const char16_t *data)
|
||||
{
|
||||
NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
|
||||
|
||||
nsCOMPtr<nsIThreadPool> threadPool;
|
||||
|
||||
{
|
||||
MutexAutoLock threadPoolLock(mThreadPoolMutex);
|
||||
threadPool = mThreadPool;
|
||||
mThreadPool = nullptr;
|
||||
}
|
||||
|
||||
if (threadPool) {
|
||||
threadPool->Shutdown();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodePool::RequestDecode(RasterImage* aImg)
|
||||
{
|
||||
MOZ_ASSERT(aImg->mDecoder);
|
||||
aImg->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If we're currently waiting on a new frame for this image, we can't do any
|
||||
// decoding.
|
||||
if (!aImg->mDecoder->NeedsNewFrame()) {
|
||||
if (aImg->mDecodeStatus == DecodeStatus::PENDING ||
|
||||
aImg->mDecodeStatus == DecodeStatus::ACTIVE) {
|
||||
// The image is already in our list of images to decode, or currently being
|
||||
// decoded, so we don't have to do anything else.
|
||||
return;
|
||||
}
|
||||
|
||||
aImg->mDecodeStatus = DecodeStatus::PENDING;
|
||||
nsRefPtr<DecodeJob> job = new DecodeJob(aImg);
|
||||
|
||||
MutexAutoLock threadPoolLock(mThreadPoolMutex);
|
||||
if (!gfxPrefs::ImageMTDecodingEnabled() || !mThreadPool) {
|
||||
NS_DispatchToMainThread(job);
|
||||
} else {
|
||||
mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aImg->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImg->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
||||
aImg->FinishedSomeDecoding();
|
||||
}
|
||||
|
||||
DecodeSomeOfImage(aImg, aStrategy);
|
||||
|
||||
aImg->FinishedSomeDecoding();
|
||||
|
||||
// If the decoder needs a new frame, enqueue an event to get it; that event
|
||||
// will enqueue another decode request when it's done.
|
||||
if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
|
||||
FrameNeededWorker::GetNewFrame(aImg);
|
||||
} else {
|
||||
// If we aren't yet finished decoding and we have more data in hand, add
|
||||
// this request to the back of the priority list.
|
||||
if (aImg->mDecoder &&
|
||||
!aImg->mError &&
|
||||
!aImg->IsDecodeFinished() &&
|
||||
aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded()) {
|
||||
RequestDecode(aImg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
|
||||
{
|
||||
aImg->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If we haven't got a decode request, we're not currently decoding. (Having
|
||||
// a decode request doesn't imply we *are* decoding, though.)
|
||||
aImg->mDecodeStatus = DecodeStatus::STOPPED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::DecodePool::DecodeJob::Run()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
|
||||
// If we were interrupted, we shouldn't do any work.
|
||||
if (mImage->mDecodeStatus == DecodeStatus::STOPPED) {
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If someone came along and synchronously decoded us, there's nothing for us to do.
|
||||
if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we're a decode job that's been enqueued since a previous decode that
|
||||
// still needs a new frame, we can't do anything. Wait until the
|
||||
// FrameNeededWorker enqueues another frame.
|
||||
if (mImage->mDecoder->NeedsNewFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mImage->mDecodeStatus = DecodeStatus::ACTIVE;
|
||||
|
||||
size_t oldByteCount = mImage->mDecoder->BytesDecoded();
|
||||
|
||||
DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES;
|
||||
|
||||
// Multithreaded decoding can be disabled. If we've done so, we don't want to
|
||||
// monopolize the main thread, and will allow a timeout in DecodeSomeOfImage.
|
||||
if (NS_IsMainThread()) {
|
||||
type = DECODE_TYPE_UNTIL_TIME;
|
||||
}
|
||||
|
||||
size_t maxBytes = mImage->mSourceData.Length() -
|
||||
mImage->mDecoder->BytesDecoded();
|
||||
DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC,
|
||||
type, maxBytes);
|
||||
|
||||
size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
|
||||
|
||||
mImage->mDecodeStatus = DecodeStatus::WORK_DONE;
|
||||
|
||||
// If the decoder needs a new frame, enqueue an event to get it; that event
|
||||
// will enqueue another decode request when it's done.
|
||||
if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
|
||||
FrameNeededWorker::GetNewFrame(mImage);
|
||||
}
|
||||
// If we aren't yet finished decoding and we have more data in hand, add
|
||||
// this request to the back of the list.
|
||||
else if (mImage->mDecoder &&
|
||||
!mImage->mError &&
|
||||
!mImage->mPendingError &&
|
||||
!mImage->IsDecodeFinished() &&
|
||||
bytesDecoded < maxBytes &&
|
||||
bytesDecoded > 0) {
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
} else {
|
||||
// Nothing more for us to do - let everyone know what happened.
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RasterImage::DecodePool::DecodeJob::~DecodeJob()
|
||||
{
|
||||
if (gfxPrefs::ImageMTDecodingEnabled()) {
|
||||
// Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
|
||||
if (mainThread) {
|
||||
// Handle ambiguous nsISupports inheritance
|
||||
RasterImage* rawImg = nullptr;
|
||||
mImage.swap(rawImg);
|
||||
DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter lock(aImg->mDecodingMonitor);
|
||||
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImg->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
||||
nsresult rv = aImg->FinishedSomeDecoding();
|
||||
if (NS_FAILED(rv)) {
|
||||
aImg->DoError();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// We use DECODE_ASYNC here because we just want to get the size information
|
||||
// here and defer the rest of the work.
|
||||
nsresult rv = DecodeSomeOfImage(aImg, DECODE_ASYNC, DECODE_TYPE_UNTIL_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If the decoder needs a new frame, enqueue an event to get it; that event
|
||||
// will enqueue another decode request when it's done.
|
||||
if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
|
||||
FrameNeededWorker::GetNewFrame(aImg);
|
||||
} else {
|
||||
rv = aImg->FinishedSomeDecoding();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg,
|
||||
DecodeStrategy aStrategy,
|
||||
DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */,
|
||||
uint32_t bytesToDecode /* = 0 */)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aImg->mInitialized,
|
||||
"Worker active for uninitialized container!");
|
||||
aImg->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
// If an error is flagged, it probably happened while we were waiting
|
||||
// in the event queue.
|
||||
if (aImg->mError)
|
||||
return NS_OK;
|
||||
|
||||
// If there is an error worker pending (say because the main thread has enqueued
|
||||
// another decode request for us before processing the error worker) then bail out.
|
||||
if (aImg->mPendingError)
|
||||
return NS_OK;
|
||||
|
||||
// If mDecoded or we don't have a decoder, we must have finished already (for
|
||||
// example, a synchronous decode request came while the worker was pending).
|
||||
if (!aImg->mDecoder || aImg->mDecoded)
|
||||
return NS_OK;
|
||||
|
||||
// If we're doing synchronous decodes, and we're waiting on a new frame for
|
||||
// this image, get it now.
|
||||
if (aStrategy == DECODE_SYNC && aImg->mDecoder->NeedsNewFrame()) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aImg->mDecoder->AllocateFrame();
|
||||
}
|
||||
|
||||
// If we're not synchronous, we can't allocate a frame right now.
|
||||
else if (aImg->mDecoder->NeedsNewFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder;
|
||||
|
||||
uint32_t maxBytes;
|
||||
if (aImg->mDecoder->IsSizeDecode()) {
|
||||
// Decode all available data if we're a size decode; they're cheap, and we
|
||||
// want them to be more or less synchronous.
|
||||
maxBytes = aImg->mSourceData.Length();
|
||||
} else {
|
||||
// We're only guaranteed to decode this many bytes, so in particular,
|
||||
// gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
|
||||
// to read the size from most images.
|
||||
maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
|
||||
}
|
||||
|
||||
if (bytesToDecode == 0) {
|
||||
bytesToDecode = aImg->mSourceData.Length() - aImg->mDecoder->BytesDecoded();
|
||||
}
|
||||
|
||||
TimeStamp deadline = TimeStamp::Now() +
|
||||
TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
|
||||
|
||||
// We keep decoding chunks until:
|
||||
// * we don't have any data left to decode,
|
||||
// * the decode completes,
|
||||
// * we're an UNTIL_SIZE decode and we get the size, or
|
||||
// * we run out of time.
|
||||
// We also try to decode at least one "chunk" if we've allocated a new frame,
|
||||
// even if we have no more data to send to the decoder.
|
||||
while ((aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded() &&
|
||||
bytesToDecode > 0 &&
|
||||
!aImg->IsDecodeFinished() &&
|
||||
!(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize) &&
|
||||
!aImg->mDecoder->NeedsNewFrame()) ||
|
||||
aImg->mDecoder->NeedsToFlushData()) {
|
||||
uint32_t chunkSize = std::min(bytesToDecode, maxBytes);
|
||||
nsresult rv = aImg->DecodeSomeData(chunkSize, aStrategy);
|
||||
if (NS_FAILED(rv)) {
|
||||
aImg->DoError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
bytesToDecode -= chunkSize;
|
||||
|
||||
// Yield if we've been decoding for too long. We check this _after_ decoding
|
||||
// a chunk to ensure that we don't yield without doing any decoding.
|
||||
if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline)
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
{ }
|
||||
|
||||
void
|
||||
RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* aImage)
|
||||
{
|
||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
||||
nsCOMPtr<nsIRunnable> worker = new DecodeDoneWorker(aImage);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::DecodeDoneWorker::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
|
||||
mImage->FinishedSomeDecoding(eShutdownIntent_Done);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RasterImage::FrameNeededWorker::FrameNeededWorker(RasterImage* image)
|
||||
: mImage(image)
|
||||
{}
|
||||
|
||||
|
||||
void
|
||||
RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> worker = new FrameNeededWorker(image);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::FrameNeededWorker::Run()
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// If we got a synchronous decode in the mean time, we don't need to do
|
||||
// anything.
|
||||
if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
|
||||
rv = mImage->mDecoder->AllocateFrame();
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
|
||||
// By definition, we're not done decoding, so enqueue us for more decoding.
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
||||
GraphicsFilter aFilter, uint32_t aFlags)
|
||||
|
@ -25,16 +25,14 @@
|
||||
#include "nsTArray.h"
|
||||
#include "imgFrame.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "DecodeStrategy.h"
|
||||
#include "DecodePool.h"
|
||||
#include "DiscardTracker.h"
|
||||
#include "Orientation.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TypedEnum.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#ifdef DEBUG
|
||||
@ -135,15 +133,6 @@ namespace image {
|
||||
|
||||
class Decoder;
|
||||
class FrameAnimator;
|
||||
class ScaleRunner;
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t)
|
||||
INACTIVE,
|
||||
PENDING,
|
||||
ACTIVE,
|
||||
WORK_DONE,
|
||||
STOPPED
|
||||
MOZ_END_ENUM_CLASS(DecodeStatus)
|
||||
|
||||
class RasterImage MOZ_FINAL : public ImageResource
|
||||
, public nsIProperties
|
||||
@ -260,9 +249,7 @@ public:
|
||||
bool aLastPart) MOZ_OVERRIDE;
|
||||
virtual nsresult OnNewSourceData() MOZ_OVERRIDE;
|
||||
|
||||
static already_AddRefed<nsIEventTarget> GetEventTarget() {
|
||||
return DecodePool::Singleton()->GetEventTarget();
|
||||
}
|
||||
static already_AddRefed<nsIEventTarget> GetEventTarget();
|
||||
|
||||
/**
|
||||
* A hint of the number of bytes of source data that the image contains. If
|
||||
@ -294,8 +281,6 @@ public:
|
||||
return mRequestedSampleSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
nsCString GetURIString() {
|
||||
nsCString spec;
|
||||
if (GetURI()) {
|
||||
@ -304,168 +289,15 @@ public:
|
||||
return spec;
|
||||
}
|
||||
|
||||
// Called from module startup. Sets up RasterImage to be used.
|
||||
static void Initialize();
|
||||
|
||||
// Decoder shutdown
|
||||
enum eShutdownIntent {
|
||||
eShutdownIntent_Done = 0,
|
||||
eShutdownIntent_NotNeeded = 1,
|
||||
eShutdownIntent_Error = 2,
|
||||
eShutdownIntent_AllCount = 3
|
||||
};
|
||||
|
||||
private:
|
||||
/*
|
||||
* DecodePool is a singleton class we use when decoding large images.
|
||||
*
|
||||
* When we wish to decode an image larger than
|
||||
* image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
|
||||
* for the image. This adds the image to a queue of pending requests and posts
|
||||
* the DecodePool singleton to the event queue, if it's not already pending
|
||||
* there.
|
||||
*
|
||||
* When the DecodePool is run from the event queue, it decodes the image (and
|
||||
* all others it's managing) in chunks, periodically yielding control back to
|
||||
* the event loop.
|
||||
*/
|
||||
class DecodePool : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
friend class DecodePool;
|
||||
friend class DecodeWorker;
|
||||
friend class FrameNeededWorker;
|
||||
friend class NotifyProgressWorker;
|
||||
|
||||
static DecodePool* Singleton();
|
||||
|
||||
/**
|
||||
* Ask the DecodePool to asynchronously decode this image.
|
||||
*/
|
||||
void RequestDecode(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Decode aImg for a short amount of time, and post the remainder to the
|
||||
* queue.
|
||||
*/
|
||||
void DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy);
|
||||
|
||||
/**
|
||||
* Ask the DecodePool to stop decoding this image. Internally, we also
|
||||
* call this function when we finish decoding an image.
|
||||
*
|
||||
* Since the DecodePool keeps raw pointers to RasterImages, make sure you
|
||||
* call this before a RasterImage is destroyed!
|
||||
*/
|
||||
static void StopDecoding(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Synchronously decode the beginning of the image until we run out of
|
||||
* bytes or we get the image's size. Note that this done on a best-effort
|
||||
* basis; if the size is burried too deep in the image, we'll give up.
|
||||
*
|
||||
* @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note
|
||||
* that we return NS_OK even when the size was not found.)
|
||||
*/
|
||||
nsresult DecodeUntilSizeAvailable(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Returns an event target interface to the thread pool; primarily for
|
||||
* OnDataAvailable delivery off main thread.
|
||||
*
|
||||
* @return An nsIEventTarget interface to mThreadPool.
|
||||
*/
|
||||
already_AddRefed<nsIEventTarget> GetEventTarget();
|
||||
|
||||
private: /* statics */
|
||||
static StaticRefPtr<DecodePool> sSingleton;
|
||||
|
||||
private: /* methods */
|
||||
DecodePool();
|
||||
virtual ~DecodePool();
|
||||
|
||||
enum DecodeType {
|
||||
DECODE_TYPE_UNTIL_TIME,
|
||||
DECODE_TYPE_UNTIL_SIZE,
|
||||
DECODE_TYPE_UNTIL_DONE_BYTES
|
||||
};
|
||||
|
||||
/* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE,
|
||||
* decode until we have the image's size, then stop. If bytesToDecode is
|
||||
* non-0, at most bytesToDecode bytes will be decoded. if aDecodeType is
|
||||
* UNTIL_DONE_BYTES, decode until all bytesToDecode bytes are decoded.
|
||||
*/
|
||||
nsresult DecodeSomeOfImage(RasterImage* aImg,
|
||||
DecodeStrategy aStrategy,
|
||||
DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME,
|
||||
uint32_t bytesToDecode = 0);
|
||||
|
||||
/* A decode job dispatched to a thread pool by DecodePool.
|
||||
*/
|
||||
class DecodeJob : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DecodeJob(RasterImage* aImage) : mImage(aImage) { }
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual ~DecodeJob();
|
||||
|
||||
private:
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
};
|
||||
|
||||
private: /* members */
|
||||
|
||||
// mThreadPoolMutex protects mThreadPool. For all RasterImages R,
|
||||
// R::mDecodingMonitor must be acquired before mThreadPoolMutex
|
||||
// if both are acquired; the other order may cause deadlock.
|
||||
Mutex mThreadPoolMutex;
|
||||
nsCOMPtr<nsIThreadPool> mThreadPool;
|
||||
};
|
||||
|
||||
class DecodeDoneWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Called by the DecodePool with an image when it's done some significant
|
||||
* portion of decoding that needs to be notified about.
|
||||
*
|
||||
* Ensures the decode state accumulated by the decoding process gets
|
||||
* applied to the image.
|
||||
*/
|
||||
static void NotifyFinishedSomeDecoding(RasterImage* aImage);
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
private:
|
||||
DecodeDoneWorker(RasterImage* aImage);
|
||||
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
};
|
||||
|
||||
class FrameNeededWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Called by the DecodeJob with an image when it's been told by the
|
||||
* decoder that it needs a new frame to be allocated on the main thread.
|
||||
*
|
||||
* Dispatches an event to do so, which will further dispatch a
|
||||
* RequestDecode event to continue decoding.
|
||||
*/
|
||||
static void GetNewFrame(RasterImage* image);
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
private: /* methods */
|
||||
explicit FrameNeededWorker(RasterImage* image);
|
||||
|
||||
private: /* members */
|
||||
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
};
|
||||
|
||||
nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
|
||||
nsresult FinishedSomeDecoding(ShutdownReason aReason = ShutdownReason::DONE,
|
||||
Progress aProgress = NoProgress);
|
||||
|
||||
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
||||
@ -624,10 +456,8 @@ private: // data
|
||||
bool mPendingError:1;
|
||||
|
||||
// Decoding
|
||||
nsresult RequestDecodeIfNeeded(nsresult aStatus,
|
||||
eShutdownIntent aIntent,
|
||||
bool aDone,
|
||||
bool aWasSize);
|
||||
nsresult RequestDecodeIfNeeded(nsresult aStatus, ShutdownReason aReason,
|
||||
bool aDone, bool aWasSize);
|
||||
nsresult WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
||||
nsresult SyncDecode();
|
||||
nsresult InitDecoder(bool aDoSizeDecode);
|
||||
@ -639,7 +469,7 @@ private: // data
|
||||
// Initializes ProgressTracker and resets it on RasterImage destruction.
|
||||
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
|
||||
|
||||
nsresult ShutdownDecoder(eShutdownIntent aIntent);
|
||||
nsresult ShutdownDecoder(ShutdownReason aReason);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -16,6 +16,7 @@ EXPORTS += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ClippedImage.cpp',
|
||||
'DecodePool.cpp',
|
||||
'Decoder.cpp',
|
||||
'DiscardTracker.cpp',
|
||||
'DynamicImage.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user