author | Ryan VanderMeulen <ryanvm@gmail.com> |
Wed, 15 Oct 2014 19:00:12 -0400 | |
changeset 210642 | a13927f7835342093b3dca3a4e6b0c2a89dd4a29 |
parent 210641 | 21caa5dfb494bacb31882115053a8d824c44f0ba |
child 210643 | 5ce2fc3939da728fdfcd4508a031cbcbf780b163 |
push id | 1 |
push user | root |
push date | Mon, 20 Oct 2014 17:29:22 +0000 |
bugs | 1079653 |
milestone | 36.0a1 |
backs out | aaac8c67129941d2a969ed0df7c76eb30dacd789 59d1754eb01fd84ec139d686fc7b2f79079f17c1 dfb0890b02be6dc35a3b76ddb1d5c31a3a9a888a 546f90c14465afd29b88fe16ff27bf5678b285d5 a8e760faf784c78986c0cbebede0177fd361ad4c |
--- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -14,25 +14,23 @@ namespace mozilla { namespace image { Decoder::Decoder(RasterImage &aImage) : mImage(aImage) , mCurrentFrame(nullptr) , mImageData(nullptr) , mColormap(nullptr) - , mChunkCount(0) , mDecodeFlags(0) , mBytesDecoded(0) , mDecodeDone(false) , mDataError(false) , mFrameCount(0) , mFailCode(NS_OK) , mNeedsNewFrame(false) - , mNeedsToFlushData(false) , mInitialized(false) , mSizeDecode(false) , mInFrame(false) , mIsAnimated(false) { } Decoder::~Decoder() @@ -94,30 +92,20 @@ Decoder::Write(const char* aBuffer, uint js::ProfileEntry::Category::GRAPHICS); MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC); // We're strict about decoder errors MOZ_ASSERT(!HasDecoderError(), "Not allowed to make more decoder calls after error!"); - // Begin recording telemetry data. - TimeStamp start = TimeStamp::Now(); - mChunkCount++; - // Keep track of the total number of bytes written. mBytesDecoded += aCount; - // If we're flushing data, clear the flag. - if (aBuffer == nullptr && aCount == 0) { - MOZ_ASSERT(mNeedsToFlushData, "Flushing when we don't need to"); - mNeedsToFlushData = false; - } - - // If a data error occured, just ignore future data. + // If a data error occured, just ignore future data if (HasDataError()) return; if (IsSizeDecode() && HasSize()) { // More data came in since we found the size. We have nothing to do here. return; } @@ -129,19 +117,16 @@ Decoder::Write(const char* aBuffer, uint while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) { nsresult rv = AllocateFrame(); if (NS_SUCCEEDED(rv)) { // Tell the decoder to use the data it saved when it asked for a new frame. WriteInternal(nullptr, 0, aStrategy); } } - - // Finish telemetry. - mDecodeTime += (TimeStamp::Now() - start); } void Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent) { MOZ_ASSERT(NS_IsMainThread()); // Implementation-specific finalization @@ -252,22 +237,16 @@ Decoder::AllocateFrame() } else if (NS_FAILED(rv)) { PostDataError(); } // Mark ourselves as not needing another frame before talking to anyone else // so they can tell us if they need yet another. mNeedsNewFrame = false; - // If we've received any data at all, we may have pending data that needs to - // be flushed now that we have a frame to decode into. - if (mBytesDecoded > 0) { - mNeedsToFlushData = true; - } - return rv; } void Decoder::FlushInvalidations() { NS_ABORT_IF_FALSE(!HasDecoderError(), "Not allowed to make more decoder calls after error!");
--- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -39,21 +39,16 @@ public: */ void InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength, uint32_t* colormap, uint32_t colormapSize, imgFrame* currentFrame); /** * Writes data to the decoder. * - * If aBuffer is null and aCount is 0, Write() flushes any buffered data to - * the decoder. Data is buffered if the decoder wasn't able to completely - * decode it because it needed a new frame. If it's necessary to flush data, - * NeedsToFlushData() will return true. - * * @param aBuffer buffer containing the data to be written * @param aCount the number of bytes to write * * Any errors are reported by setting the appropriate state on the decoder. * * Notifications Sent: TODO */ void Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy); @@ -103,22 +98,16 @@ public: void SetObserver(imgDecoderObserver* aObserver) { MOZ_ASSERT(aObserver); mObserver = aObserver; } size_t BytesDecoded() const { return mBytesDecoded; } - // The amount of time we've spent inside Write() so far for this decoder. - TimeDuration DecodeTime() const { return mDecodeTime; } - - // The number of times Write() has been called so far for this decoder. - uint32_t ChunkCount() const { return mChunkCount; } - // The number of frames we have, including anything in-progress. Thus, this // is only 0 if we haven't begun any frames. uint32_t GetFrameCount() { return mFrameCount; } // The number of complete frames we have (ie, not including anything in-progress). uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; } // Error tracking @@ -164,21 +153,16 @@ public: // will be called again with nullptr and 0 as arguments. void NeedNewFrame(uint32_t frameNum, uint32_t x_offset, uint32_t y_offset, uint32_t width, uint32_t height, gfx::SurfaceFormat format, uint8_t palette_depth = 0); virtual bool NeedsNewFrame() const { return mNeedsNewFrame; } - // Returns true if we may have stored data that we need to flush now that we - // have a new frame to decode into. Callers can use Write() to actually - // flush the data; see the documentation for that method. - bool NeedsToFlushData() const { return mNeedsToFlushData; } - // Try to allocate a frame as described in mNewFrameData and return the // status code from that attempt. Clears mNewFrameData. virtual nsresult AllocateFrame(); already_AddRefed<imgFrame> GetCurrentFrame() const { nsRefPtr<imgFrame> frame = mCurrentFrame; return frame.forget(); @@ -246,20 +230,16 @@ protected: RefPtr<imgDecoderObserver> mObserver; ImageMetadata mImageMetadata; uint8_t* mImageData; // Pointer to image data in either Cairo or 8bit format uint32_t mImageDataLength; uint32_t* mColormap; // Current colormap to be used in Cairo format uint32_t mColormapSize; - // Telemetry data for this decoder. - TimeDuration mDecodeTime; - uint32_t mChunkCount; - uint32_t mDecodeFlags; size_t mBytesDecoded; bool mDecodeDone; bool mDataError; private: uint32_t mFrameCount; // Number of frames, including anything in-progress @@ -288,17 +268,16 @@ private: uint32_t mOffsetY; uint32_t mWidth; uint32_t mHeight; gfx::SurfaceFormat mFormat; uint8_t mPaletteDepth; }; NewFrameData mNewFrameData; bool mNeedsNewFrame; - bool mNeedsToFlushData; bool mInitialized; bool mSizeDecode; bool mInFrame; bool mIsAnimated; }; } // namespace image } // namespace mozilla
--- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -316,17 +316,16 @@ RasterImage::RasterImage(imgStatusTracke mLockCount(0), mDecodeCount(0), mRequestedSampleSize(0), #ifdef DEBUG mFramesNotified(0), #endif mDecodingMonitor("RasterImage Decoding Monitor"), mDecoder(nullptr), - mDecodeStatus(DecodeStatus::INACTIVE), mInDecoder(false), mStatusDiff(ImageStatusDiff::NoChange()), mNotifying(false), mHasSize(false), mDecodeOnDraw(false), mMultipart(false), mDiscardable(false), mHasSourceData(false), @@ -1762,18 +1761,17 @@ RasterImage::OnNewSourceData() // The decoder was shut down and we didn't flag an error, so we should be decoded NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData"); // Reset some flags mDecoded = false; mHasSourceData = false; mHasSize = false; mWantFullDecode = true; - mDecodeStatus = DecodeStatus::INACTIVE; - mDecodeStatusTracker = nullptr; + mDecodeRequest = nullptr; if (mAnim) { mAnim->SetDoneDecoding(false); } // We always need the size first. rv = InitDecoder(/* aDoSizeDecode = */ true); CONTAINER_ENSURE_SUCCESS(rv); @@ -1864,18 +1862,17 @@ RasterImage::Discard(bool force) // Flag that we no longer have decoded frames for this image mDecoded = false; // Notify that we discarded if (mStatusTracker) mStatusTracker->OnDiscard(); - mDecodeStatus = DecodeStatus::INACTIVE; - mDecodeStatusTracker = nullptr; + mDecodeRequest = nullptr; if (force) DiscardTracker::Remove(&mDiscardTrackerNode); // Log PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG, ("CompressedImageAccounting: discarded uncompressed image " "data from RasterImage %p (%s) - %d frames (cached count: %d); " @@ -1985,22 +1982,22 @@ RasterImage::InitDecoder(bool aDoSizeDec // case. Regardless, we need to lock the last frame. Our invariant is that, // while we have a decoder open, the last frame is always locked. if (GetNumFrames() > 0) { nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1); curframe->LockImageData(); } // Initialize the decoder - if (!mDecodeStatusTracker) { - MOZ_ASSERT(mStatusTracker, "Should have an imgStatusTracker"); - mDecodeStatusTracker = mStatusTracker->CloneForRecording(); + if (!mDecodeRequest) { + mDecodeRequest = new DecodeRequest(this); } - MOZ_ASSERT(mDecodeStatusTracker->GetDecoderObserver()); - mDecoder->SetObserver(mDecodeStatusTracker->GetDecoderObserver()); + MOZ_ASSERT(mDecodeRequest->mStatusTracker); + MOZ_ASSERT(mDecodeRequest->mStatusTracker->GetDecoderObserver()); + mDecoder->SetObserver(mDecodeRequest->mStatusTracker->GetDecoderObserver()); mDecoder->SetSizeDecode(aDoSizeDecode); mDecoder->SetDecodeFlags(mFrameDecodeFlags); if (!aDoSizeDecode) { // We already have the size; tell the decoder so it can preallocate a // frame. By default, we create an ARGB frame with no offset. If decoders // need a different type, they need to ask for it themselves. mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height, SurfaceFormat::B8G8R8A8); @@ -2226,17 +2223,18 @@ RasterImage::RequestDecodeCore(RequestDe // data, so signal that we want a full decode and give up for now. if (!mHasSize) { mWantFullDecode = true; return NS_OK; } } // If the image is waiting for decode work to be notified, go ahead and do that. - if (mDecodeStatus == DecodeStatus::WORK_DONE && + if (mDecodeRequest && + mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE && aDecodeType == SYNCHRONOUS_NOTIFY) { ReentrantMonitorAutoEnter lock(mDecodingMonitor); nsresult rv = FinishedSomeDecoding(); CONTAINER_ENSURE_SUCCESS(rv); } // If we're fully decoded, we have nothing to do. We need this check after // DecodeUntilSizeAvailable and FinishedSomeDecoding because they can result @@ -2262,17 +2260,19 @@ RasterImage::RequestDecodeCore(RequestDe if (mDecoder && mDecoder->BytesDecoded() > mSourceData.Length()) { return NS_OK; } // After acquiring the lock we may have finished some more decoding, so // we need to repeat the following three checks after getting the lock. // If the image is waiting for decode work to be notified, go ahead and do that. - if (mDecodeStatus == DecodeStatus::WORK_DONE && aDecodeType != ASYNCHRONOUS) { + if (mDecodeRequest && + mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE && + aDecodeType != ASYNCHRONOUS) { nsresult rv = FinishedSomeDecoding(); CONTAINER_ENSURE_SUCCESS(rv); } // If we're fully decoded, we have nothing to do. We need this check after // DecodeUntilSizeAvailable and FinishedSomeDecoding because they can result // in us finishing an in-progress decode (or kicking off and finishing a // synchronous decode if we're already waiting on a full decode). @@ -2353,20 +2353,22 @@ RasterImage::SyncDecode() ReentrantMonitorAutoEnter lock(mDecodingMonitor); // We really have no good way of forcing a synchronous decode if we're being // called in a re-entrant manner (ie, from an event listener fired by a // decoder), because the decoding machinery is already tied up. We thus explicitly // disallow this type of call in the API, and check for it in API methods. NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!"); - // If the image is waiting for decode work to be notified, go ahead and do that. - if (mDecodeStatus == DecodeStatus::WORK_DONE) { - nsresult rv = FinishedSomeDecoding(); - CONTAINER_ENSURE_SUCCESS(rv); + 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; // If we're decoded already, or decoding until the size was available // finished us as a side-effect, no worries if (mDecoded) return NS_OK; @@ -2389,19 +2391,21 @@ RasterImage::SyncDecode() // with the new flags. If we can't discard then there isn't // anything we can do. if (!CanForciblyDiscardAndRedecode()) return NS_ERROR_NOT_AVAILABLE; ForceDiscard(); } } - // If we're currently waiting on a new frame for this image, create it now. + // If we're currently waiting on a new frame for this image, we have to create + // it now. if (mDecoder && mDecoder->NeedsNewFrame()) { mDecoder->AllocateFrame(); + mDecodeRequest->mAllocatedNewFrame = true; } // If we don't have a decoder, create one if (!mDecoder) { rv = InitDecoder(/* aDoSizeDecode = */ false); CONTAINER_ENSURE_SUCCESS(rv); } @@ -2771,17 +2775,18 @@ RasterImage::DecodeSomeData(size_t aMaxB { MOZ_ASSERT(mDecoder, "Should have a decoder"); mDecodingMonitor.AssertCurrentThreadIn(); // 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. - if (mDecoder->NeedsToFlushData()) { + if (mDecodeRequest->mAllocatedNewFrame) { + mDecodeRequest->mAllocatedNewFrame = false; nsresult rv = WriteToDecoder(nullptr, 0, aStrategy); if (NS_FAILED(rv) || mDecoder->NeedsNewFrame()) { return rv; } } // If we have nothing else to decode, return. if (mDecoder->BytesDecoded() == mSourceData.Length()) { @@ -2817,17 +2822,18 @@ RasterImage::IsDecodeFinished() } } else if (mDecoder->GetDecodeDone()) { return true; } // If the decoder returned because it needed a new frame and we haven't // written to it since then, the decoder may be storing data that it hasn't // decoded yet. - if (mDecoder->NeedsNewFrame() || mDecoder->NeedsToFlushData()) { + if (mDecoder->NeedsNewFrame() || + (mDecodeRequest && mDecodeRequest->mAllocatedNewFrame)) { return false; } // Otherwise, if we have all the source data and wrote all the source data, // we're done. // // (NB - This can be the case even for non-erroneous images because // Decoder::GetDecodeDone() might not return true until after we call @@ -2971,38 +2977,40 @@ RasterImage::RequestDecodeIfNeeded(nsres } // We don't need a full decode right now, so just return the existing status. return aStatus; } nsresult RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */, - imgStatusTracker* aDecodeTracker /* = nullptr */) + DecodeRequest* aRequest /* = nullptr */) { MOZ_ASSERT(NS_IsMainThread()); mDecodingMonitor.AssertCurrentThreadIn(); - nsRefPtr<imgStatusTracker> statusTracker = aDecodeTracker - ? aDecodeTracker - : mDecodeStatusTracker.get(); + nsRefPtr<DecodeRequest> request; + if (aRequest) { + request = aRequest; + } else { + request = mDecodeRequest; + } // Ensure that, if the decoder is the last reference to the image, we don't // destroy it by destroying the decoder. nsRefPtr<RasterImage> image(this); bool done = false; bool wasSize = false; nsresult rv = NS_OK; if (image->mDecoder) { - if (!image->mDecoder->IsSizeDecode() && image->mDecoder->ChunkCount()) { - Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, - image->mDecoder->ChunkCount()); + if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) { + Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount); } if (!image->mHasSize && image->mDecoder->HasSize()) { image->mDecoder->SetSizeOnImage(); } // If the decode finished, or we're specifically being told to shut down, // tell the image and shut down the decoder. @@ -3010,42 +3018,42 @@ RasterImage::FinishedSomeDecoding(eShutd done = true; // Hold on to a reference to the decoder until we're done with it nsRefPtr<Decoder> decoder = image->mDecoder; wasSize = decoder->IsSizeDecode(); // Do some telemetry if this isn't a size decode. - if (!wasSize) { + if (request && !wasSize) { Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME, - int32_t(decoder->DecodeTime().ToMicroseconds())); + int32_t(request->mDecodeTime.ToMicroseconds())); // We record the speed for only some decoders. The rest have // SpeedHistogram return HistogramCount. Telemetry::ID id = decoder->SpeedHistogram(); if (id < Telemetry::HistogramCount) { int32_t KBps = int32_t(decoder->BytesDecoded() / - (1024 * decoder->DecodeTime().ToSeconds())); + (1024 * request->mDecodeTime.ToSeconds())); Telemetry::Accumulate(id, KBps); } } // We need to shut down the decoder first, in order to ensure all // decoding routines have been finished. rv = image->ShutdownDecoder(aIntent); if (NS_FAILED(rv)) { image->DoError(); } } } - ImageStatusDiff diff = statusTracker - ? image->mStatusTracker->Difference(statusTracker) - : image->mStatusTracker->DecodeStateAsDifference(); + ImageStatusDiff diff = + request ? image->mStatusTracker->Difference(request->mStatusTracker) + : image->mStatusTracker->DecodeStateAsDifference(); image->mStatusTracker->ApplyDifference(diff); if (mNotifying) { // Accumulate the status changes. We don't permit recursive notifications // because they cause subtle concurrency bugs, so we'll delay sending out // the notifications until we pop back to the lowest invocation of // FinishedSomeDecoding on the stack. NS_WARNING("Recursively notifying in RasterImage::FinishedSomeDecoding!"); @@ -3192,44 +3200,51 @@ void RasterImage::DecodePool::RequestDecode(RasterImage* aImg) { MOZ_ASSERT(aImg->mDecoder); aImg->mDecodingMonitor.AssertCurrentThreadIn(); // If we're currently waiting on a new frame for this image, we can't do any // decoding. if (!aImg->mDecoder->NeedsNewFrame()) { - if (aImg->mDecodeStatus == DecodeStatus::PENDING || - aImg->mDecodeStatus == DecodeStatus::ACTIVE) { + // 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->mDecoder->BytesDecoded(); + + 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->mDecodeStatus = DecodeStatus::PENDING; - nsRefPtr<DecodeJob> job = new DecodeJob(aImg); + aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_PENDING; + nsRefPtr<DecodeJob> job = new DecodeJob(aImg->mDecodeRequest, aImg); MutexAutoLock threadPoolLock(mThreadPoolMutex); if (!gfxPrefs::ImageMTDecodingEnabled() || !mThreadPool) { NS_DispatchToMainThread(job); } else { mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL); } } } void RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy) { MOZ_ASSERT(NS_IsMainThread()); aImg->mDecodingMonitor.AssertCurrentThreadIn(); - // If the image is waiting for decode work to be notified, go ahead and do that. - if (aImg->mDecodeStatus == DecodeStatus::WORK_DONE) { - aImg->FinishedSomeDecoding(); + 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, aStrategy); aImg->FinishedSomeDecoding(); // If the decoder needs a new frame, enqueue an event to get it; that event // will enqueue another decode request when it's done. @@ -3249,84 +3264,80 @@ RasterImage::DecodePool::DecodeABitOf(Ra /* static */ void RasterImage::DecodePool::StopDecoding(RasterImage* aImg) { aImg->mDecodingMonitor.AssertCurrentThreadIn(); // If we haven't got a decode request, we're not currently decoding. (Having // a decode request doesn't imply we *are* decoding, though.) - aImg->mDecodeStatus = DecodeStatus::STOPPED; + if (aImg->mDecodeRequest) { + aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_STOPPED; + } } NS_IMETHODIMP RasterImage::DecodePool::DecodeJob::Run() { ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor); // If we were interrupted, we shouldn't do any work. - if (mImage->mDecodeStatus == DecodeStatus::STOPPED) { - DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, - mImage->mDecodeStatusTracker); + if (mRequest->mRequestStatus == DecodeRequest::REQUEST_STOPPED) { + DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); return NS_OK; } // If someone came along and synchronously decoded us, there's nothing for us to do. if (!mImage->mDecoder || mImage->IsDecodeFinished()) { - DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, - mImage->mDecodeStatusTracker); + DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); return NS_OK; } // If we're a decode job that's been enqueued since a previous decode that // still needs a new frame, we can't do anything. Wait until the // FrameNeededWorker enqueues another frame. if (mImage->mDecoder->NeedsNewFrame()) { return NS_OK; } - mImage->mDecodeStatus = DecodeStatus::ACTIVE; + mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE; size_t oldByteCount = mImage->mDecoder->BytesDecoded(); DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES; // Multithreaded decoding can be disabled. If we've done so, we don't want to // monopolize the main thread, and will allow a timeout in DecodeSomeOfImage. if (NS_IsMainThread()) { type = DECODE_TYPE_UNTIL_TIME; } - size_t maxBytes = mImage->mSourceData.Length() - - mImage->mDecoder->BytesDecoded(); - DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC, - type, maxBytes); + DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC, type, mRequest->mBytesToDecode); size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount; - mImage->mDecodeStatus = DecodeStatus::WORK_DONE; + 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->mPendingError && !mImage->IsDecodeFinished() && - bytesDecoded < maxBytes && + bytesDecoded < mRequest->mBytesToDecode && bytesDecoded > 0) { DecodePool::Singleton()->RequestDecode(mImage); } else { // Nothing more for us to do - let everyone know what happened. - DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, - mImage->mDecodeStatusTracker); + DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); } return NS_OK; } RasterImage::DecodePool::DecodeJob::~DecodeJob() { if (gfxPrefs::ImageMTDecodingEnabled()) { @@ -3344,22 +3355,24 @@ RasterImage::DecodePool::DecodeJob::~Dec } nsresult RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg) { MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter lock(aImg->mDecodingMonitor); - // If the image is waiting for decode work to be notified, go ahead and do that. - if (aImg->mDecodeStatus == DecodeStatus::WORK_DONE) { - nsresult rv = aImg->FinishedSomeDecoding(); - if (NS_FAILED(rv)) { - aImg->DoError(); - return rv; + 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; + } } } // We use DECODE_ASYNC here because we just want to get the size information // here and defer the rest of the work. nsresult rv = DecodeSomeOfImage(aImg, DECODE_ASYNC, DECODE_TYPE_UNTIL_SIZE); if (NS_FAILED(rv)) { return rv; @@ -3400,17 +3413,19 @@ RasterImage::DecodePool::DecodeSomeOfIma // example, a synchronous decode request came while the worker was pending). if (!aImg->mDecoder || aImg->mDecoded) return NS_OK; // If we're doing synchronous decodes, and we're waiting on a new frame for // this image, get it now. if (aStrategy == DECODE_SYNC && aImg->mDecoder->NeedsNewFrame()) { MOZ_ASSERT(NS_IsMainThread()); + aImg->mDecoder->AllocateFrame(); + 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; } nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder; @@ -3426,47 +3441,54 @@ RasterImage::DecodePool::DecodeSomeOfIma // to read the size from most images. maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime(); } if (bytesToDecode == 0) { bytesToDecode = aImg->mSourceData.Length() - aImg->mDecoder->BytesDecoded(); } - TimeStamp deadline = TimeStamp::Now() + - TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield()); + int32_t chunkCount = 0; + TimeStamp start = TimeStamp::Now(); + TimeStamp deadline = start + TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield()); // We keep decoding chunks until: // * we don't have any data left to decode, // * the decode completes, // * we're an UNTIL_SIZE decode and we get the size, or // * we run out of time. // We also try to decode at least one "chunk" if we've allocated a new frame, // even if we have no more data to send to the decoder. while ((aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded() && bytesToDecode > 0 && !aImg->IsDecodeFinished() && !(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize) && !aImg->mDecoder->NeedsNewFrame()) || - aImg->mDecoder->NeedsToFlushData()) { + (aImg->mDecodeRequest && aImg->mDecodeRequest->mAllocatedNewFrame)) { + chunkCount++; uint32_t chunkSize = std::min(bytesToDecode, maxBytes); nsresult rv = aImg->DecodeSomeData(chunkSize, aStrategy); if (NS_FAILED(rv)) { aImg->DoError(); return rv; } bytesToDecode -= chunkSize; // Yield if we've been decoding for too long. We check this _after_ decoding // a chunk to ensure that we don't yield without doing any decoding. if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline) break; } + if (aImg->mDecodeRequest) { + aImg->mDecodeRequest->mDecodeTime += (TimeStamp::Now() - start); + aImg->mDecodeRequest->mChunkCount += chunkCount; + } + // Flush invalidations (and therefore paint) now that we've decoded all the // chunks we're going to. // // However, don't paint if: // // * This was an until-size decode. Until-size decodes are always followed // by normal decodes, so don't bother painting. // @@ -3485,33 +3507,37 @@ RasterImage::DecodePool::DecodeSomeOfIma aImg->mInDecoder = true; aImg->mDecoder->FlushInvalidations(); aImg->mInDecoder = false; } return NS_OK; } +RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeRequest* request) + : mImage(image) + , mRequest(request) +{} + void -RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* aImage, - imgStatusTracker* aTracker) +RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request) { - aImage->mDecodingMonitor.AssertCurrentThreadIn(); - - nsCOMPtr<nsIRunnable> worker = new DecodeDoneWorker(aImage, aTracker); + image->mDecodingMonitor.AssertCurrentThreadIn(); + + nsCOMPtr<nsIRunnable> worker = new DecodeDoneWorker(image, request); NS_DispatchToMainThread(worker); } NS_IMETHODIMP RasterImage::DecodeDoneWorker::Run() { MOZ_ASSERT(NS_IsMainThread()); ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor); - mImage->FinishedSomeDecoding(eShutdownIntent_Done, mTracker); + mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest); return NS_OK; } RasterImage::FrameNeededWorker::FrameNeededWorker(RasterImage* image) : mImage(image) {} @@ -3528,16 +3554,17 @@ RasterImage::FrameNeededWorker::Run() { ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor); nsresult rv = NS_OK; // If we got a synchronous decode in the mean time, we don't need to do // anything. if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) { rv = mImage->mDecoder->AllocateFrame(); + mImage->mDecodeRequest->mAllocatedNewFrame = true; } if (NS_SUCCEEDED(rv) && mImage->mDecoder) { // By definition, we're not done decoding, so enqueue us for more decoding. DecodePool::Singleton()->RequestDecode(mImage); } return NS_OK;
--- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -28,17 +28,16 @@ #include "DecodeStrategy.h" #include "DiscardTracker.h" #include "Orientation.h" #include "nsIObserver.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Mutex.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/TimeStamp.h" -#include "mozilla/TypedEnum.h" #include "mozilla/StaticPtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/UniquePtr.h" #ifdef DEBUG #include "imgIContainerDebug.h" #endif class nsIInputStream; @@ -132,24 +131,16 @@ class Image; } namespace image { class Decoder; class FrameAnimator; class ScaleRunner; -MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t) - INACTIVE, - PENDING, - ACTIVE, - WORK_DONE, - STOPPED -MOZ_END_ENUM_CLASS(DecodeStatus) - class RasterImage MOZ_FINAL : public ImageResource , public nsIProperties , public SupportsWeakPtr<RasterImage> #ifdef DEBUG , public imgIContainerDebug #endif { // (no public constructor - use ImageFactory) @@ -317,22 +308,77 @@ public: // Decode strategy private: already_AddRefed<imgStatusTracker> CurrentStatusTracker() { mDecodingMonitor.AssertCurrentThreadIn(); nsRefPtr<imgStatusTracker> statusTracker; - statusTracker = mDecodeStatusTracker ? mDecodeStatusTracker - : mStatusTracker; + statusTracker = mDecodeRequest ? mDecodeRequest->mStatusTracker + : mStatusTracker; MOZ_ASSERT(statusTracker); return statusTracker.forget(); } + nsresult OnImageDataCompleteCore(nsIRequest* aRequest, nsISupports*, nsresult aStatus); + + /** + * Each RasterImage has a pointer to one or zero heap-allocated + * DecodeRequests. + */ + struct DecodeRequest + { + explicit DecodeRequest(RasterImage* aImage) + : mImage(aImage) + , mBytesToDecode(0) + , mRequestStatus(REQUEST_INACTIVE) + , mChunkCount(0) + , mAllocatedNewFrame(false) + { + MOZ_ASSERT(aImage, "aImage cannot be null"); + MOZ_ASSERT(aImage->mStatusTracker, + "aImage should have an imgStatusTracker"); + 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; + + RasterImage* mImage; + + size_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; + + /* The number of chunks it took to decode this image. */ + int32_t mChunkCount; + + /* True if a new frame has been allocated, but DecodeSomeData hasn't yet + * been called to flush data to it */ + bool mAllocatedNewFrame; + + private: + ~DecodeRequest() {} + }; + /* * DecodePool is a singleton class we use when decoding large images. * * When we wish to decode an image larger than * image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode() * for the image. This adds the image to a queue of pending requests and posts * the DecodePool singleton to the event queue, if it's not already pending * there. @@ -410,24 +456,28 @@ private: DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME, uint32_t bytesToDecode = 0); /* A decode job dispatched to a thread pool by DecodePool. */ class DecodeJob : public nsRunnable { public: - DecodeJob(RasterImage* aImage) : mImage(aImage) { } + DecodeJob(DecodeRequest* aRequest, RasterImage* aImg) + : mRequest(aRequest) + , mImage(aImg) + {} - NS_IMETHOD Run() MOZ_OVERRIDE; + NS_IMETHOD Run(); protected: virtual ~DecodeJob(); private: + nsRefPtr<DecodeRequest> mRequest; nsRefPtr<RasterImage> mImage; }; private: /* members */ // mThreadPoolMutex protects mThreadPool. For all RasterImages R, // R::mDecodingMonitor must be acquired before mThreadPoolMutex // if both are acquired; the other order may cause deadlock. @@ -440,31 +490,27 @@ private: public: /** * Called by the DecodePool with an image when it's done some significant * portion of decoding that needs to be notified about. * * Ensures the decode state accumulated by the decoding process gets * applied to the image. */ - static void NotifyFinishedSomeDecoding(RasterImage* aImage, - imgStatusTracker* aTracker); + static void NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request); NS_IMETHOD Run(); private: /* methods */ - DecodeDoneWorker(RasterImage* aImage, imgStatusTracker* aTracker) - : mImage(aImage) - , mTracker(aTracker) - { } + DecodeDoneWorker(RasterImage* image, DecodeRequest* request); private: /* members */ nsRefPtr<RasterImage> mImage; - nsRefPtr<imgStatusTracker> mTracker; + nsRefPtr<DecodeRequest> mRequest; }; class FrameNeededWorker : public nsRunnable { public: /** * Called by the DecodeJob with an image when it's been told by the * decoder that it needs a new frame to be allocated on the main thread. @@ -479,18 +525,18 @@ private: private: /* methods */ explicit FrameNeededWorker(RasterImage* image); private: /* members */ nsRefPtr<RasterImage> mImage; }; - nsresult FinishedSomeDecoding(eShutdownIntent aIntent = eShutdownIntent_Done, - imgStatusTracker* aDecodeTracker = nullptr); + nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done, + DecodeRequest* request = nullptr); void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef, gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, GraphicsFilter aFilter, uint32_t aFlags); @@ -604,18 +650,17 @@ private: // data // BEGIN LOCKED MEMBER VARIABLES ReentrantMonitor mDecodingMonitor; FallibleTArray<char> mSourceData; // Decoder and friends nsRefPtr<Decoder> mDecoder; - nsRefPtr<imgStatusTracker> mDecodeStatusTracker; - DecodeStatus mDecodeStatus; + nsRefPtr<DecodeRequest> mDecodeRequest; bool mInDecoder; // END LOCKED MEMBER VARIABLES // Notification state. Used to avoid recursive notifications. ImageStatusDiff mStatusDiff; bool mNotifying:1;