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
8a2d5edd9f
commit
dc37edcda5
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",
|
PROFILER_LABEL("ImageDecoder", "Write",
|
||||||
js::ProfileEntry::Category::GRAPHICS);
|
js::ProfileEntry::Category::GRAPHICS);
|
||||||
|
|
||||||
MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
|
MOZ_ASSERT(NS_IsMainThread() || aStrategy == DecodeStrategy::ASYNC);
|
||||||
|
|
||||||
// We're strict about decoder errors
|
// We're strict about decoder errors
|
||||||
MOZ_ASSERT(!HasDecoderError(),
|
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
|
// If we're a synchronous decoder and we need a new frame to proceed, let's
|
||||||
// create one and call it again.
|
// create one and call it again.
|
||||||
while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) {
|
if (aStrategy == DecodeStrategy::SYNC) {
|
||||||
nsresult rv = AllocateFrame();
|
while (NeedsNewFrame() && !HasDataError()) {
|
||||||
|
nsresult rv = AllocateFrame();
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
// Tell the decoder to use the data it saved when it asked for a new frame.
|
// Use the data we saved when we asked for a new frame.
|
||||||
WriteInternal(nullptr, 0, aStrategy);
|
WriteInternal(nullptr, 0, aStrategy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +145,7 @@ Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
|
Decoder::Finish(ShutdownReason aReason)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
@ -180,7 +182,7 @@ Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool usable = !HasDecoderError();
|
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 we only have a data error, we're usable if we have at least one complete frame.
|
||||||
if (GetCompleteFrameCount() == 0) {
|
if (GetCompleteFrameCount() == 0) {
|
||||||
usable = false;
|
usable = false;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "RasterImage.h"
|
#include "RasterImage.h"
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "DecodeStrategy.h"
|
#include "DecodePool.h"
|
||||||
#include "ImageMetadata.h"
|
#include "ImageMetadata.h"
|
||||||
#include "Orientation.h"
|
#include "Orientation.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
@ -62,7 +62,7 @@ public:
|
|||||||
*
|
*
|
||||||
* Notifications Sent: TODO
|
* Notifications Sent: TODO
|
||||||
*/
|
*/
|
||||||
void Finish(RasterImage::eShutdownIntent aShutdownIntent);
|
void Finish(ShutdownReason aReason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the shared decoder that all the data has been written.
|
* Informs the shared decoder that all the data has been written.
|
||||||
|
@ -20,9 +20,6 @@
|
|||||||
#include "ImageRegion.h"
|
#include "ImageRegion.h"
|
||||||
#include "Layers.h"
|
#include "Layers.h"
|
||||||
#include "nsPresContext.h"
|
#include "nsPresContext.h"
|
||||||
#include "nsIThreadPool.h"
|
|
||||||
#include "nsXPCOMCIDInternal.h"
|
|
||||||
#include "nsIObserverService.h"
|
|
||||||
#include "SurfaceCache.h"
|
#include "SurfaceCache.h"
|
||||||
#include "FrameAnimator.h"
|
#include "FrameAnimator.h"
|
||||||
|
|
||||||
@ -51,10 +48,6 @@
|
|||||||
#include "gfxPrefs.h"
|
#include "gfxPrefs.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#ifdef MOZ_NUWA_PROCESS
|
|
||||||
#include "ipc/Nuwa.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
using namespace gfx;
|
using namespace gfx;
|
||||||
@ -296,7 +289,6 @@ private:
|
|||||||
ScaleState mState;
|
ScaleState mState;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
|
|
||||||
static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
|
static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
|
||||||
|
|
||||||
#ifndef DEBUG
|
#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,
|
// 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.)
|
// we'll queue up the data and write it out when we do.)
|
||||||
if (!StoringSourceData() && mHasSize) {
|
if (!StoringSourceData() && mHasSize) {
|
||||||
rv = WriteToDecoder(aBuffer, aCount, DECODE_SYNC);
|
rv = WriteToDecoder(aBuffer, aCount, DecodeStrategy::SYNC);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
|
|
||||||
rv = FinishedSomeDecoding();
|
rv = FinishedSomeDecoding();
|
||||||
@ -1665,7 +1657,7 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus, bo
|
|||||||
// We just recorded OnStopRequest; we need to inform our listeners.
|
// We just recorded OnStopRequest; we need to inform our listeners.
|
||||||
{
|
{
|
||||||
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
|
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
|
||||||
FinishedSomeDecoding(eShutdownIntent_Done,
|
FinishedSomeDecoding(ShutdownReason::DONE,
|
||||||
LoadCompleteProgress(aLastPart, mError, finalStatus));
|
LoadCompleteProgress(aLastPart, mError, finalStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1745,6 +1737,12 @@ RasterImage::OnNewSourceData()
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ already_AddRefed<nsIEventTarget>
|
||||||
|
RasterImage::GetEventTarget()
|
||||||
|
{
|
||||||
|
return DecodePool::Singleton()->GetEventTarget();
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
RasterImage::SetSourceSizeHint(uint32_t sizeHint)
|
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
|
// state. It is an error to call this function when there is no initialized
|
||||||
// decoder.
|
// decoder.
|
||||||
//
|
//
|
||||||
// aIntent specifies the intent of the shutdown. If aIntent is
|
// aReason specifies why the shutdown is happening. If aReason is
|
||||||
// eShutdownIntent_Done, an error is flagged if we didn't get what we should
|
// ShutdownReason::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
|
// have out of the decode. If aReason is ShutdownReason::NOT_NEEDED, we don't
|
||||||
// check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
|
// check this. If aReason is ShutdownReason::FATAL_ERROR, we shut down in error
|
||||||
|
// mode.
|
||||||
nsresult
|
nsresult
|
||||||
RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
RasterImage::ShutdownDecoder(ShutdownReason aReason)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
mDecodingMonitor.AssertCurrentThreadIn();
|
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
|
// Ensure that the decoder is initialized
|
||||||
NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
|
NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
|
||||||
|
|
||||||
@ -2015,7 +2010,7 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
|||||||
nsRefPtr<Decoder> decoder = mDecoder;
|
nsRefPtr<Decoder> decoder = mDecoder;
|
||||||
mDecoder = nullptr;
|
mDecoder = nullptr;
|
||||||
|
|
||||||
decoder->Finish(aIntent);
|
decoder->Finish(aReason);
|
||||||
|
|
||||||
// Unlock the last frame (if we have any). Our invariant is that, while we
|
// Unlock the last frame (if we have any). Our invariant is that, while we
|
||||||
// have a decoder open, the last frame is always locked.
|
// 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
|
// We just shut down the decoder. If we didn't get what we want, but expected
|
||||||
// to, flag an error
|
// to, flag an error
|
||||||
bool failed = false;
|
bool succeeded = wasSizeDecode ? mHasSize : mDecoded;
|
||||||
if (wasSizeDecode && !mHasSize)
|
if ((aReason == ShutdownReason::DONE) && !succeeded) {
|
||||||
failed = true;
|
|
||||||
if (!wasSizeDecode && !mDecoded)
|
|
||||||
failed = true;
|
|
||||||
if ((aIntent == eShutdownIntent_Done) && failed) {
|
|
||||||
DoError();
|
DoError();
|
||||||
return NS_ERROR_FAILURE;
|
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
|
// If we have a size decode open, interrupt it and shut it down; or if
|
||||||
// the decoder has different flags than what we need
|
// the decoder has different flags than what we need
|
||||||
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
|
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
|
||||||
nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
nsresult rv = FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2250,7 +2241,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||||||
PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf",
|
PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf",
|
||||||
js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
|
js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
|
||||||
|
|
||||||
DecodePool::Singleton()->DecodeABitOf(this, DECODE_SYNC);
|
DecodePool::Singleton()->DecodeABitOf(this, DecodeStrategy::SYNC);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2309,7 +2300,7 @@ RasterImage::SyncDecode()
|
|||||||
// If we have a decoder open with different flags than what we need, shut it
|
// If we have a decoder open with different flags than what we need, shut it
|
||||||
// down
|
// down
|
||||||
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
|
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
|
||||||
nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
nsresult rv = FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
|
|
||||||
if (mDecoded) {
|
if (mDecoded) {
|
||||||
@ -2337,7 +2328,7 @@ RasterImage::SyncDecode()
|
|||||||
|
|
||||||
// Write everything we have
|
// Write everything we have
|
||||||
rv = DecodeSomeData(mSourceData.Length() - mDecoder->BytesDecoded(),
|
rv = DecodeSomeData(mSourceData.Length() - mDecoder->BytesDecoded(),
|
||||||
DECODE_SYNC);
|
DecodeStrategy::SYNC);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
|
|
||||||
rv = FinishedSomeDecoding();
|
rv = FinishedSomeDecoding();
|
||||||
@ -2652,7 +2643,7 @@ RasterImage::UnlockImage()
|
|||||||
("RasterImage[0x%p] canceling decode because image "
|
("RasterImage[0x%p] canceling decode because image "
|
||||||
"is now unlocked.", this));
|
"is now unlocked.", this));
|
||||||
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
|
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
|
||||||
FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
|
||||||
ForceDiscard();
|
ForceDiscard();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -2773,7 +2764,7 @@ RasterImage::DoError()
|
|||||||
|
|
||||||
// If we're mid-decode, shut down the decoder.
|
// If we're mid-decode, shut down the decoder.
|
||||||
if (mDecoder) {
|
if (mDecoder) {
|
||||||
FinishedSomeDecoding(eShutdownIntent_Error);
|
FinishedSomeDecoding(ShutdownReason::FATAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the container in an error state.
|
// Put the container in an error state.
|
||||||
@ -2859,7 +2850,7 @@ RasterImage::GetFramesNotified(uint32_t *aFramesNotified)
|
|||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
|
RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
|
||||||
eShutdownIntent aIntent,
|
ShutdownReason aReason,
|
||||||
bool aDone,
|
bool aDone,
|
||||||
bool aWasSize)
|
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 we were a size decode and a full decode was requested, now's the time.
|
||||||
if (NS_SUCCEEDED(aStatus) &&
|
if (NS_SUCCEEDED(aStatus) &&
|
||||||
aIntent == eShutdownIntent_Done &&
|
aReason == ShutdownReason::DONE &&
|
||||||
aDone &&
|
aDone &&
|
||||||
aWasSize &&
|
aWasSize &&
|
||||||
mWantFullDecode) {
|
mWantFullDecode) {
|
||||||
@ -2885,7 +2876,7 @@ RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */,
|
RasterImage::FinishedSomeDecoding(ShutdownReason aReason /* = ShutdownReason::DONE */,
|
||||||
Progress aProgress /* = NoProgress */)
|
Progress aProgress /* = NoProgress */)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
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,
|
// If the decode finished, or we're specifically being told to shut down,
|
||||||
// tell the image and shut down the decoder.
|
// tell the image and shut down the decoder.
|
||||||
if (image->IsDecodeFinished() || aIntent != eShutdownIntent_Done) {
|
if (image->IsDecodeFinished() || aReason != ShutdownReason::DONE) {
|
||||||
done = true;
|
done = true;
|
||||||
|
|
||||||
// Hold on to a reference to the decoder until we're done with it
|
// 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
|
// We need to shut down the decoder first, in order to ensure all
|
||||||
// decoding routines have been finished.
|
// decoding routines have been finished.
|
||||||
rv = image->ShutdownDecoder(aIntent);
|
rv = image->ShutdownDecoder(aReason);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
image->DoError();
|
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>
|
already_AddRefed<imgIContainer>
|
||||||
@ -3004,447 +2995,6 @@ RasterImage::Unwrap()
|
|||||||
return self.forget();
|
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
|
nsIntSize
|
||||||
RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
||||||
GraphicsFilter aFilter, uint32_t aFlags)
|
GraphicsFilter aFilter, uint32_t aFlags)
|
||||||
|
@ -25,16 +25,14 @@
|
|||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "imgFrame.h"
|
#include "imgFrame.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "DecodeStrategy.h"
|
#include "DecodePool.h"
|
||||||
#include "DiscardTracker.h"
|
#include "DiscardTracker.h"
|
||||||
#include "Orientation.h"
|
#include "Orientation.h"
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/Mutex.h"
|
|
||||||
#include "mozilla/ReentrantMonitor.h"
|
#include "mozilla/ReentrantMonitor.h"
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
#include "mozilla/TypedEnum.h"
|
#include "mozilla/TypedEnum.h"
|
||||||
#include "mozilla/StaticPtr.h"
|
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -135,15 +133,6 @@ namespace image {
|
|||||||
|
|
||||||
class Decoder;
|
class Decoder;
|
||||||
class FrameAnimator;
|
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
|
class RasterImage MOZ_FINAL : public ImageResource
|
||||||
, public nsIProperties
|
, public nsIProperties
|
||||||
@ -260,9 +249,7 @@ public:
|
|||||||
bool aLastPart) MOZ_OVERRIDE;
|
bool aLastPart) MOZ_OVERRIDE;
|
||||||
virtual nsresult OnNewSourceData() MOZ_OVERRIDE;
|
virtual nsresult OnNewSourceData() MOZ_OVERRIDE;
|
||||||
|
|
||||||
static already_AddRefed<nsIEventTarget> GetEventTarget() {
|
static already_AddRefed<nsIEventTarget> GetEventTarget();
|
||||||
return DecodePool::Singleton()->GetEventTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A hint of the number of bytes of source data that the image contains. If
|
* A hint of the number of bytes of source data that the image contains. If
|
||||||
@ -294,8 +281,6 @@ public:
|
|||||||
return mRequestedSampleSize;
|
return mRequestedSampleSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
nsCString GetURIString() {
|
nsCString GetURIString() {
|
||||||
nsCString spec;
|
nsCString spec;
|
||||||
if (GetURI()) {
|
if (GetURI()) {
|
||||||
@ -304,168 +289,15 @@ public:
|
|||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called from module startup. Sets up RasterImage to be used.
|
|
||||||
static void Initialize();
|
static void Initialize();
|
||||||
|
|
||||||
// Decoder shutdown
|
|
||||||
enum eShutdownIntent {
|
|
||||||
eShutdownIntent_Done = 0,
|
|
||||||
eShutdownIntent_NotNeeded = 1,
|
|
||||||
eShutdownIntent_Error = 2,
|
|
||||||
eShutdownIntent_AllCount = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
friend class DecodePool;
|
||||||
* DecodePool is a singleton class we use when decoding large images.
|
friend class DecodeWorker;
|
||||||
*
|
friend class FrameNeededWorker;
|
||||||
* When we wish to decode an image larger than
|
friend class NotifyProgressWorker;
|
||||||
* 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();
|
nsresult FinishedSomeDecoding(ShutdownReason aReason = ShutdownReason::DONE,
|
||||||
|
|
||||||
/**
|
|
||||||
* 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,
|
|
||||||
Progress aProgress = NoProgress);
|
Progress aProgress = NoProgress);
|
||||||
|
|
||||||
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
||||||
@ -624,10 +456,8 @@ private: // data
|
|||||||
bool mPendingError:1;
|
bool mPendingError:1;
|
||||||
|
|
||||||
// Decoding
|
// Decoding
|
||||||
nsresult RequestDecodeIfNeeded(nsresult aStatus,
|
nsresult RequestDecodeIfNeeded(nsresult aStatus, ShutdownReason aReason,
|
||||||
eShutdownIntent aIntent,
|
bool aDone, bool aWasSize);
|
||||||
bool aDone,
|
|
||||||
bool aWasSize);
|
|
||||||
nsresult WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
nsresult WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
||||||
nsresult SyncDecode();
|
nsresult SyncDecode();
|
||||||
nsresult InitDecoder(bool aDoSizeDecode);
|
nsresult InitDecoder(bool aDoSizeDecode);
|
||||||
@ -639,7 +469,7 @@ private: // data
|
|||||||
// Initializes ProgressTracker and resets it on RasterImage destruction.
|
// Initializes ProgressTracker and resets it on RasterImage destruction.
|
||||||
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
|
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
|
||||||
|
|
||||||
nsresult ShutdownDecoder(eShutdownIntent aIntent);
|
nsresult ShutdownDecoder(ShutdownReason aReason);
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -16,6 +16,7 @@ EXPORTS += [
|
|||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
'ClippedImage.cpp',
|
'ClippedImage.cpp',
|
||||||
|
'DecodePool.cpp',
|
||||||
'Decoder.cpp',
|
'Decoder.cpp',
|
||||||
'DiscardTracker.cpp',
|
'DiscardTracker.cpp',
|
||||||
'DynamicImage.cpp',
|
'DynamicImage.cpp',
|
||||||
|
Loading…
Reference in New Issue
Block a user