mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 716140 - Implement multithreaded decoding using a thread pool. r=seth
This commit is contained in:
parent
969dfd9705
commit
bf3283de15
@ -76,7 +76,7 @@ public:
|
|||||||
void FlushInvalidations();
|
void FlushInvalidations();
|
||||||
|
|
||||||
// We're not COM-y, so we don't get refcounts by default
|
// 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.
|
* State.
|
||||||
@ -97,6 +97,11 @@ public:
|
|||||||
mSynchronous = aSynchronous;
|
mSynchronous = aSynchronous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSynchronous() const
|
||||||
|
{
|
||||||
|
return mSynchronous;
|
||||||
|
}
|
||||||
|
|
||||||
void SetObserver(imgDecoderObserver* aObserver)
|
void SetObserver(imgDecoderObserver* aObserver)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aObserver);
|
MOZ_ASSERT(aObserver);
|
||||||
|
@ -16,9 +16,14 @@
|
|||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "nsStringStream.h"
|
#include "nsStringStream.h"
|
||||||
#include "prenv.h"
|
#include "prenv.h"
|
||||||
|
#include "prsystem.h"
|
||||||
#include "ImageContainer.h"
|
#include "ImageContainer.h"
|
||||||
#include "Layers.h"
|
#include "Layers.h"
|
||||||
#include "nsPresContext.h"
|
#include "nsPresContext.h"
|
||||||
|
#include "nsThread.h"
|
||||||
|
#include "nsIThreadPool.h"
|
||||||
|
#include "nsXPCOMCIDInternal.h"
|
||||||
|
#include "nsIObserverService.h"
|
||||||
|
|
||||||
#include "nsPNGDecoder.h"
|
#include "nsPNGDecoder.h"
|
||||||
#include "nsGIFDecoder2.h"
|
#include "nsGIFDecoder2.h"
|
||||||
@ -30,6 +35,7 @@
|
|||||||
|
|
||||||
#include "gfxContext.h"
|
#include "gfxContext.h"
|
||||||
|
|
||||||
|
#include "mozilla/Services.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/StandardInteger.h"
|
#include "mozilla/StandardInteger.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
@ -349,14 +355,14 @@ private:
|
|||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace image {
|
namespace image {
|
||||||
|
|
||||||
/* static */ StaticRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
|
/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
|
||||||
static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
|
static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
|
||||||
|
|
||||||
#ifndef DEBUG
|
#ifndef DEBUG
|
||||||
NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
|
NS_IMPL_THREADSAFE_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
|
||||||
#else
|
#else
|
||||||
NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
|
NS_IMPL_THREADSAFE_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
|
||||||
imgIContainerDebug)
|
imgIContainerDebug)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
@ -368,12 +374,14 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
|
|||||||
mAnim(nullptr),
|
mAnim(nullptr),
|
||||||
mLoopCount(-1),
|
mLoopCount(-1),
|
||||||
mLockCount(0),
|
mLockCount(0),
|
||||||
mDecoder(nullptr),
|
|
||||||
mBytesDecoded(0),
|
|
||||||
mDecodeCount(0),
|
mDecodeCount(0),
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
mFramesNotified(0),
|
mFramesNotified(0),
|
||||||
#endif
|
#endif
|
||||||
|
mDecodingMutex("RasterImage"),
|
||||||
|
mDecoder(nullptr),
|
||||||
|
mBytesDecoded(0),
|
||||||
|
mInDecoder(false),
|
||||||
mHasSize(false),
|
mHasSize(false),
|
||||||
mDecodeOnDraw(false),
|
mDecodeOnDraw(false),
|
||||||
mMultipart(false),
|
mMultipart(false),
|
||||||
@ -381,7 +389,6 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
|
|||||||
mHasSourceData(false),
|
mHasSourceData(false),
|
||||||
mDecoded(false),
|
mDecoded(false),
|
||||||
mHasBeenDecoded(false),
|
mHasBeenDecoded(false),
|
||||||
mInDecoder(false),
|
|
||||||
mAnimationFinished(false),
|
mAnimationFinished(false),
|
||||||
mFinishing(false),
|
mFinishing(false),
|
||||||
mInUpdateImageContainer(false),
|
mInUpdateImageContainer(false),
|
||||||
@ -418,7 +425,8 @@ RasterImage::~RasterImage()
|
|||||||
if (mDecoder) {
|
if (mDecoder) {
|
||||||
// Kill off our decode request, if it's pending. (If not, this call is
|
// Kill off our decode request, if it's pending. (If not, this call is
|
||||||
// harmless.)
|
// harmless.)
|
||||||
DecodeWorker::Singleton()->StopDecoding(this);
|
MutexAutoLock lock(mDecodingMutex);
|
||||||
|
DecodePool::StopDecoding(this);
|
||||||
mDecoder = nullptr;
|
mDecoder = nullptr;
|
||||||
|
|
||||||
// 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
|
||||||
@ -452,7 +460,7 @@ RasterImage::Initialize()
|
|||||||
|
|
||||||
// Create our singletons now, so we don't have to worry about what thread
|
// Create our singletons now, so we don't have to worry about what thread
|
||||||
// they're created on.
|
// they're created on.
|
||||||
DecodeWorker::Singleton();
|
DecodePool::Singleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -779,7 +787,7 @@ RasterImage::GetHeight(int32_t *aHeight)
|
|||||||
*aHeight = mSize.height;
|
*aHeight = mSize.height;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//******************************************************************************
|
//******************************************************************************
|
||||||
/* [noscript] readonly attribute nsSize intrinsicSize; */
|
/* [noscript] readonly attribute nsSize intrinsicSize; */
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
@ -1377,6 +1385,8 @@ RasterImage::ApplyDecodeFlags(uint32_t aNewFlags)
|
|||||||
nsresult
|
nsresult
|
||||||
RasterImage::SetSize(int32_t aWidth, int32_t aHeight)
|
RasterImage::SetSize(int32_t aWidth, int32_t aHeight)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (mError)
|
if (mError)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
@ -1532,6 +1542,8 @@ RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult)
|
|||||||
nsresult
|
nsresult
|
||||||
RasterImage::DecodingComplete()
|
RasterImage::DecodingComplete()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (mError)
|
if (mError)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
@ -1660,6 +1672,8 @@ RasterImage::SetLoopCount(int32_t aLoopCount)
|
|||||||
nsresult
|
nsresult
|
||||||
RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(mDecodingMutex);
|
||||||
|
|
||||||
if (mError)
|
if (mError)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
@ -1721,6 +1735,9 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
|||||||
mInDecoder = true;
|
mInDecoder = true;
|
||||||
mDecoder->FlushInvalidations();
|
mDecoder->FlushInvalidations();
|
||||||
mInDecoder = false;
|
mInDecoder = false;
|
||||||
|
|
||||||
|
rv = FinishedSomeDecoding();
|
||||||
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we're storing data in the source buffer
|
// Otherwise, we're storing data in the source buffer
|
||||||
@ -1731,10 +1748,8 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
|
|||||||
if (!newElem)
|
if (!newElem)
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
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) {
|
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
|
nsresult
|
||||||
RasterImage::DoImageDataComplete()
|
RasterImage::DoImageDataComplete()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (mError)
|
if (mError)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
@ -1786,26 +1803,27 @@ RasterImage::DoImageDataComplete()
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
mHasSourceData = true;
|
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
|
// 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
|
// 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
|
// image's size, this does nothing.) Then kick off an async decode of the
|
||||||
// rest of the image.
|
// rest of the image.
|
||||||
if (mDecoder) {
|
if (mDecoder) {
|
||||||
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
|
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
|
{
|
||||||
// finish decoding this image.
|
MutexAutoLock lock(mDecodingMutex);
|
||||||
if (mDecoder) {
|
|
||||||
DecodeWorker::Singleton()->RequestDecode(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free up any extra space in the backing buffer
|
// If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
|
||||||
mSourceData.Compact();
|
// finish decoding this image.
|
||||||
|
if (mDecoder) {
|
||||||
|
DecodePool::Singleton()->RequestDecode(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free up any extra space in the backing buffer
|
||||||
|
mSourceData.Compact();
|
||||||
|
}
|
||||||
|
|
||||||
// Log header information
|
// Log header information
|
||||||
if (PR_LOG_TEST(GetCompressedImageAccountingLog(), PR_LOG_DEBUG)) {
|
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.
|
// We just recorded OnStopRequest; we need to inform our listeners.
|
||||||
FinishedSomeDecoding();
|
{
|
||||||
|
MutexAutoLock lock(mDecodingMutex);
|
||||||
|
FinishedSomeDecoding();
|
||||||
|
}
|
||||||
|
|
||||||
return finalStatus;
|
return finalStatus;
|
||||||
}
|
}
|
||||||
@ -1873,6 +1894,8 @@ RasterImage::OnImageDataAvailable(nsIRequest*,
|
|||||||
nsresult
|
nsresult
|
||||||
RasterImage::OnNewSourceData()
|
RasterImage::OnNewSourceData()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
if (mError)
|
if (mError)
|
||||||
@ -2428,6 +2451,8 @@ RasterImage::GetKeys(uint32_t *count, char ***keys)
|
|||||||
void
|
void
|
||||||
RasterImage::Discard(bool force)
|
RasterImage::Discard(bool force)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
// We should be ok for discard
|
// We should be ok for discard
|
||||||
NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
|
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
|
// Kill off our decode request, if it's pending. (If not, this call is
|
||||||
// harmless.)
|
// harmless.)
|
||||||
DecodeWorker::Singleton()->StopDecoding(this);
|
DecodePool::StopDecoding(this);
|
||||||
|
|
||||||
nsresult decoderStatus = decoder->GetDecoderError();
|
nsresult decoderStatus = decoder->GetDecoderError();
|
||||||
if (NS_FAILED(decoderStatus)) {
|
if (NS_FAILED(decoderStatus)) {
|
||||||
@ -2688,6 +2713,8 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
|||||||
nsresult
|
nsresult
|
||||||
RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount)
|
RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount)
|
||||||
{
|
{
|
||||||
|
mDecodingMutex.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
// We should have a decoder
|
// We should have a decoder
|
||||||
NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
|
NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
|
||||||
|
|
||||||
@ -2751,6 +2778,8 @@ RasterImage::StartDecoding()
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
if (mError)
|
if (mError)
|
||||||
@ -2781,7 +2810,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||||||
|
|
||||||
// If we have a size decoder open, make sure we get the size
|
// If we have a size decoder open, make sure we get the size
|
||||||
if (mDecoder && mDecoder->IsSizeDecode()) {
|
if (mDecoder && mDecoder->IsSizeDecode()) {
|
||||||
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
|
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
|
|
||||||
// If we didn't get the size out of the image, we won't until we get more
|
// 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)
|
if (mDecoded)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
|
MutexAutoLock lock(mDecodingMutex);
|
||||||
|
|
||||||
// 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) {
|
||||||
@ -2805,7 +2836,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't have a decoder, create one
|
// If we don't have a decoder, create one
|
||||||
if (!mDecoder) {
|
if (!mDecoder) {
|
||||||
rv = InitDecoder(/* aDoSizeDecode = */ false);
|
rv = InitDecoder(/* aDoSizeDecode = */ false);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
@ -2816,6 +2847,13 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||||||
MOZ_ASSERT(mDecoder);
|
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 we've read all the data we have, we're done
|
||||||
if (mHasSourceData && mBytesDecoded == mSourceData.Length())
|
if (mHasSourceData && mBytesDecoded == mSourceData.Length())
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -2827,7 +2865,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||||||
SAMPLE_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString().get());
|
SAMPLE_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString().get());
|
||||||
mDecoder->SetSynchronous(true);
|
mDecoder->SetSynchronous(true);
|
||||||
|
|
||||||
DecodeWorker::Singleton()->DecodeABitOf(this);
|
DecodePool::Singleton()->DecodeABitOf(this);
|
||||||
|
|
||||||
// DecodeABitOf can destroy mDecoder.
|
// DecodeABitOf can destroy mDecoder.
|
||||||
if (mDecoder) {
|
if (mDecoder) {
|
||||||
@ -2836,10 +2874,12 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get this far, dispatch the worker. We do this instead of starting
|
if (!mDecoded) {
|
||||||
// any immediate decoding to guarantee that all our decode notifications are
|
// If we get this far, dispatch the worker. We do this instead of starting
|
||||||
// dispatched asynchronously, and to ensure we stay responsive.
|
// any immediate decoding to guarantee that all our decode notifications are
|
||||||
DecodeWorker::Singleton()->RequestDecode(this);
|
// dispatched asynchronously, and to ensure we stay responsive.
|
||||||
|
DecodePool::Singleton()->RequestDecode(this);
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -2848,6 +2888,16 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
|||||||
nsresult
|
nsresult
|
||||||
RasterImage::SyncDecode()
|
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;
|
nsresult rv;
|
||||||
|
|
||||||
SAMPLE_LABEL_PRINTF("RasterImage", "SyncDecode", "%s", GetURIString().get());;
|
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 we have a size decoder open, make sure we get the size
|
||||||
if (mDecoder && mDecoder->IsSizeDecode()) {
|
if (mDecoder && mDecoder->IsSizeDecode()) {
|
||||||
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
|
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
|
||||||
CONTAINER_ENSURE_SUCCESS(rv);
|
CONTAINER_ENSURE_SUCCESS(rv);
|
||||||
|
|
||||||
// If we didn't get the size out of the image, we won't until we get more
|
// 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) {
|
if (mDecoder) {
|
||||||
mDecoder->SetSynchronous(false);
|
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!
|
// All good if no errors!
|
||||||
@ -3126,11 +3179,7 @@ RasterImage::Draw(gfxContext *aContext,
|
|||||||
|
|
||||||
// We use !mDecoded && mHasSourceData to mean discarded.
|
// We use !mDecoded && mHasSourceData to mean discarded.
|
||||||
if (!mDecoded && mHasSourceData) {
|
if (!mDecoded && mHasSourceData) {
|
||||||
mDrawStartTime = TimeStamp::Now();
|
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
|
// 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,
|
PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
|
||||||
("RasterImage[0x%p] canceling decode because image "
|
("RasterImage[0x%p] canceling decode because image "
|
||||||
"is now unlocked.", this));
|
"is now unlocked.", this));
|
||||||
|
MutexAutoLock lock(mDecodingMutex);
|
||||||
FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
||||||
ForceDiscard();
|
ForceDiscard();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -3238,6 +3288,8 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes)
|
|||||||
// We should have a decoder if we get here
|
// We should have a decoder if we get here
|
||||||
NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
|
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
|
// 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
|
// thread, let the decoder deal with the data it set aside at that time by
|
||||||
// passing it a null buffer.
|
// passing it a null buffer.
|
||||||
@ -3255,7 +3307,7 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes)
|
|||||||
|
|
||||||
// write the proper amount of data
|
// write the proper amount of data
|
||||||
uint32_t bytesToDecode = std::min(aMaxBytes,
|
uint32_t bytesToDecode = std::min(aMaxBytes,
|
||||||
mSourceData.Length() - mBytesDecoded);
|
mSourceData.Length() - mBytesDecoded);
|
||||||
nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
|
nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
|
||||||
bytesToDecode);
|
bytesToDecode);
|
||||||
|
|
||||||
@ -3311,6 +3363,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) {
|
||||||
|
MutexAutoLock lock(mDecodingMutex);
|
||||||
FinishedSomeDecoding(eShutdownIntent_Error);
|
FinishedSomeDecoding(eShutdownIntent_Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3383,6 +3436,8 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
|||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
mDecodingMutex.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
nsRefPtr<DecodeRequest> request;
|
nsRefPtr<DecodeRequest> request;
|
||||||
if (aRequest) {
|
if (aRequest) {
|
||||||
request = aRequest;
|
request = aRequest;
|
||||||
@ -3444,135 +3499,134 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then, tell the observers what happened in the decoder.
|
imgStatusTracker::StatusDiff diff;
|
||||||
// If we have no request, we have not yet created a decoder, but we still
|
|
||||||
// need to send out notifications.
|
|
||||||
if (request) {
|
if (request) {
|
||||||
image->mStatusTracker->SyncAndSyncNotifyDifference(request->mStatusTracker);
|
diff = image->mStatusTracker->CalculateAndApplyDifference(request->mStatusTracker);
|
||||||
} else {
|
|
||||||
image->mStatusTracker->SyncNotifyDecodeState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we were a size decode and a full decode was requested, now's the time.
|
{
|
||||||
if (NS_SUCCEEDED(rv) && done && wasSize && image->mWantFullDecode) {
|
// Notifications can't go out with the decoding lock held.
|
||||||
image->mWantFullDecode = false;
|
MutexAutoUnlock unlock(mDecodingMutex);
|
||||||
|
|
||||||
// If we're not meant to be storing source data and we just got the size,
|
// Then, tell the observers what happened in the decoder.
|
||||||
// we need to synchronously flush all the data we got to a full decoder.
|
// If we have no request, we have not yet created a decoder, but we still
|
||||||
// When that decoder is shut down, we'll also clear our source data.
|
// need to send out notifications.
|
||||||
if (!image->StoringSourceData()) {
|
if (request) {
|
||||||
rv = image->SyncDecode();
|
image->mStatusTracker->SyncNotifyDifference(diff);
|
||||||
} else {
|
} else {
|
||||||
rv = image->RequestDecode();
|
image->mStatusTracker->SyncNotifyDecodeState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we were a size decode and a full decode was requested, now's the time.
|
||||||
|
if (NS_SUCCEEDED(rv) && done && wasSize && image->mWantFullDecode) {
|
||||||
|
image->mWantFullDecode = false;
|
||||||
|
|
||||||
|
// If we're not meant to be storing source data and we just got the size,
|
||||||
|
// we need to synchronously flush all the data we got to a full decoder.
|
||||||
|
// When that decoder is shut down, we'll also clear our source data.
|
||||||
|
if (!image->StoringSourceData()) {
|
||||||
|
rv = image->SyncDecode();
|
||||||
|
} else {
|
||||||
|
rv = image->RequestDecode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ RasterImage::DecodeWorker*
|
NS_IMPL_THREADSAFE_ISUPPORTS1(RasterImage::DecodePool,
|
||||||
RasterImage::DecodeWorker::Singleton()
|
nsIObserver)
|
||||||
|
|
||||||
|
/* static */ RasterImage::DecodePool*
|
||||||
|
RasterImage::DecodePool::Singleton()
|
||||||
{
|
{
|
||||||
if (!sSingleton) {
|
if (!sSingleton) {
|
||||||
sSingleton = new DecodeWorker();
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
sSingleton = new DecodePool();
|
||||||
ClearOnShutdown(&sSingleton);
|
ClearOnShutdown(&sSingleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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.
|
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
|
||||||
DecodeRequest* request = mASAPDecodeRequests.getFirst();
|
if (obsSvc) {
|
||||||
while (request) {
|
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
|
||||||
request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
}
|
||||||
|
|
||||||
request = request->getNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
request = mNormalDecodeRequests.getFirst();
|
|
||||||
while (request) {
|
|
||||||
request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
|
|
||||||
|
|
||||||
request = request->getNext();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
RasterImage::DecodePool::~DecodePool()
|
||||||
RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
|
|
||||||
{
|
{
|
||||||
// We can be marked as ASAP before we've been asked to decode. If we are,
|
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
|
||||||
// create the request so we have somewhere to write down our status.
|
}
|
||||||
if (!aImg->mDecodeRequest) {
|
|
||||||
aImg->mDecodeRequest = new DecodeRequest(aImg);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecodeRequest* request = aImg->mDecodeRequest;
|
return NS_OK;
|
||||||
|
|
||||||
// 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
|
void
|
||||||
RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode)
|
RasterImage::DecodePool::RequestDecode(RasterImage* aImg)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aImg->mDecoder);
|
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
|
// If we're currently waiting on a new frame for this image, we can't do any
|
||||||
// decoding.
|
// decoding.
|
||||||
if (!aImg->mDecoder->NeedsNewFrame()) {
|
if (!aImg->mDecoder->NeedsNewFrame()) {
|
||||||
AddDecodeRequest(aImg->mDecodeRequest, aImg->mSourceData.Length() - aImg->mBytesDecoded);
|
// No matter whether this is currently being decoded, we need to update the
|
||||||
EnsurePendingInEventLoop();
|
// 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
|
void
|
||||||
RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
|
RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
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);
|
DecodeSomeOfImage(aImg);
|
||||||
|
|
||||||
@ -3594,98 +3648,90 @@ RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
/* static */ void
|
||||||
RasterImage::DecodeWorker::EnsurePendingInEventLoop()
|
RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
|
||||||
{
|
{
|
||||||
if (!mPendingInEventLoop) {
|
aImg->mDecodingMutex.AssertCurrentThreadOwns();
|
||||||
mPendingInEventLoop = true;
|
|
||||||
NS_DispatchToCurrentThread(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
|
|
||||||
{
|
|
||||||
// If we haven't got a decode request, we're not currently decoding. (Having
|
// If we haven't got a decode request, we're not currently decoding. (Having
|
||||||
// a decode request doesn't imply we *are* decoding, though.)
|
// a decode request doesn't imply we *are* decoding, though.)
|
||||||
if (aImg->mDecodeRequest) {
|
if (aImg->mDecodeRequest) {
|
||||||
if (aImg->mDecodeRequest->isInList()) {
|
aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_INACTIVE;
|
||||||
aImg->mDecodeRequest->remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
RasterImage::DecodeWorker::Run()
|
RasterImage::DecodePool::DecodeJob::Run()
|
||||||
{
|
{
|
||||||
// We just got called back by the event loop; therefore, we're no longer
|
MutexAutoLock imglock(mImage->mDecodingMutex);
|
||||||
// pending.
|
|
||||||
mPendingInEventLoop = false;
|
|
||||||
|
|
||||||
TimeStamp eventStart = TimeStamp::Now();
|
// If we were interrupted, we shouldn't do any work.
|
||||||
|
if (mRequest->mRequestStatus == DecodeRequest::REQUEST_STOPPED) {
|
||||||
// Now decode until we either run out of time or run out of images.
|
return NS_OK;
|
||||||
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;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
DecodeSomeOfImage(image, DECODE_TYPE_NORMAL, request->mBytesToDecode);
|
|
||||||
|
|
||||||
uint32_t bytesDecoded = image->mBytesDecoded - oldByteCount;
|
|
||||||
|
|
||||||
// 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 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 {
|
|
||||||
// Nothing more for us to do - let everyone know what happened.
|
|
||||||
DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
} 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,
|
// If someone came along and synchronously decoded us, there's nothing for us to do.
|
||||||
uint32_t((TimeStamp::Now() - eventStart).ToMicroseconds()));
|
if (!mRequest->mAllocatedNewFrame && (!mImage->mDecoder || mImage->IsDecodeFinished() ||
|
||||||
|
mRequest->mRequestStatus != DecodeRequest::REQUEST_PENDING)) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE;
|
||||||
|
|
||||||
|
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 (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->IsDecodeFinished() &&
|
||||||
|
bytesDecoded < mRequest->mBytesToDecode) {
|
||||||
|
DecodePool::Singleton()->RequestDecode(mImage);
|
||||||
|
} else {
|
||||||
|
// Nothing more for us to do - let everyone know what happened.
|
||||||
|
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
|
RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
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);
|
nsresult rv = DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
@ -3703,12 +3749,13 @@ RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
|
RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg,
|
||||||
DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */,
|
DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */,
|
||||||
uint32_t bytesToDecode /* = 0 */)
|
uint32_t bytesToDecode /* = 0 */)
|
||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(aImg->mInitialized,
|
NS_ABORT_IF_FALSE(aImg->mInitialized,
|
||||||
"Worker active for uninitialized container!");
|
"Worker active for uninitialized container!");
|
||||||
|
aImg->mDecodingMutex.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
// If an error is flagged, it probably happened while we were waiting
|
// If an error is flagged, it probably happened while we were waiting
|
||||||
// in the event queue.
|
// in the event queue.
|
||||||
@ -3720,9 +3767,17 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
|
|||||||
if (!aImg->mDecoder || aImg->mDecoded)
|
if (!aImg->mDecoder || aImg->mDecoded)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
// If we're currently waiting on a new frame for this image, we can't do any
|
// If we're doing synchronous decodes, and we're waiting on a new frame for
|
||||||
// decoding right now.
|
// this image, get it now.
|
||||||
if (aImg->mDecoder->NeedsNewFrame()) {
|
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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3773,7 +3828,7 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
|
|||||||
|
|
||||||
// Yield if we've been decoding for too long. We check this _after_ decoding
|
// 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.
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3818,6 +3873,8 @@ RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeReques
|
|||||||
void
|
void
|
||||||
RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request)
|
RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request)
|
||||||
{
|
{
|
||||||
|
image->mDecodingMutex.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
nsCOMPtr<DecodeDoneWorker> worker = new DecodeDoneWorker(image, request);
|
nsCOMPtr<DecodeDoneWorker> worker = new DecodeDoneWorker(image, request);
|
||||||
NS_DispatchToMainThread(worker);
|
NS_DispatchToMainThread(worker);
|
||||||
}
|
}
|
||||||
@ -3825,12 +3882,15 @@ RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, De
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
RasterImage::DecodeDoneWorker::Run()
|
RasterImage::DecodeDoneWorker::Run()
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MutexAutoLock lock(mImage->mDecodingMutex);
|
||||||
|
|
||||||
mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest);
|
mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest);
|
||||||
|
|
||||||
// If we didn't finish decoding yet, try again
|
// If we didn't finish decoding yet, try again
|
||||||
if (mImage->mDecoder && !mImage->IsDecodeFinished() &&
|
if (mImage->mDecoder && !mImage->IsDecodeFinished() &&
|
||||||
mImage->mSourceData.Length() > mImage->mBytesDecoded) {
|
mImage->mSourceData.Length() > mImage->mBytesDecoded) {
|
||||||
DecodeWorker::Singleton()->RequestDecode(mImage);
|
DecodePool::Singleton()->RequestDecode(mImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@ -3851,6 +3911,7 @@ RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image)
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
RasterImage::FrameNeededWorker::Run()
|
RasterImage::FrameNeededWorker::Run()
|
||||||
{
|
{
|
||||||
|
MutexAutoLock lock(mImage->mDecodingMutex);
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
// If we got a synchronous decode in the mean time, we don't need to do
|
// If we got a synchronous decode in the mean time, we don't need to do
|
||||||
@ -3861,7 +3922,8 @@ RasterImage::FrameNeededWorker::Run()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
|
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;
|
return NS_OK;
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#define mozilla_imagelib_RasterImage_h_
|
#define mozilla_imagelib_RasterImage_h_
|
||||||
|
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
#include "nsCOMArray.h"
|
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "imgIContainer.h"
|
#include "imgIContainer.h"
|
||||||
#include "nsIProperties.h"
|
#include "nsIProperties.h"
|
||||||
@ -28,18 +27,20 @@
|
|||||||
#include "imgFrame.h"
|
#include "imgFrame.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "DiscardTracker.h"
|
#include "DiscardTracker.h"
|
||||||
|
#include "nsISupportsImpl.h"
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
#include "mozilla/LinkedList.h"
|
#include "mozilla/LinkedList.h"
|
||||||
#include "mozilla/StaticPtr.h"
|
#include "mozilla/StaticPtr.h"
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/Mutex.h"
|
||||||
#include "gfx2DGlue.h"
|
#include "gfx2DGlue.h"
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#include "imgIContainerDebug.h"
|
#include "imgIContainerDebug.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class nsIInputStream;
|
class nsIInputStream;
|
||||||
|
class nsIThreadPool;
|
||||||
|
|
||||||
#define NS_RASTERIMAGE_CID \
|
#define NS_RASTERIMAGE_CID \
|
||||||
{ /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */ \
|
{ /* 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
|
* Each RasterImage has a pointer to one or zero heap-allocated
|
||||||
* DecodeRequests.
|
* DecodeRequests.
|
||||||
*/
|
*/
|
||||||
struct DecodeRequest : public LinkedListElement<DecodeRequest>,
|
struct DecodeRequest
|
||||||
public RefCounted<DecodeRequest>
|
|
||||||
{
|
{
|
||||||
DecodeRequest(RasterImage* aImage)
|
DecodeRequest(RasterImage* aImage)
|
||||||
: mImage(aImage)
|
: mImage(aImage)
|
||||||
, mBytesToDecode(0)
|
, mBytesToDecode(0)
|
||||||
|
, mRequestStatus(REQUEST_INACTIVE)
|
||||||
, mChunkCount(0)
|
, mChunkCount(0)
|
||||||
, mAllocatedNewFrame(false)
|
, mAllocatedNewFrame(false)
|
||||||
, mIsASAP(false)
|
|
||||||
{
|
{
|
||||||
mStatusTracker = aImage->mStatusTracker->CloneForRecording();
|
mStatusTracker = aImage->mStatusTracker->CloneForRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodeRequest)
|
||||||
|
|
||||||
// The status tracker that is associated with a given decode request, to
|
// The status tracker that is associated with a given decode request, to
|
||||||
// ensure their lifetimes are linked.
|
// ensure their lifetimes are linked.
|
||||||
nsRefPtr<imgStatusTracker> mStatusTracker;
|
nsRefPtr<imgStatusTracker> mStatusTracker;
|
||||||
@ -414,6 +413,15 @@ private:
|
|||||||
|
|
||||||
uint32_t mBytesToDecode;
|
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
|
/* Keeps track of how much time we've burned decoding this particular decode
|
||||||
* request. */
|
* request. */
|
||||||
TimeDuration mDecodeTime;
|
TimeDuration mDecodeTime;
|
||||||
@ -424,35 +432,31 @@ private:
|
|||||||
/* True if a new frame has been allocated, but DecodeSomeData hasn't yet
|
/* True if a new frame has been allocated, but DecodeSomeData hasn't yet
|
||||||
* been called to flush data to it */
|
* been called to flush data to it */
|
||||||
bool mAllocatedNewFrame;
|
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
|
* 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
|
* 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.
|
* 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
|
* all others it's managing) in chunks, periodically yielding control back to
|
||||||
* the event loop.
|
* 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:
|
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);
|
void RequestDecode(RasterImage* aImg);
|
||||||
|
|
||||||
@ -463,23 +467,13 @@ private:
|
|||||||
void DecodeABitOf(RasterImage* aImg);
|
void DecodeABitOf(RasterImage* aImg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Give this image ASAP priority; it will be decoded before all non-ASAP
|
* Ask the DecodePool to stop decoding this image. Internally, we also
|
||||||
* 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
|
|
||||||
* call this function when we finish decoding an image.
|
* 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!
|
* 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
|
* Synchronously decode the beginning of the image until we run out of
|
||||||
@ -491,56 +485,56 @@ private:
|
|||||||
*/
|
*/
|
||||||
nsresult DecodeUntilSizeAvailable(RasterImage* aImg);
|
nsresult DecodeUntilSizeAvailable(RasterImage* aImg);
|
||||||
|
|
||||||
NS_IMETHOD Run();
|
virtual ~DecodePool();
|
||||||
|
|
||||||
virtual ~DecodeWorker();
|
|
||||||
|
|
||||||
private: /* statics */
|
private: /* statics */
|
||||||
static StaticRefPtr<DecodeWorker> sSingleton;
|
static StaticRefPtr<DecodePool> sSingleton;
|
||||||
|
|
||||||
private: /* methods */
|
private: /* methods */
|
||||||
DecodeWorker()
|
DecodePool();
|
||||||
: 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);
|
|
||||||
|
|
||||||
enum DecodeType {
|
enum DecodeType {
|
||||||
DECODE_TYPE_NORMAL,
|
DECODE_TYPE_UNTIL_TIME,
|
||||||
DECODE_TYPE_UNTIL_SIZE
|
DECODE_TYPE_UNTIL_SIZE,
|
||||||
|
DECODE_TYPE_UNTIL_DONE_BYTES
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE,
|
/* 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
|
* 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,
|
nsresult DecodeSomeOfImage(RasterImage* aImg,
|
||||||
DecodeType aDecodeType = DECODE_TYPE_NORMAL,
|
DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME,
|
||||||
uint32_t bytesToDecode = 0);
|
uint32_t bytesToDecode = 0);
|
||||||
|
|
||||||
/* Create a new DecodeRequest suitable for doing some decoding and set it
|
/* A decode job dispatched to a thread pool by DecodePool.
|
||||||
* as aImg's mDecodeRequest. */
|
*/
|
||||||
void CreateRequestForImage(RasterImage* aImg);
|
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 */
|
private: /* members */
|
||||||
|
|
||||||
LinkedList<DecodeRequest> mASAPDecodeRequests;
|
nsCOMPtr<nsIThreadPool> mThreadPool;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DecodeDoneWorker : public nsRunnable
|
class DecodeDoneWorker : public nsRunnable
|
||||||
{
|
{
|
||||||
public:
|
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.
|
* portion of decoding that needs to be notified about.
|
||||||
*
|
*
|
||||||
* Ensures the decode state accumulated by the decoding process gets
|
* Ensures the decode state accumulated by the decoding process gets
|
||||||
@ -563,7 +557,7 @@ private:
|
|||||||
{
|
{
|
||||||
public:
|
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.
|
* 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
|
* Dispatches an event to do so, which will further dispatch a
|
||||||
@ -754,16 +748,10 @@ private: // data
|
|||||||
DiscardTracker::Node mDiscardTrackerNode;
|
DiscardTracker::Node mDiscardTrackerNode;
|
||||||
|
|
||||||
// Source data members
|
// Source data members
|
||||||
FallibleTArray<char> mSourceData;
|
|
||||||
nsCString mSourceDataMimeType;
|
nsCString mSourceDataMimeType;
|
||||||
|
|
||||||
friend class DiscardTracker;
|
friend class DiscardTracker;
|
||||||
|
|
||||||
// Decoder and friends
|
|
||||||
nsRefPtr<Decoder> mDecoder;
|
|
||||||
nsRefPtr<DecodeRequest> mDecodeRequest;
|
|
||||||
uint32_t mBytesDecoded;
|
|
||||||
|
|
||||||
// How many times we've decoded this image.
|
// How many times we've decoded this image.
|
||||||
// This is currently only used for statistics
|
// This is currently only used for statistics
|
||||||
int32_t mDecodeCount;
|
int32_t mDecodeCount;
|
||||||
@ -778,6 +766,22 @@ private: // data
|
|||||||
uint32_t mFramesNotified;
|
uint32_t mFramesNotified;
|
||||||
#endif
|
#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):
|
// Boolean flags (clustered together to conserve space):
|
||||||
bool mHasSize:1; // Has SetSize() been called?
|
bool mHasSize:1; // Has SetSize() been called?
|
||||||
bool mDecodeOnDraw:1; // Decoding on draw?
|
bool mDecodeOnDraw:1; // Decoding on draw?
|
||||||
@ -789,7 +793,6 @@ private: // data
|
|||||||
bool mDecoded:1;
|
bool mDecoded:1;
|
||||||
bool mHasBeenDecoded:1;
|
bool mHasBeenDecoded:1;
|
||||||
|
|
||||||
bool mInDecoder:1;
|
|
||||||
|
|
||||||
// Whether the animation can stop, due to running out
|
// Whether the animation can stop, due to running out
|
||||||
// of frames, or no more owning request
|
// of frames, or no more owning request
|
||||||
|
@ -388,7 +388,8 @@ VectorImage::OnImageDataComplete(nsIRequest* aRequest,
|
|||||||
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
|
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
|
||||||
imgDecoderObserver* observer = clone->GetDecoderObserver();
|
imgDecoderObserver* observer = clone->GetDecoderObserver();
|
||||||
observer->OnStopRequest(aLastPart, finalStatus);
|
observer->OnStopRequest(aLastPart, finalStatus);
|
||||||
mStatusTracker->SyncAndSyncNotifyDifference(clone);
|
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
|
||||||
|
mStatusTracker->SyncNotifyDifference(diff);
|
||||||
}
|
}
|
||||||
return finalStatus;
|
return finalStatus;
|
||||||
}
|
}
|
||||||
@ -846,7 +847,8 @@ VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
|
|||||||
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
|
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
|
||||||
imgDecoderObserver* observer = clone->GetDecoderObserver();
|
imgDecoderObserver* observer = clone->GetDecoderObserver();
|
||||||
observer->OnStartDecode();
|
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
|
// Create a listener to wait until the SVG document is fully loaded, which
|
||||||
@ -933,7 +935,8 @@ VectorImage::OnSVGDocumentLoaded()
|
|||||||
observer->OnStopFrame();
|
observer->OnStopFrame();
|
||||||
observer->OnStopDecode(NS_OK); // Unblock page load.
|
observer->OnStopDecode(NS_OK); // Unblock page load.
|
||||||
|
|
||||||
mStatusTracker->SyncAndSyncNotifyDifference(clone);
|
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
|
||||||
|
mStatusTracker->SyncNotifyDifference(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
EvaluateAnimation();
|
EvaluateAnimation();
|
||||||
@ -955,7 +958,8 @@ VectorImage::OnSVGDocumentError()
|
|||||||
|
|
||||||
// Unblock page load.
|
// Unblock page load.
|
||||||
observer->OnStopDecode(NS_ERROR_FAILURE);
|
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::StatusDiff
|
||||||
imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
|
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.
|
// We must not modify or notify for the start-load state, which happens from Necko callbacks.
|
||||||
uint32_t loadState = mState & stateRequestStarted;
|
uint32_t loadState = mState & stateRequestStarted;
|
||||||
uint32_t diffState = ~mState & other->mState & ~stateRequestStarted;
|
|
||||||
bool unblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload);
|
StatusDiff diff;
|
||||||
bool foundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR);
|
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
|
// Now that we've calculated the difference in state, synchronize our state
|
||||||
// with the other tracker.
|
// with the other tracker.
|
||||||
|
|
||||||
// First, actually synchronize our state.
|
// First, actually synchronize our state.
|
||||||
mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
|
diff.mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
|
||||||
mState |= diffState | loadState;
|
mState |= diff.mDiffState | loadState;
|
||||||
if (unblockedOnload) {
|
if (diff.mUnblockedOnload) {
|
||||||
mState &= ~stateBlockingOnload;
|
mState &= ~stateBlockingOnload;
|
||||||
}
|
}
|
||||||
mImageStatus = other->mImageStatus;
|
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);
|
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
|
||||||
while (iter.HasMore()) {
|
while (iter.HasMore()) {
|
||||||
// Hold on to a reference to this proxy, since notifying the state can
|
// 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.
|
if (diff.mFoundError) {
|
||||||
other->mInvalidRect.SetEmpty();
|
|
||||||
mInvalidRect.SetEmpty();
|
|
||||||
|
|
||||||
if (foundError) {
|
|
||||||
FireFailureNotification();
|
FireFailureNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,11 @@ class Image;
|
|||||||
|
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsAutoPtr.h"
|
|
||||||
#include "nsTObserverArray.h"
|
#include "nsTObserverArray.h"
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
#include "nscore.h"
|
#include "nscore.h"
|
||||||
#include "imgDecoderObserver.h"
|
#include "imgDecoderObserver.h"
|
||||||
|
#include "nsISupportsImpl.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
stateRequestStarted = 1u << 0,
|
stateRequestStarted = 1u << 0,
|
||||||
@ -51,9 +51,11 @@ enum {
|
|||||||
* and the notifications will be replayed to the proxy asynchronously.
|
* and the notifications will be replayed to the proxy asynchronously.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class imgStatusTracker : public mozilla::RefCounted<imgStatusTracker>
|
class imgStatusTracker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker)
|
||||||
|
|
||||||
// aImage is the image that this status tracker will pass to the
|
// aImage is the image that this status tracker will pass to the
|
||||||
// imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be
|
// imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be
|
||||||
// alive as long as this instance is, because we hold a weak reference to it.
|
// 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(); }
|
inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
|
||||||
|
|
||||||
imgStatusTracker* CloneForRecording();
|
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; }
|
nsIntRect GetInvalidRect() const { return mInvalidRect; }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user