зеркало из https://github.com/mozilla/gecko-dev.git
Bug 716140 - Implement multithreaded decoding using a thread pool. r=seth
This commit is contained in:
Родитель
120d6e205c
Коммит
87a1590a0d
|
@ -76,7 +76,7 @@ public:
|
|||
void FlushInvalidations();
|
||||
|
||||
// We're not COM-y, so we don't get refcounts by default
|
||||
NS_INLINE_DECL_REFCOUNTING(Decoder)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
|
||||
|
||||
/*
|
||||
* State.
|
||||
|
@ -97,6 +97,11 @@ public:
|
|||
mSynchronous = aSynchronous;
|
||||
}
|
||||
|
||||
bool IsSynchronous() const
|
||||
{
|
||||
return mSynchronous;
|
||||
}
|
||||
|
||||
void SetObserver(imgDecoderObserver* aObserver)
|
||||
{
|
||||
MOZ_ASSERT(aObserver);
|
||||
|
|
|
@ -16,9 +16,14 @@
|
|||
#include "nsAutoPtr.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "prenv.h"
|
||||
#include "prsystem.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "Layers.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsThread.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
||||
#include "nsPNGDecoder.h"
|
||||
#include "nsGIFDecoder2.h"
|
||||
|
@ -30,6 +35,7 @@
|
|||
|
||||
#include "gfxContext.h"
|
||||
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StandardInteger.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
@ -349,13 +355,13 @@ private:
|
|||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
/* static */ StaticRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
|
||||
/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
|
||||
static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
|
||||
|
||||
#ifndef DEBUG
|
||||
NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
|
||||
#else
|
||||
NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
|
||||
imgIContainerDebug)
|
||||
#endif
|
||||
|
||||
|
@ -368,12 +374,14 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
|
|||
mAnim(nullptr),
|
||||
mLoopCount(-1),
|
||||
mLockCount(0),
|
||||
mDecoder(nullptr),
|
||||
mBytesDecoded(0),
|
||||
mDecodeCount(0),
|
||||
#ifdef DEBUG
|
||||
mFramesNotified(0),
|
||||
#endif
|
||||
mDecodingMutex("RasterImage"),
|
||||
mDecoder(nullptr),
|
||||
mBytesDecoded(0),
|
||||
mInDecoder(false),
|
||||
mHasSize(false),
|
||||
mDecodeOnDraw(false),
|
||||
mMultipart(false),
|
||||
|
@ -381,7 +389,6 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
|
|||
mHasSourceData(false),
|
||||
mDecoded(false),
|
||||
mHasBeenDecoded(false),
|
||||
mInDecoder(false),
|
||||
mAnimationFinished(false),
|
||||
mFinishing(false),
|
||||
mInUpdateImageContainer(false),
|
||||
|
@ -418,7 +425,8 @@ RasterImage::~RasterImage()
|
|||
if (mDecoder) {
|
||||
// Kill off our decode request, if it's pending. (If not, this call is
|
||||
// harmless.)
|
||||
DecodeWorker::Singleton()->StopDecoding(this);
|
||||
MutexAutoLock lock(mDecodingMutex);
|
||||
DecodePool::StopDecoding(this);
|
||||
mDecoder = nullptr;
|
||||
|
||||
// Unlock the last frame (if we have any). Our invariant is that, while we
|
||||
|
@ -452,7 +460,7 @@ RasterImage::Initialize()
|
|||
|
||||
// Create our singletons now, so we don't have to worry about what thread
|
||||
// they're created on.
|
||||
DecodeWorker::Singleton();
|
||||
DecodePool::Singleton();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1377,6 +1385,8 @@ RasterImage::ApplyDecodeFlags(uint32_t aNewFlags)
|
|||
nsresult
|
||||
RasterImage::SetSize(int32_t aWidth, int32_t aHeight)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mError)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -1532,6 +1542,8 @@ RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult)
|
|||
nsresult
|
||||
RasterImage::DecodingComplete()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mError)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -1660,6 +1672,8 @@ RasterImage::SetLoopCount(int32_t aLoopCount)
|
|||
nsresult
|
||||
RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
||||
{
|
||||
MutexAutoLock lock(mDecodingMutex);
|
||||
|
||||
if (mError)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -1721,6 +1735,9 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
|||
mInDecoder = true;
|
||||
mDecoder->FlushInvalidations();
|
||||
mInDecoder = false;
|
||||
|
||||
rv = FinishedSomeDecoding();
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
// Otherwise, we're storing data in the source buffer
|
||||
|
@ -1731,10 +1748,8 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
|||
if (!newElem)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// If there's a decoder open, that means we want to do more decoding.
|
||||
// Wake up the worker.
|
||||
if (mDecoder) {
|
||||
DecodeWorker::Singleton()->RequestDecode(this);
|
||||
DecodePool::Singleton()->RequestDecode(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1778,6 +1793,8 @@ get_header_str (char *buf, char *data, size_t data_len)
|
|||
nsresult
|
||||
RasterImage::DoImageDataComplete()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mError)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -1786,26 +1803,27 @@ RasterImage::DoImageDataComplete()
|
|||
return NS_OK;
|
||||
mHasSourceData = true;
|
||||
|
||||
// This call should come straight from necko - no reentrancy allowed
|
||||
NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
|
||||
|
||||
// If there's a decoder open, synchronously decode the beginning of the image
|
||||
// to check for errors and get the image's size. (If we already have the
|
||||
// image's size, this does nothing.) Then kick off an async decode of the
|
||||
// rest of the image.
|
||||
if (mDecoder) {
|
||||
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
|
||||
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mDecodingMutex);
|
||||
|
||||
// If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
|
||||
// finish decoding this image.
|
||||
if (mDecoder) {
|
||||
DecodeWorker::Singleton()->RequestDecode(this);
|
||||
DecodePool::Singleton()->RequestDecode(this);
|
||||
}
|
||||
|
||||
// Free up any extra space in the backing buffer
|
||||
mSourceData.Compact();
|
||||
}
|
||||
|
||||
// Log header information
|
||||
if (PR_LOG_TEST(GetCompressedImageAccountingLog(), PR_LOG_DEBUG)) {
|
||||
|
@ -1845,7 +1863,10 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus, bo
|
|||
}
|
||||
|
||||
// We just recorded OnStopRequest; we need to inform our listeners.
|
||||
{
|
||||
MutexAutoLock lock(mDecodingMutex);
|
||||
FinishedSomeDecoding();
|
||||
}
|
||||
|
||||
return finalStatus;
|
||||
}
|
||||
|
@ -1873,6 +1894,8 @@ RasterImage::OnImageDataAvailable(nsIRequest*,
|
|||
nsresult
|
||||
RasterImage::OnNewSourceData()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (mError)
|
||||
|
@ -2428,6 +2451,8 @@ RasterImage::GetKeys(uint32_t *count, char ***keys)
|
|||
void
|
||||
RasterImage::Discard(bool force)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// We should be ok for discard
|
||||
NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
|
||||
|
||||
|
@ -2653,7 +2678,7 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
|||
|
||||
// Kill off our decode request, if it's pending. (If not, this call is
|
||||
// harmless.)
|
||||
DecodeWorker::Singleton()->StopDecoding(this);
|
||||
DecodePool::StopDecoding(this);
|
||||
|
||||
nsresult decoderStatus = decoder->GetDecoderError();
|
||||
if (NS_FAILED(decoderStatus)) {
|
||||
|
@ -2688,6 +2713,8 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
|||
nsresult
|
||||
RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount)
|
||||
{
|
||||
mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
// We should have a decoder
|
||||
NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
|
||||
|
||||
|
@ -2751,6 +2778,8 @@ RasterImage::StartDecoding()
|
|||
NS_IMETHODIMP
|
||||
RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (mError)
|
||||
|
@ -2781,7 +2810,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||
|
||||
// If we have a size decoder open, make sure we get the size
|
||||
if (mDecoder && mDecoder->IsSizeDecode()) {
|
||||
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
|
||||
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
|
||||
// If we didn't get the size out of the image, we won't until we get more
|
||||
|
@ -2798,6 +2827,8 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||
if (mDecoded)
|
||||
return NS_OK;
|
||||
|
||||
MutexAutoLock lock(mDecodingMutex);
|
||||
|
||||
// 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) {
|
||||
|
@ -2816,6 +2847,13 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||
MOZ_ASSERT(mDecoder);
|
||||
}
|
||||
|
||||
// If we're waiting for decode work to be notified, go ahead and do that.
|
||||
if (mDecodeRequest &&
|
||||
mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
|
||||
nsresult rv = FinishedSomeDecoding();
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
// If we've read all the data we have, we're done
|
||||
if (mHasSourceData && mBytesDecoded == mSourceData.Length())
|
||||
return NS_OK;
|
||||
|
@ -2827,7 +2865,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||
SAMPLE_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString().get());
|
||||
mDecoder->SetSynchronous(true);
|
||||
|
||||
DecodeWorker::Singleton()->DecodeABitOf(this);
|
||||
DecodePool::Singleton()->DecodeABitOf(this);
|
||||
|
||||
// DecodeABitOf can destroy mDecoder.
|
||||
if (mDecoder) {
|
||||
|
@ -2836,10 +2874,12 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mDecoded) {
|
||||
// If we get this far, dispatch the worker. We do this instead of starting
|
||||
// any immediate decoding to guarantee that all our decode notifications are
|
||||
// dispatched asynchronously, and to ensure we stay responsive.
|
||||
DecodeWorker::Singleton()->RequestDecode(this);
|
||||
DecodePool::Singleton()->RequestDecode(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2848,6 +2888,16 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||
nsresult
|
||||
RasterImage::SyncDecode()
|
||||
{
|
||||
MutexAutoLock imgLock(mDecodingMutex);
|
||||
|
||||
if (mDecodeRequest) {
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
|
||||
nsresult rv = FinishedSomeDecoding();
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
SAMPLE_LABEL_PRINTF("RasterImage", "SyncDecode", "%s", GetURIString().get());;
|
||||
|
@ -2860,7 +2910,7 @@ RasterImage::SyncDecode()
|
|||
|
||||
// If we have a size decoder open, make sure we get the size
|
||||
if (mDecoder && mDecoder->IsSizeDecode()) {
|
||||
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
|
||||
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
|
||||
// If we didn't get the size out of the image, we won't until we get more
|
||||
|
@ -2916,6 +2966,9 @@ RasterImage::SyncDecode()
|
|||
|
||||
if (mDecoder) {
|
||||
mDecoder->SetSynchronous(false);
|
||||
|
||||
// If our decoder's still open, there's still work to be done.
|
||||
DecodePool::Singleton()->RequestDecode(this);
|
||||
}
|
||||
|
||||
// All good if no errors!
|
||||
|
@ -3127,10 +3180,6 @@ RasterImage::Draw(gfxContext *aContext,
|
|||
// We use !mDecoded && mHasSourceData to mean discarded.
|
||||
if (!mDecoded && mHasSourceData) {
|
||||
mDrawStartTime = TimeStamp::Now();
|
||||
|
||||
// We're drawing this image, so indicate that we should decode it as soon
|
||||
// as possible.
|
||||
DecodeWorker::Singleton()->MarkAsASAP(this);
|
||||
}
|
||||
|
||||
// If a synchronous draw is requested, flush anything that might be sitting around
|
||||
|
@ -3204,6 +3253,7 @@ RasterImage::UnlockImage()
|
|||
PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
|
||||
("RasterImage[0x%p] canceling decode because image "
|
||||
"is now unlocked.", this));
|
||||
MutexAutoLock lock(mDecodingMutex);
|
||||
FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
||||
ForceDiscard();
|
||||
return NS_OK;
|
||||
|
@ -3238,6 +3288,8 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes)
|
|||
// We should have a decoder if we get here
|
||||
NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
|
||||
|
||||
mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
// First, if we've just been called because we allocated a frame on the main
|
||||
// thread, let the decoder deal with the data it set aside at that time by
|
||||
// passing it a null buffer.
|
||||
|
@ -3311,6 +3363,7 @@ RasterImage::DoError()
|
|||
|
||||
// If we're mid-decode, shut down the decoder.
|
||||
if (mDecoder) {
|
||||
MutexAutoLock lock(mDecodingMutex);
|
||||
FinishedSomeDecoding(eShutdownIntent_Error);
|
||||
}
|
||||
|
||||
|
@ -3383,6 +3436,8 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
nsRefPtr<DecodeRequest> request;
|
||||
if (aRequest) {
|
||||
request = aRequest;
|
||||
|
@ -3444,11 +3499,20 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
|||
}
|
||||
}
|
||||
|
||||
imgStatusTracker::StatusDiff diff;
|
||||
if (request) {
|
||||
diff = image->mStatusTracker->CalculateAndApplyDifference(request->mStatusTracker);
|
||||
}
|
||||
|
||||
{
|
||||
// Notifications can't go out with the decoding lock held.
|
||||
MutexAutoUnlock unlock(mDecodingMutex);
|
||||
|
||||
// Then, tell the observers what happened in the decoder.
|
||||
// If we have no request, we have not yet created a decoder, but we still
|
||||
// need to send out notifications.
|
||||
if (request) {
|
||||
image->mStatusTracker->SyncAndSyncNotifyDifference(request->mStatusTracker);
|
||||
image->mStatusTracker->SyncNotifyDifference(diff);
|
||||
} else {
|
||||
image->mStatusTracker->SyncNotifyDecodeState();
|
||||
}
|
||||
|
@ -3467,112 +3531,102 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */ RasterImage::DecodeWorker*
|
||||
RasterImage::DecodeWorker::Singleton()
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(RasterImage::DecodePool,
|
||||
nsIObserver)
|
||||
|
||||
/* static */ RasterImage::DecodePool*
|
||||
RasterImage::DecodePool::Singleton()
|
||||
{
|
||||
if (!sSingleton) {
|
||||
sSingleton = new DecodeWorker();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sSingleton = new DecodePool();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
RasterImage::DecodeWorker::~DecodeWorker()
|
||||
RasterImage::DecodePool::DecodePool()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodeWorker on main thread!");
|
||||
mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
|
||||
if (mThreadPool) {
|
||||
mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
|
||||
mThreadPool->SetThreadLimit(std::max(PR_GetNumberOfProcessors() - 1, 1));
|
||||
|
||||
// Shut down all the decoders since we're going away.
|
||||
DecodeRequest* request = mASAPDecodeRequests.getFirst();
|
||||
while (request) {
|
||||
request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
||||
|
||||
request = request->getNext();
|
||||
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
|
||||
if (obsSvc) {
|
||||
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request = mNormalDecodeRequests.getFirst();
|
||||
while (request) {
|
||||
request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
||||
|
||||
request = request->getNext();
|
||||
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 PRUnichar *data)
|
||||
{
|
||||
NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
|
||||
|
||||
if (mThreadPool) {
|
||||
mThreadPool->Shutdown();
|
||||
mThreadPool = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
|
||||
{
|
||||
// We can be marked as ASAP before we've been asked to decode. If we are,
|
||||
// create the request so we have somewhere to write down our status.
|
||||
if (!aImg->mDecodeRequest) {
|
||||
aImg->mDecodeRequest = new DecodeRequest(aImg);
|
||||
}
|
||||
|
||||
DecodeRequest* request = aImg->mDecodeRequest;
|
||||
|
||||
// If we're already an ASAP request, there's nothing to do here.
|
||||
if (request->mIsASAP) {
|
||||
return;
|
||||
}
|
||||
|
||||
request->mIsASAP = true;
|
||||
|
||||
if (request->isInList()) {
|
||||
// If the decode request is in a list, it must be in the normal decode
|
||||
// requests list -- if it had been in the ASAP list, then mIsASAP would
|
||||
// have been true above. Move the request to the ASAP list.
|
||||
request->removeFrom(mNormalDecodeRequests);
|
||||
mASAPDecodeRequests.insertBack(request);
|
||||
|
||||
// Since request is in a list, one of the decode worker's lists is
|
||||
// non-empty, so the worker should be pending in the event loop.
|
||||
//
|
||||
// (Note that this invariant only holds while we are not in Run(), because
|
||||
// DecodeSomeOfImage adds requests to the decode worker using
|
||||
// AddDecodeRequest, not RequestDecode, and AddDecodeRequest does not call
|
||||
// EnsurePendingInEventLoop. Therefore, it is an error to call MarkAsASAP
|
||||
// from within DecodeWorker::Run.)
|
||||
MOZ_ASSERT(mPendingInEventLoop);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode)
|
||||
{
|
||||
if (aRequest->isInList()) {
|
||||
// The image is already in our list of images to decode, so we don't have
|
||||
// to do anything here.
|
||||
return;
|
||||
}
|
||||
|
||||
aRequest->mBytesToDecode = bytesToDecode;
|
||||
|
||||
if (aRequest->mIsASAP) {
|
||||
mASAPDecodeRequests.insertBack(aRequest);
|
||||
} else {
|
||||
mNormalDecodeRequests.insertBack(aRequest);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg)
|
||||
RasterImage::DecodePool::RequestDecode(RasterImage* aImg)
|
||||
{
|
||||
MOZ_ASSERT(aImg->mDecoder);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aImg->mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
// If we're currently waiting on a new frame for this image, we can't do any
|
||||
// decoding.
|
||||
if (!aImg->mDecoder->NeedsNewFrame()) {
|
||||
AddDecodeRequest(aImg->mDecodeRequest, aImg->mSourceData.Length() - aImg->mBytesDecoded);
|
||||
EnsurePendingInEventLoop();
|
||||
// No matter whether this is currently being decoded, we need to update the
|
||||
// number of bytes we want it to decode.
|
||||
aImg->mDecodeRequest->mBytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
|
||||
|
||||
if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_PENDING ||
|
||||
aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_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->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_PENDING;
|
||||
nsRefPtr<DecodeJob> job = new DecodeJob(aImg->mDecodeRequest, aImg);
|
||||
if (!mThreadPool) {
|
||||
NS_DispatchToMainThread(job);
|
||||
} else {
|
||||
mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
|
||||
RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aImg->mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (aImg->mDecodeRequest) {
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
|
||||
aImg->FinishedSomeDecoding();
|
||||
}
|
||||
}
|
||||
|
||||
DecodeSomeOfImage(aImg);
|
||||
|
||||
|
@ -3594,98 +3648,98 @@ RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::EnsurePendingInEventLoop()
|
||||
/* static */ void
|
||||
RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
|
||||
{
|
||||
if (!mPendingInEventLoop) {
|
||||
mPendingInEventLoop = true;
|
||||
NS_DispatchToCurrentThread(this);
|
||||
}
|
||||
}
|
||||
aImg->mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
|
||||
{
|
||||
// If we haven't got a decode request, we're not currently decoding. (Having
|
||||
// a decode request doesn't imply we *are* decoding, though.)
|
||||
if (aImg->mDecodeRequest) {
|
||||
if (aImg->mDecodeRequest->isInList()) {
|
||||
aImg->mDecodeRequest->remove();
|
||||
}
|
||||
aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::DecodeWorker::Run()
|
||||
RasterImage::DecodePool::DecodeJob::Run()
|
||||
{
|
||||
// We just got called back by the event loop; therefore, we're no longer
|
||||
// pending.
|
||||
mPendingInEventLoop = false;
|
||||
MutexAutoLock imglock(mImage->mDecodingMutex);
|
||||
|
||||
TimeStamp eventStart = TimeStamp::Now();
|
||||
// If we were interrupted, we shouldn't do any work.
|
||||
if (mRequest->mRequestStatus == DecodeRequest::REQUEST_STOPPED) {
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Now decode until we either run out of time or run out of images.
|
||||
do {
|
||||
// Try to get an ASAP request to handle. If there isn't one, try to get a
|
||||
// normal request. If no normal request is pending either, then we're done
|
||||
// here.
|
||||
DecodeRequest* request = mASAPDecodeRequests.popFirst();
|
||||
if (!request)
|
||||
request = mNormalDecodeRequests.popFirst();
|
||||
if (!request)
|
||||
break;
|
||||
// If someone came along and synchronously decoded us, there's nothing for us to do.
|
||||
if (!mRequest->mAllocatedNewFrame && (!mImage->mDecoder || mImage->IsDecodeFinished())) {
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This has to be a strong pointer, because DecodeSomeOfImage may destroy
|
||||
// image->mDecoder, which may be holding the only other reference to image.
|
||||
nsRefPtr<RasterImage> image = request->mImage;
|
||||
uint32_t oldFrameCount = image->mDecoder->GetFrameCount();
|
||||
uint32_t oldByteCount = image->mBytesDecoded;
|
||||
// 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;
|
||||
}
|
||||
|
||||
DecodeSomeOfImage(image, DECODE_TYPE_NORMAL, request->mBytesToDecode);
|
||||
mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE;
|
||||
|
||||
uint32_t bytesDecoded = image->mBytesDecoded - oldByteCount;
|
||||
uint32_t oldByteCount = mImage->mBytesDecoded;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DecodePool::Singleton()->DecodeSomeOfImage(mImage, type, mRequest->mBytesToDecode);
|
||||
|
||||
uint32_t bytesDecoded = mImage->mBytesDecoded - oldByteCount;
|
||||
|
||||
mRequest->mRequestStatus = DecodeRequest::REQUEST_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 (image->mDecoder && image->mDecoder->NeedsNewFrame()) {
|
||||
FrameNeededWorker::GetNewFrame(image);
|
||||
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 (image->mDecoder &&
|
||||
!image->mError &&
|
||||
!image->IsDecodeFinished() &&
|
||||
bytesDecoded < request->mBytesToDecode) {
|
||||
AddDecodeRequest(request, request->mBytesToDecode - bytesDecoded);
|
||||
|
||||
// If we have a new frame, let everybody know about it.
|
||||
if (image->mDecoder->GetFrameCount() != oldFrameCount) {
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
|
||||
}
|
||||
else if (mImage->mDecoder &&
|
||||
!mImage->mError &&
|
||||
!mImage->IsDecodeFinished() &&
|
||||
bytesDecoded < mRequest->mBytesToDecode) {
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
} else {
|
||||
// Nothing more for us to do - let everyone know what happened.
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
|
||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
|
||||
}
|
||||
|
||||
} while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
|
||||
|
||||
// If decode requests are pending, re-post ourself to the event loop.
|
||||
if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
|
||||
EnsurePendingInEventLoop();
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY_US,
|
||||
uint32_t((TimeStamp::Now() - eventStart).ToMicroseconds()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
|
||||
RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MutexAutoLock imgLock(aImg->mDecodingMutex);
|
||||
|
||||
if (aImg->mDecodeRequest) {
|
||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
||||
if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
|
||||
nsresult rv = aImg->FinishedSomeDecoding();
|
||||
if (NS_FAILED(rv)) {
|
||||
aImg->DoError();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
|
@ -3703,12 +3757,13 @@ RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
|
|||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
|
||||
DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */,
|
||||
RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg,
|
||||
DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */,
|
||||
uint32_t bytesToDecode /* = 0 */)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aImg->mInitialized,
|
||||
"Worker active for uninitialized container!");
|
||||
aImg->mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
// If an error is flagged, it probably happened while we were waiting
|
||||
// in the event queue.
|
||||
|
@ -3720,9 +3775,17 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
|
|||
if (!aImg->mDecoder || aImg->mDecoded)
|
||||
return NS_OK;
|
||||
|
||||
// If we're currently waiting on a new frame for this image, we can't do any
|
||||
// decoding right now.
|
||||
if (aImg->mDecoder->NeedsNewFrame()) {
|
||||
// If we're doing synchronous decodes, and we're waiting on a new frame for
|
||||
// this image, get it now.
|
||||
if (aImg->mDecoder->IsSynchronous() && aImg->mDecoder->NeedsNewFrame()) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
aImg->mDecoder->AllocateFrame();
|
||||
aImg->mDecodeRequest->mAllocatedNewFrame = true;
|
||||
}
|
||||
|
||||
// If we're not synchronous, we can't allocate a frame right now.
|
||||
else if (aImg->mDecoder->NeedsNewFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3773,7 +3836,7 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
|
|||
|
||||
// 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 (TimeStamp::Now() >= deadline)
|
||||
if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3818,6 +3881,8 @@ RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeReques
|
|||
void
|
||||
RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request)
|
||||
{
|
||||
image->mDecodingMutex.AssertCurrentThreadOwns();
|
||||
|
||||
nsCOMPtr<DecodeDoneWorker> worker = new DecodeDoneWorker(image, request);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
@ -3825,12 +3890,14 @@ RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, De
|
|||
NS_IMETHODIMP
|
||||
RasterImage::DecodeDoneWorker::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MutexAutoLock lock(mImage->mDecodingMutex);
|
||||
|
||||
mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest);
|
||||
|
||||
// If we didn't finish decoding yet, try again
|
||||
if (mImage->mDecoder && !mImage->IsDecodeFinished() &&
|
||||
mImage->mSourceData.Length() > mImage->mBytesDecoded) {
|
||||
DecodeWorker::Singleton()->RequestDecode(mImage);
|
||||
if (mImage->mDecoder) {
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -3851,6 +3918,7 @@ RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image)
|
|||
NS_IMETHODIMP
|
||||
RasterImage::FrameNeededWorker::Run()
|
||||
{
|
||||
MutexAutoLock lock(mImage->mDecodingMutex);
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// If we got a synchronous decode in the mean time, we don't need to do
|
||||
|
@ -3861,7 +3929,8 @@ RasterImage::FrameNeededWorker::Run()
|
|||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
|
||||
DecodeWorker::Singleton()->RequestDecode(mImage);
|
||||
// By definition, we're not done decoding, so enqueue us for more decoding.
|
||||
DecodePool::Singleton()->RequestDecode(mImage);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#define mozilla_imagelib_RasterImage_h_
|
||||
|
||||
#include "Image.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsIProperties.h"
|
||||
|
@ -28,18 +27,20 @@
|
|||
#include "imgFrame.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "DiscardTracker.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#ifdef DEBUG
|
||||
#include "imgIContainerDebug.h"
|
||||
#endif
|
||||
|
||||
class nsIInputStream;
|
||||
class nsIThreadPool;
|
||||
|
||||
#define NS_RASTERIMAGE_CID \
|
||||
{ /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */ \
|
||||
|
@ -387,25 +388,23 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* DecodeWorker keeps a linked list of DecodeRequests to keep track of the
|
||||
* images it needs to decode.
|
||||
*
|
||||
* Each RasterImage has a pointer to one or zero heap-allocated
|
||||
* DecodeRequests.
|
||||
*/
|
||||
struct DecodeRequest : public LinkedListElement<DecodeRequest>,
|
||||
public RefCounted<DecodeRequest>
|
||||
struct DecodeRequest
|
||||
{
|
||||
DecodeRequest(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
, mBytesToDecode(0)
|
||||
, mRequestStatus(REQUEST_INACTIVE)
|
||||
, mChunkCount(0)
|
||||
, mAllocatedNewFrame(false)
|
||||
, mIsASAP(false)
|
||||
{
|
||||
mStatusTracker = aImage->mStatusTracker->CloneForRecording();
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodeRequest)
|
||||
|
||||
// The status tracker that is associated with a given decode request, to
|
||||
// ensure their lifetimes are linked.
|
||||
nsRefPtr<imgStatusTracker> mStatusTracker;
|
||||
|
@ -414,6 +413,15 @@ private:
|
|||
|
||||
uint32_t mBytesToDecode;
|
||||
|
||||
enum DecodeRequestStatus
|
||||
{
|
||||
REQUEST_INACTIVE,
|
||||
REQUEST_PENDING,
|
||||
REQUEST_ACTIVE,
|
||||
REQUEST_WORK_DONE,
|
||||
REQUEST_STOPPED
|
||||
} mRequestStatus;
|
||||
|
||||
/* Keeps track of how much time we've burned decoding this particular decode
|
||||
* request. */
|
||||
TimeDuration mDecodeTime;
|
||||
|
@ -424,35 +432,31 @@ private:
|
|||
/* True if a new frame has been allocated, but DecodeSomeData hasn't yet
|
||||
* been called to flush data to it */
|
||||
bool mAllocatedNewFrame;
|
||||
|
||||
/* True if we need to handle this decode as soon as possible. */
|
||||
bool mIsASAP;
|
||||
};
|
||||
|
||||
/*
|
||||
* DecodeWorker is a singleton class we use when decoding large images.
|
||||
* 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 DecodeWorker::RequestDecode()
|
||||
* 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 DecodeWorker singleton to the event queue, if it's not already pending
|
||||
* the DecodePool singleton to the event queue, if it's not already pending
|
||||
* there.
|
||||
*
|
||||
* When the DecodeWorker is run from the event queue, it decodes the image (and
|
||||
* 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.
|
||||
*
|
||||
* An image being decoded may have one of two priorities: normal or ASAP. ASAP
|
||||
* images are always decoded before normal images. (We currently give ASAP
|
||||
* priority to images which appear onscreen but are not yet decoded.)
|
||||
*/
|
||||
class DecodeWorker : public nsRunnable
|
||||
class DecodePool : public nsIObserver
|
||||
{
|
||||
public:
|
||||
static DecodeWorker* Singleton();
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static DecodePool* Singleton();
|
||||
|
||||
/**
|
||||
* Ask the DecodeWorker to asynchronously decode this image.
|
||||
* Ask the DecodePool to asynchronously decode this image.
|
||||
*/
|
||||
void RequestDecode(RasterImage* aImg);
|
||||
|
||||
|
@ -463,23 +467,13 @@ private:
|
|||
void DecodeABitOf(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Give this image ASAP priority; it will be decoded before all non-ASAP
|
||||
* images. You can call MarkAsASAP before or after you call RequestDecode
|
||||
* for the image, but if you MarkAsASAP before you call RequestDecode, you
|
||||
* still need to call RequestDecode.
|
||||
*
|
||||
* StopDecoding() resets the image's ASAP flag.
|
||||
*/
|
||||
void MarkAsASAP(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Ask the DecodeWorker to stop decoding this image. Internally, we also
|
||||
* Ask the DecodePool to stop decoding this image. Internally, we also
|
||||
* call this function when we finish decoding an image.
|
||||
*
|
||||
* Since the DecodeWorker keeps raw pointers to RasterImages, make sure you
|
||||
* Since the DecodePool keeps raw pointers to RasterImages, make sure you
|
||||
* call this before a RasterImage is destroyed!
|
||||
*/
|
||||
void StopDecoding(RasterImage* aImg);
|
||||
static void StopDecoding(RasterImage* aImg);
|
||||
|
||||
/**
|
||||
* Synchronously decode the beginning of the image until we run out of
|
||||
|
@ -491,56 +485,56 @@ private:
|
|||
*/
|
||||
nsresult DecodeUntilSizeAvailable(RasterImage* aImg);
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
virtual ~DecodeWorker();
|
||||
virtual ~DecodePool();
|
||||
|
||||
private: /* statics */
|
||||
static StaticRefPtr<DecodeWorker> sSingleton;
|
||||
static StaticRefPtr<DecodePool> sSingleton;
|
||||
|
||||
private: /* methods */
|
||||
DecodeWorker()
|
||||
: mPendingInEventLoop(false)
|
||||
{}
|
||||
|
||||
/* Post ourselves to the event loop if we're not currently pending. */
|
||||
void EnsurePendingInEventLoop();
|
||||
|
||||
/* Add the given request to the appropriate list of decode requests, but
|
||||
* don't ensure that we're pending in the event loop. */
|
||||
void AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode);
|
||||
DecodePool();
|
||||
|
||||
enum DecodeType {
|
||||
DECODE_TYPE_NORMAL,
|
||||
DECODE_TYPE_UNTIL_SIZE
|
||||
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. */
|
||||
* 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,
|
||||
DecodeType aDecodeType = DECODE_TYPE_NORMAL,
|
||||
DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME,
|
||||
uint32_t bytesToDecode = 0);
|
||||
|
||||
/* Create a new DecodeRequest suitable for doing some decoding and set it
|
||||
* as aImg's mDecodeRequest. */
|
||||
void CreateRequestForImage(RasterImage* aImg);
|
||||
/* A decode job dispatched to a thread pool by DecodePool.
|
||||
*/
|
||||
class DecodeJob : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DecodeJob(DecodeRequest* aRequest, RasterImage* aImg)
|
||||
: mRequest(aRequest)
|
||||
, mImage(aImg)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
private:
|
||||
nsRefPtr<DecodeRequest> mRequest;
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
};
|
||||
|
||||
private: /* members */
|
||||
|
||||
LinkedList<DecodeRequest> mASAPDecodeRequests;
|
||||
LinkedList<DecodeRequest> mNormalDecodeRequests;
|
||||
|
||||
/* True if we've posted ourselves to the event loop and expect Run() to
|
||||
* be called sometime in the future. */
|
||||
bool mPendingInEventLoop;
|
||||
nsCOMPtr<nsIThreadPool> mThreadPool;
|
||||
};
|
||||
|
||||
class DecodeDoneWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Called by the DecodeWorker with an image when it's done some significant
|
||||
* 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
|
||||
|
@ -563,7 +557,7 @@ private:
|
|||
{
|
||||
public:
|
||||
/**
|
||||
* Called by the DecodeWorker with an image when it's been told by the
|
||||
* 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
|
||||
|
@ -754,16 +748,10 @@ private: // data
|
|||
DiscardTracker::Node mDiscardTrackerNode;
|
||||
|
||||
// Source data members
|
||||
FallibleTArray<char> mSourceData;
|
||||
nsCString mSourceDataMimeType;
|
||||
|
||||
friend class DiscardTracker;
|
||||
|
||||
// Decoder and friends
|
||||
nsRefPtr<Decoder> mDecoder;
|
||||
nsRefPtr<DecodeRequest> mDecodeRequest;
|
||||
uint32_t mBytesDecoded;
|
||||
|
||||
// How many times we've decoded this image.
|
||||
// This is currently only used for statistics
|
||||
int32_t mDecodeCount;
|
||||
|
@ -778,6 +766,22 @@ private: // data
|
|||
uint32_t mFramesNotified;
|
||||
#endif
|
||||
|
||||
// Below are the pieces of data that can be accessed on more than one thread
|
||||
// at once, and hence need to be locked by mDecodingMutex.
|
||||
|
||||
// BEGIN LOCKED MEMBER VARIABLES
|
||||
mozilla::Mutex mDecodingMutex;
|
||||
|
||||
FallibleTArray<char> mSourceData;
|
||||
|
||||
// Decoder and friends
|
||||
nsRefPtr<Decoder> mDecoder;
|
||||
nsRefPtr<DecodeRequest> mDecodeRequest;
|
||||
uint32_t mBytesDecoded;
|
||||
// END LOCKED MEMBER VARIABLES
|
||||
|
||||
bool mInDecoder;
|
||||
|
||||
// Boolean flags (clustered together to conserve space):
|
||||
bool mHasSize:1; // Has SetSize() been called?
|
||||
bool mDecodeOnDraw:1; // Decoding on draw?
|
||||
|
@ -789,7 +793,6 @@ private: // data
|
|||
bool mDecoded:1;
|
||||
bool mHasBeenDecoded:1;
|
||||
|
||||
bool mInDecoder:1;
|
||||
|
||||
// Whether the animation can stop, due to running out
|
||||
// of frames, or no more owning request
|
||||
|
|
|
@ -388,7 +388,8 @@ VectorImage::OnImageDataComplete(nsIRequest* aRequest,
|
|||
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
|
||||
imgDecoderObserver* observer = clone->GetDecoderObserver();
|
||||
observer->OnStopRequest(aLastPart, finalStatus);
|
||||
mStatusTracker->SyncAndSyncNotifyDifference(clone);
|
||||
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
|
||||
mStatusTracker->SyncNotifyDifference(diff);
|
||||
}
|
||||
return finalStatus;
|
||||
}
|
||||
|
@ -846,7 +847,8 @@ VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
|
|||
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
|
||||
imgDecoderObserver* observer = clone->GetDecoderObserver();
|
||||
observer->OnStartDecode();
|
||||
mStatusTracker->SyncAndSyncNotifyDifference(clone);
|
||||
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
|
||||
mStatusTracker->SyncNotifyDifference(diff);
|
||||
}
|
||||
|
||||
// Create a listener to wait until the SVG document is fully loaded, which
|
||||
|
@ -933,7 +935,8 @@ VectorImage::OnSVGDocumentLoaded()
|
|||
observer->OnStopFrame();
|
||||
observer->OnStopDecode(NS_OK); // Unblock page load.
|
||||
|
||||
mStatusTracker->SyncAndSyncNotifyDifference(clone);
|
||||
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
|
||||
mStatusTracker->SyncNotifyDifference(diff);
|
||||
}
|
||||
|
||||
EvaluateAnimation();
|
||||
|
@ -955,7 +958,8 @@ VectorImage::OnSVGDocumentError()
|
|||
|
||||
// Unblock page load.
|
||||
observer->OnStopDecode(NS_ERROR_FAILURE);
|
||||
mStatusTracker->SyncAndSyncNotifyDifference(clone);
|
||||
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
|
||||
mStatusTracker->SyncNotifyDifference(diff);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -517,24 +517,26 @@ imgStatusTracker::SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
|
||||
imgStatusTracker::StatusDiff
|
||||
imgStatusTracker::CalculateAndApplyDifference(imgStatusTracker* other)
|
||||
{
|
||||
LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncAndSyncNotifyDifference");
|
||||
LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncAndCalculateDifference");
|
||||
|
||||
// We must not modify or notify for the start-load state, which happens from Necko callbacks.
|
||||
uint32_t loadState = mState & stateRequestStarted;
|
||||
uint32_t diffState = ~mState & other->mState & ~stateRequestStarted;
|
||||
bool unblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload);
|
||||
bool foundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR);
|
||||
|
||||
StatusDiff diff;
|
||||
diff.mDiffState = ~mState & other->mState & ~stateRequestStarted;
|
||||
diff.mUnblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload);
|
||||
diff.mFoundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR);
|
||||
|
||||
// Now that we've calculated the difference in state, synchronize our state
|
||||
// with the other tracker.
|
||||
|
||||
// First, actually synchronize our state.
|
||||
mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
|
||||
mState |= diffState | loadState;
|
||||
if (unblockedOnload) {
|
||||
diff.mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
|
||||
mState |= diff.mDiffState | loadState;
|
||||
if (diff.mUnblockedOnload) {
|
||||
mState &= ~stateBlockingOnload;
|
||||
}
|
||||
mImageStatus = other->mImageStatus;
|
||||
|
@ -552,9 +554,21 @@ imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
|
|||
}
|
||||
}
|
||||
|
||||
SyncNotifyState(mConsumers, !!mImage, diffState, mInvalidRect, mHadLastPart);
|
||||
// Reset the invalid rectangles for another go.
|
||||
other->mInvalidRect.SetEmpty();
|
||||
mInvalidRect.SetEmpty();
|
||||
|
||||
if (unblockedOnload) {
|
||||
return diff;
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::SyncNotifyDifference(imgStatusTracker::StatusDiff diff)
|
||||
{
|
||||
LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference");
|
||||
|
||||
SyncNotifyState(mConsumers, !!mImage, diff.mDiffState, diff.mInvalidRect, mHadLastPart);
|
||||
|
||||
if (diff.mUnblockedOnload) {
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
|
||||
while (iter.HasMore()) {
|
||||
// Hold on to a reference to this proxy, since notifying the state can
|
||||
|
@ -567,11 +581,7 @@ imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
|
|||
}
|
||||
}
|
||||
|
||||
// Reset the invalid rectangles for another go.
|
||||
other->mInvalidRect.SetEmpty();
|
||||
mInvalidRect.SetEmpty();
|
||||
|
||||
if (foundError) {
|
||||
if (diff.mFoundError) {
|
||||
FireFailureNotification();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ class Image;
|
|||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nscore.h"
|
||||
#include "imgDecoderObserver.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
enum {
|
||||
stateRequestStarted = 1u << 0,
|
||||
|
@ -51,9 +51,11 @@ enum {
|
|||
* and the notifications will be replayed to the proxy asynchronously.
|
||||
*/
|
||||
|
||||
class imgStatusTracker : public mozilla::RefCounted<imgStatusTracker>
|
||||
class imgStatusTracker
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker)
|
||||
|
||||
// aImage is the image that this status tracker will pass to the
|
||||
// imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be
|
||||
// alive as long as this instance is, because we hold a weak reference to it.
|
||||
|
@ -193,7 +195,22 @@ public:
|
|||
inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
|
||||
|
||||
imgStatusTracker* CloneForRecording();
|
||||
void SyncAndSyncNotifyDifference(imgStatusTracker* other);
|
||||
|
||||
struct StatusDiff
|
||||
{
|
||||
uint32_t mDiffState;
|
||||
bool mUnblockedOnload;
|
||||
bool mFoundError;
|
||||
nsIntRect mInvalidRect;
|
||||
};
|
||||
|
||||
// Calculate the difference between this and other, apply that difference to
|
||||
// ourselves, and return it for passing to SyncNotifyDifference.
|
||||
StatusDiff CalculateAndApplyDifference(imgStatusTracker* other);
|
||||
|
||||
// Notify for the difference found in CalculateAndApplyDifference. No
|
||||
// decoding locks may be held.
|
||||
void SyncNotifyDifference(StatusDiff diff);
|
||||
|
||||
nsIntRect GetInvalidRect() const { return mInvalidRect; }
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче