зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1079627 (Part 3) - Support multiple decoders for a single RasterImage. r=tn
This commit is contained in:
Родитель
ddb14760dd
Коммит
968ba626cc
|
@ -237,16 +237,15 @@ private:
|
||||||
DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
|
DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
|
||||||
DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
|
DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
|
||||||
DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
|
DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
|
||||||
DECL_GFX_PREF(Live, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
|
DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
|
||||||
DECL_GFX_PREF(Live, "image.mem.decodeondraw", ImageMemDecodeOnDraw, bool, false);
|
DECL_GFX_PREF(Live, "image.mem.decodeondraw", ImageMemDecodeOnDraw, bool, false);
|
||||||
DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
|
DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
|
||||||
DECL_GFX_PREF(Live, "image.mem.max_ms_before_yield", ImageMemMaxMSBeforeYield, uint32_t, 400);
|
|
||||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
|
DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
|
||||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
|
DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
|
||||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
|
DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
|
||||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor", ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
|
DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor", ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
|
||||||
DECL_GFX_PREF(Live, "image.mozsamplesize.enabled", ImageMozSampleSizeEnabled, bool, false);
|
DECL_GFX_PREF(Live, "image.mozsamplesize.enabled", ImageMozSampleSizeEnabled, bool, false);
|
||||||
DECL_GFX_PREF(Live, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
|
DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
|
||||||
|
|
||||||
DECL_GFX_PREF(Once, "layers.acceleration.disabled", LayersAccelerationDisabled, bool, false);
|
DECL_GFX_PREF(Once, "layers.acceleration.disabled", LayersAccelerationDisabled, bool, false);
|
||||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);
|
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "nsIThreadPool.h"
|
#include "nsIThreadPool.h"
|
||||||
#include "nsProxyRelease.h"
|
|
||||||
#include "nsXPCOMCIDInternal.h"
|
#include "nsXPCOMCIDInternal.h"
|
||||||
#include "prsystem.h"
|
#include "prsystem.h"
|
||||||
|
|
||||||
|
@ -42,102 +41,88 @@ public:
|
||||||
* Called by the DecodePool when it's done some significant portion of
|
* Called by the DecodePool when it's done some significant portion of
|
||||||
* decoding, so that progress can be recorded and notifications can be sent.
|
* decoding, so that progress can be recorded and notifications can be sent.
|
||||||
*/
|
*/
|
||||||
static void Dispatch(RasterImage* aImage)
|
static void Dispatch(RasterImage* aImage,
|
||||||
|
Progress aProgress,
|
||||||
|
const nsIntRect& aInvalidRect,
|
||||||
|
uint32_t aFlags)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIRunnable> worker = new NotifyProgressWorker(aImage);
|
MOZ_ASSERT(aImage);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> worker =
|
||||||
|
new NotifyProgressWorker(aImage, aProgress, aInvalidRect, aFlags);
|
||||||
NS_DispatchToMainThread(worker);
|
NS_DispatchToMainThread(worker);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
mImage->NotifyProgress(mProgress, mInvalidRect, mFlags);
|
||||||
|
|
||||||
mImage->FinishedSomeDecoding(ShutdownReason::DONE);
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit NotifyProgressWorker(RasterImage* aImage)
|
NotifyProgressWorker(RasterImage* aImage, Progress aProgress,
|
||||||
|
const nsIntRect& aInvalidRect, uint32_t aFlags)
|
||||||
: mImage(aImage)
|
: mImage(aImage)
|
||||||
|
, mProgress(aProgress)
|
||||||
|
, mInvalidRect(aInvalidRect)
|
||||||
|
, mFlags(aFlags)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
nsRefPtr<RasterImage> mImage;
|
nsRefPtr<RasterImage> mImage;
|
||||||
|
const Progress mProgress;
|
||||||
|
const nsIntRect mInvalidRect;
|
||||||
|
const uint32_t mFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NotifyDecodeCompleteWorker : public nsRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Called by the DecodePool when decoding is complete, so that final cleanup
|
||||||
|
* can be performed.
|
||||||
|
*/
|
||||||
|
static void Dispatch(Decoder* aDecoder)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aDecoder);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> worker = new NotifyDecodeCompleteWorker(aDecoder);
|
||||||
|
NS_DispatchToMainThread(worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
DecodePool::Singleton()->NotifyDecodeComplete(mDecoder);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit NotifyDecodeCompleteWorker(Decoder* aDecoder)
|
||||||
|
: mDecoder(aDecoder)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
nsRefPtr<Decoder> mDecoder;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DecodeWorker : public nsRunnable
|
class DecodeWorker : public nsRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit DecodeWorker(RasterImage* aImage)
|
explicit DecodeWorker(Decoder* aDecoder)
|
||||||
: mImage(aImage)
|
: mDecoder(aDecoder)
|
||||||
{ }
|
{
|
||||||
|
MOZ_ASSERT(mDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
|
DecodePool::Singleton()->Decode(mDecoder);
|
||||||
ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
|
|
||||||
|
|
||||||
// If we were interrupted, we shouldn't do any work.
|
|
||||||
if (mImage->mDecodeStatus == DecodeStatus::STOPPED) {
|
|
||||||
NotifyProgressWorker::Dispatch(mImage);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If someone came along and synchronously decoded us, there's nothing for us to do.
|
|
||||||
if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
|
|
||||||
NotifyProgressWorker::Dispatch(mImage);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
mImage->mDecodeStatus = DecodeStatus::ACTIVE;
|
|
||||||
|
|
||||||
size_t oldByteCount = mImage->mDecoder->BytesDecoded();
|
|
||||||
|
|
||||||
size_t maxBytes = mImage->mSourceData.Length() -
|
|
||||||
mImage->mDecoder->BytesDecoded();
|
|
||||||
DecodePool::Singleton()->DecodeSomeOfImage(mImage, DecodeUntil::DONE_BYTES,
|
|
||||||
maxBytes);
|
|
||||||
|
|
||||||
size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
|
|
||||||
|
|
||||||
mImage->mDecodeStatus = DecodeStatus::WORK_DONE;
|
|
||||||
|
|
||||||
if (mImage->mDecoder &&
|
|
||||||
!mImage->mError &&
|
|
||||||
!mImage->mPendingError &&
|
|
||||||
!mImage->IsDecodeFinished() &&
|
|
||||||
bytesDecoded < maxBytes &&
|
|
||||||
bytesDecoded > 0) {
|
|
||||||
// We aren't finished decoding, and we have more data, so add this request
|
|
||||||
// to the back of the list.
|
|
||||||
DecodePool::Singleton()->RequestDecode(mImage);
|
|
||||||
} else {
|
|
||||||
// Nothing more for us to do - let everyone know what happened.
|
|
||||||
NotifyProgressWorker::Dispatch(mImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~DecodeWorker()
|
|
||||||
{
|
|
||||||
// Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
|
|
||||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
|
||||||
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
|
|
||||||
if (mainThread) {
|
|
||||||
// Handle ambiguous nsISupports inheritance
|
|
||||||
RasterImage* rawImg = nullptr;
|
|
||||||
mImage.swap(rawImg);
|
|
||||||
DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
|
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsRefPtr<RasterImage> mImage;
|
nsRefPtr<Decoder> mDecoder;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef MOZ_NUWA_PROCESS
|
#ifdef MOZ_NUWA_PROCESS
|
||||||
|
@ -194,13 +179,6 @@ DecodePool::Singleton()
|
||||||
return sSingleton;
|
return sSingleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<nsIEventTarget>
|
|
||||||
DecodePool::GetEventTarget()
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
|
|
||||||
return target.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
DecodePool::DecodePool()
|
DecodePool::DecodePool()
|
||||||
: mThreadPoolMutex("Thread Pool")
|
: mThreadPoolMutex("Thread Pool")
|
||||||
{
|
{
|
||||||
|
@ -258,20 +236,11 @@ DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
DecodePool::RequestDecode(RasterImage* aImage)
|
DecodePool::AsyncDecode(Decoder* aDecoder)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aImage->mDecoder);
|
MOZ_ASSERT(aDecoder);
|
||||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
|
||||||
|
|
||||||
if (aImage->mDecodeStatus == DecodeStatus::PENDING ||
|
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
|
||||||
aImage->mDecodeStatus == DecodeStatus::ACTIVE) {
|
|
||||||
// The image is already in our list of images to decode, or currently being
|
|
||||||
// decoded, so we don't have to do anything else.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
aImage->mDecodeStatus = DecodeStatus::PENDING;
|
|
||||||
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aImage);
|
|
||||||
|
|
||||||
// Dispatch to the thread pool if it exists. If it doesn't, we're currently
|
// Dispatch to the thread pool if it exists. If it doesn't, we're currently
|
||||||
// shutting down, so it's OK to just drop the job on the floor.
|
// shutting down, so it's OK to just drop the job on the floor.
|
||||||
|
@ -282,136 +251,90 @@ DecodePool::RequestDecode(RasterImage* aImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
DecodePool::DecodeABitOf(RasterImage* aImage)
|
DecodePool::SyncDecodeIfSmall(Decoder* aDecoder)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
MOZ_ASSERT(aDecoder);
|
||||||
|
|
||||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
if (aDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime())) {
|
||||||
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
Decode(aDecoder);
|
||||||
aImage->FinishedSomeDecoding();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecodeSomeOfImage(aImage);
|
AsyncDecode(aDecoder);
|
||||||
|
|
||||||
aImage->FinishedSomeDecoding();
|
|
||||||
|
|
||||||
// If we aren't yet finished decoding and we have more data in hand, add
|
|
||||||
// this request to the back of the priority list.
|
|
||||||
if (aImage->mDecoder &&
|
|
||||||
!aImage->mError &&
|
|
||||||
!aImage->IsDecodeFinished() &&
|
|
||||||
aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded()) {
|
|
||||||
RequestDecode(aImage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
void
|
||||||
DecodePool::StopDecoding(RasterImage* aImage)
|
DecodePool::SyncDecodeIfPossible(Decoder* aDecoder)
|
||||||
{
|
|
||||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
|
||||||
|
|
||||||
// If we haven't got a decode request, we're not currently decoding. (Having
|
|
||||||
// a decode request doesn't imply we *are* decoding, though.)
|
|
||||||
aImage->mDecodeStatus = DecodeStatus::STOPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
DecodePool::DecodeUntilSizeAvailable(RasterImage* aImage)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
ReentrantMonitorAutoEnter lock(aImage->mDecodingMonitor);
|
Decode(aDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
// If the image is waiting for decode work to be notified, go ahead and do that.
|
already_AddRefed<nsIEventTarget>
|
||||||
if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
|
DecodePool::GetEventTarget()
|
||||||
nsresult rv = aImage->FinishedSomeDecoding();
|
{
|
||||||
if (NS_FAILED(rv)) {
|
MutexAutoLock threadPoolLock(mThreadPoolMutex);
|
||||||
aImage->DoError();
|
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
|
||||||
return rv;
|
return target.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<nsIRunnable>
|
||||||
|
DecodePool::CreateDecodeWorker(Decoder* aDecoder)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aDecoder);
|
||||||
|
nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
|
||||||
|
return worker.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DecodePool::Decode(Decoder* aDecoder)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aDecoder);
|
||||||
|
|
||||||
|
nsresult rv = aDecoder->Decode();
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(rv) && !aDecoder->GetDecodeDone()) {
|
||||||
|
if (aDecoder->HasProgress()) {
|
||||||
|
NotifyProgress(aDecoder);
|
||||||
}
|
}
|
||||||
}
|
// The decoder will ensure that a new worker gets enqueued to continue
|
||||||
|
// decoding when more data is available.
|
||||||
nsresult rv = DecodeSomeOfImage(aImage, DecodeUntil::SIZE);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return aImage->FinishedSomeDecoding();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
DecodePool::DecodeSomeOfImage(RasterImage* aImage,
|
|
||||||
DecodeUntil aDecodeUntil /* = DecodeUntil::TIME */,
|
|
||||||
uint32_t bytesToDecode /* = 0 */)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(aImage->mInitialized, "Worker active for uninitialized container");
|
|
||||||
aImage->mDecodingMonitor.AssertCurrentThreadIn();
|
|
||||||
|
|
||||||
// If an error is flagged, it probably happened while we were waiting
|
|
||||||
// in the event queue.
|
|
||||||
if (aImage->mError) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is an error worker pending (say because the main thread has enqueued
|
|
||||||
// another decode request for us before processing the error worker) then bail out.
|
|
||||||
if (aImage->mPendingError) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If mDecoded or we don't have a decoder, we must have finished already (for
|
|
||||||
// example, a synchronous decode request came while the worker was pending).
|
|
||||||
if (!aImage->mDecoder || aImage->mDecoded) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsRefPtr<Decoder> decoderKungFuDeathGrip = aImage->mDecoder;
|
|
||||||
|
|
||||||
uint32_t maxBytes;
|
|
||||||
if (aImage->mDecoder->IsSizeDecode()) {
|
|
||||||
// Decode all available data if we're a size decode; they're cheap, and we
|
|
||||||
// want them to be more or less synchronous.
|
|
||||||
maxBytes = aImage->mSourceData.Length();
|
|
||||||
} else {
|
} else {
|
||||||
// We're only guaranteed to decode this many bytes, so in particular,
|
NotifyDecodeComplete(aDecoder);
|
||||||
// gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
|
}
|
||||||
// to read the size from most images.
|
}
|
||||||
maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
|
|
||||||
|
void
|
||||||
|
DecodePool::NotifyProgress(Decoder* aDecoder)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aDecoder);
|
||||||
|
|
||||||
|
if (!NS_IsMainThread()) {
|
||||||
|
NotifyProgressWorker::Dispatch(aDecoder->GetImage(),
|
||||||
|
aDecoder->TakeProgress(),
|
||||||
|
aDecoder->TakeInvalidRect(),
|
||||||
|
aDecoder->GetDecodeFlags());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytesToDecode == 0) {
|
aDecoder->GetImage()->NotifyProgress(aDecoder->TakeProgress(),
|
||||||
bytesToDecode = aImage->mSourceData.Length() - aImage->mDecoder->BytesDecoded();
|
aDecoder->TakeInvalidRect(),
|
||||||
|
aDecoder->GetDecodeFlags());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DecodePool::NotifyDecodeComplete(Decoder* aDecoder)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aDecoder);
|
||||||
|
|
||||||
|
if (!NS_IsMainThread()) {
|
||||||
|
NotifyDecodeCompleteWorker::Dispatch(aDecoder);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeStamp deadline = TimeStamp::Now() +
|
aDecoder->Finish();
|
||||||
TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
|
aDecoder->GetImage()->FinalizeDecoder(aDecoder);
|
||||||
|
|
||||||
// We keep decoding chunks until:
|
|
||||||
// * we don't have any data left to decode,
|
|
||||||
// * the decode completes,
|
|
||||||
// * we're an DecodeUntil::SIZE decode and we get the size, or
|
|
||||||
// * we run out of time.
|
|
||||||
while (aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded() &&
|
|
||||||
bytesToDecode > 0 &&
|
|
||||||
!aImage->IsDecodeFinished() &&
|
|
||||||
!(aDecodeUntil == DecodeUntil::SIZE && aImage->mHasSize)) {
|
|
||||||
uint32_t chunkSize = min(bytesToDecode, maxBytes);
|
|
||||||
nsresult rv = aImage->DecodeSomeData(chunkSize);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
aImage->DoError();
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesToDecode -= chunkSize;
|
|
||||||
|
|
||||||
// Yield if we've been decoding for too long. We check this _after_ decoding
|
|
||||||
// a chunk to ensure that we don't yield without doing any decoding.
|
|
||||||
if (aDecodeUntil == DecodeUntil::TIME && TimeStamp::Now() >= deadline) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
|
|
@ -25,39 +25,15 @@ namespace image {
|
||||||
class Decoder;
|
class Decoder;
|
||||||
class RasterImage;
|
class RasterImage;
|
||||||
|
|
||||||
MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t)
|
|
||||||
INACTIVE,
|
|
||||||
PENDING,
|
|
||||||
ACTIVE,
|
|
||||||
WORK_DONE,
|
|
||||||
STOPPED
|
|
||||||
MOZ_END_ENUM_CLASS(DecodeStatus)
|
|
||||||
|
|
||||||
MOZ_BEGIN_ENUM_CLASS(DecodeUntil, uint8_t)
|
|
||||||
TIME,
|
|
||||||
SIZE,
|
|
||||||
DONE_BYTES
|
|
||||||
MOZ_END_ENUM_CLASS(DecodeUntil)
|
|
||||||
|
|
||||||
MOZ_BEGIN_ENUM_CLASS(ShutdownReason, uint8_t)
|
|
||||||
DONE,
|
|
||||||
NOT_NEEDED,
|
|
||||||
FATAL_ERROR
|
|
||||||
MOZ_END_ENUM_CLASS(ShutdownReason)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DecodePool is a singleton class we use when decoding large images.
|
* DecodePool is a singleton class that manages decoding of raster images. It
|
||||||
|
* owns a pool of image decoding threads that are used for asynchronous
|
||||||
|
* decoding.
|
||||||
*
|
*
|
||||||
* When we wish to decode an image larger than
|
* DecodePool allows callers to run a decoder, handling management of the
|
||||||
* image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
|
* decoder's lifecycle and whether it executes on the main thread,
|
||||||
* for the image. This adds the image to a queue of pending requests and posts
|
* off-main-thread in the image decoding thread pool, or on some combination of
|
||||||
* the DecodePool singleton to the event queue, if it's not already pending
|
* the two.
|
||||||
* there.
|
|
||||||
*
|
|
||||||
* When the DecodePool is run from the event queue, it decodes the image (and
|
|
||||||
* all others it's managing) in chunks, periodically yielding control back to
|
|
||||||
* the event loop.
|
|
||||||
*/
|
*/
|
||||||
class DecodePool : public nsIObserver
|
class DecodePool : public nsIObserver
|
||||||
{
|
{
|
||||||
|
@ -67,58 +43,52 @@ public:
|
||||||
|
|
||||||
static DecodePool* Singleton();
|
static DecodePool* Singleton();
|
||||||
|
|
||||||
/**
|
/// Ask the DecodePool to run @aDecoder asynchronously and return immediately.
|
||||||
* Ask the DecodePool to asynchronously decode this image.
|
void AsyncDecode(Decoder* aDecoder);
|
||||||
*/
|
|
||||||
void RequestDecode(RasterImage* aImage);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode aImage for a short amount of time, and post the remainder to the
|
* Run @aDecoder synchronously if the image it's decoding is small. If the
|
||||||
* queue.
|
* image is too large, or if the source data isn't complete yet, run @aDecoder
|
||||||
|
* asynchronously instead.
|
||||||
*/
|
*/
|
||||||
void DecodeABitOf(RasterImage* aImage);
|
void SyncDecodeIfSmall(Decoder* aDecoder);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ask the DecodePool to stop decoding this image. Internally, we also
|
* Run aDecoder synchronously if at all possible. If it can't complete
|
||||||
* call this function when we finish decoding an image.
|
* synchronously because the source data isn't complete, asynchronously decode
|
||||||
|
* the rest.
|
||||||
|
*/
|
||||||
|
void SyncDecodeIfPossible(Decoder* aDecoder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an event target interface to the DecodePool's underlying thread
|
||||||
|
* pool. Callers can use this event target to submit work to the image
|
||||||
|
* decoding thread pool.
|
||||||
*
|
*
|
||||||
* Since the DecodePool keeps raw pointers to RasterImages, make sure you
|
* @return An nsIEventTarget interface to the thread pool.
|
||||||
* call this before a RasterImage is destroyed!
|
|
||||||
*/
|
|
||||||
static void StopDecoding(RasterImage* aImage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronously decode the beginning of the image until we run out of
|
|
||||||
* bytes or we get the image's size. Note that this done on a best-effort
|
|
||||||
* basis; if the size is burried too deep in the image, we'll give up.
|
|
||||||
*
|
|
||||||
* @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note
|
|
||||||
* that we return NS_OK even when the size was not found.)
|
|
||||||
*/
|
|
||||||
nsresult DecodeUntilSizeAvailable(RasterImage* aImage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an event target interface to the thread pool; primarily for
|
|
||||||
* OnDataAvailable delivery off main thread.
|
|
||||||
*
|
|
||||||
* @return An nsIEventTarget interface to mThreadPool.
|
|
||||||
*/
|
*/
|
||||||
already_AddRefed<nsIEventTarget> GetEventTarget();
|
already_AddRefed<nsIEventTarget> GetEventTarget();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode some chunks of the given image. If aDecodeUntil is SIZE,
|
* Creates a worker which can be used to attempt further decoding using the
|
||||||
* decode until we have the image's size, then stop. If bytesToDecode is
|
* provided decoder.
|
||||||
* non-0, at most bytesToDecode bytes will be decoded. if aDecodeUntil is
|
*
|
||||||
* DONE_BYTES, decode until all bytesToDecode bytes are decoded.
|
* @return The new worker, which should be posted to the event target returned
|
||||||
|
* by GetEventTarget.
|
||||||
*/
|
*/
|
||||||
nsresult DecodeSomeOfImage(RasterImage* aImage,
|
already_AddRefed<nsIRunnable> CreateDecodeWorker(Decoder* aDecoder);
|
||||||
DecodeUntil aDecodeUntil = DecodeUntil::TIME,
|
|
||||||
uint32_t bytesToDecode = 0);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class DecodeWorker;
|
||||||
|
friend class NotifyDecodeCompleteWorker;
|
||||||
|
|
||||||
DecodePool();
|
DecodePool();
|
||||||
virtual ~DecodePool();
|
virtual ~DecodePool();
|
||||||
|
|
||||||
|
void Decode(Decoder* aDecoder);
|
||||||
|
void NotifyDecodeComplete(Decoder* aDecoder);
|
||||||
|
void NotifyProgress(Decoder* aDecoder);
|
||||||
|
|
||||||
static StaticRefPtr<DecodePool> sSingleton;
|
static StaticRefPtr<DecodePool> sSingleton;
|
||||||
|
|
||||||
// mThreadPoolMutex protects mThreadPool. For all RasterImages R,
|
// mThreadPoolMutex protects mThreadPool. For all RasterImages R,
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
#include "Decoder.h"
|
#include "Decoder.h"
|
||||||
|
|
||||||
#include "mozilla/gfx/2D.h"
|
#include "mozilla/gfx/2D.h"
|
||||||
|
#include "DecodePool.h"
|
||||||
|
#include "GeckoProfiler.h"
|
||||||
#include "imgIContainer.h"
|
#include "imgIContainer.h"
|
||||||
#include "nsIConsoleService.h"
|
#include "nsIConsoleService.h"
|
||||||
#include "nsIScriptError.h"
|
#include "nsIScriptError.h"
|
||||||
#include "GeckoProfiler.h"
|
#include "nsProxyRelease.h"
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "nsComponentManagerUtils.h"
|
#include "nsComponentManagerUtils.h"
|
||||||
|
|
||||||
|
@ -29,8 +31,11 @@ Decoder::Decoder(RasterImage* aImage)
|
||||||
, mDecodeFlags(0)
|
, mDecodeFlags(0)
|
||||||
, mBytesDecoded(0)
|
, mBytesDecoded(0)
|
||||||
, mSendPartialInvalidations(false)
|
, mSendPartialInvalidations(false)
|
||||||
|
, mDataDone(false)
|
||||||
, mDecodeDone(false)
|
, mDecodeDone(false)
|
||||||
, mDataError(false)
|
, mDataError(false)
|
||||||
|
, mDecodeAborted(false)
|
||||||
|
, mImageIsTransient(false)
|
||||||
, mFrameCount(0)
|
, mFrameCount(0)
|
||||||
, mFailCode(NS_OK)
|
, mFailCode(NS_OK)
|
||||||
, mNeedsNewFrame(false)
|
, mNeedsNewFrame(false)
|
||||||
|
@ -73,7 +78,7 @@ void
|
||||||
Decoder::Init()
|
Decoder::Init()
|
||||||
{
|
{
|
||||||
// No re-initializing
|
// No re-initializing
|
||||||
NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
|
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
|
||||||
|
|
||||||
// Fire OnStartDecode at init time to support bug 512435.
|
// Fire OnStartDecode at init time to support bug 512435.
|
||||||
if (!IsSizeDecode()) {
|
if (!IsSizeDecode()) {
|
||||||
|
@ -113,6 +118,69 @@ Decoder::InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength,
|
||||||
mInitialized = true;
|
mInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
Decoder::Decode()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mInitialized, "Should be initialized here");
|
||||||
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
||||||
|
|
||||||
|
// We keep decoding chunks until the decode completes or there are no more
|
||||||
|
// chunks available.
|
||||||
|
while (!GetDecodeDone() && !HasError()) {
|
||||||
|
auto newState = mIterator->AdvanceOrScheduleResume(this);
|
||||||
|
|
||||||
|
if (newState == SourceBufferIterator::WAITING) {
|
||||||
|
// We can't continue because the rest of the data hasn't arrived from the
|
||||||
|
// network yet. We don't have to do anything special; the
|
||||||
|
// SourceBufferIterator will ensure that Decode() gets called again on a
|
||||||
|
// DecodePool thread when more data is available.
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState == SourceBufferIterator::COMPLETE) {
|
||||||
|
mDataDone = true;
|
||||||
|
|
||||||
|
nsresult finalStatus = mIterator->CompletionStatus();
|
||||||
|
if (NS_FAILED(finalStatus)) {
|
||||||
|
PostDataError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(newState == SourceBufferIterator::READY);
|
||||||
|
|
||||||
|
Write(mIterator->Data(), mIterator->Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
return HasError() ? NS_ERROR_FAILURE : NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Decoder::Resume()
|
||||||
|
{
|
||||||
|
DecodePool* decodePool = DecodePool::Singleton();
|
||||||
|
MOZ_ASSERT(decodePool);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIEventTarget> target = decodePool->GetEventTarget();
|
||||||
|
if (MOZ_UNLIKELY(!target)) {
|
||||||
|
// We're shutting down and the DecodePool's thread pool has been destroyed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> worker = decodePool->CreateDecodeWorker(this);
|
||||||
|
target->Dispatch(worker, nsIEventTarget::DISPATCH_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Decoder::ShouldSyncDecode(size_t aByteLimit)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aByteLimit > 0);
|
||||||
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
||||||
|
|
||||||
|
return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Decoder::Write(const char* aBuffer, uint32_t aCount)
|
Decoder::Write(const char* aBuffer, uint32_t aCount)
|
||||||
{
|
{
|
||||||
|
@ -172,7 +240,7 @@ Decoder::Write(const char* aBuffer, uint32_t aCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Decoder::Finish(ShutdownReason aReason)
|
Decoder::Finish()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
@ -184,9 +252,10 @@ Decoder::Finish(ShutdownReason aReason)
|
||||||
if (mInFrame && !HasError())
|
if (mInFrame && !HasError())
|
||||||
PostFrameStop();
|
PostFrameStop();
|
||||||
|
|
||||||
// If PostDecodeDone() has not been called, we need to sent teardown
|
// If PostDecodeDone() has not been called, and this decoder wasn't aborted
|
||||||
// notifications.
|
// early because of low-memory conditions or losing a race with another
|
||||||
if (!IsSizeDecode() && !mDecodeDone) {
|
// decoder, we need to send teardown notifications.
|
||||||
|
if (!IsSizeDecode() && !mDecodeDone && !WasAborted()) {
|
||||||
|
|
||||||
// Log data errors to the error console
|
// Log data errors to the error console
|
||||||
nsCOMPtr<nsIConsoleService> consoleService =
|
nsCOMPtr<nsIConsoleService> consoleService =
|
||||||
|
@ -208,22 +277,17 @@ Decoder::Finish(ShutdownReason aReason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool usable = !HasDecoderError();
|
// If we only have a data error, we're usable if we have at least one
|
||||||
if (aReason != ShutdownReason::NOT_NEEDED && !HasDecoderError()) {
|
// complete frame.
|
||||||
// If we only have a data error, we're usable if we have at least one complete frame.
|
if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
|
||||||
if (GetCompleteFrameCount() == 0) {
|
// We're usable, so do exactly what we should have when the decoder
|
||||||
usable = false;
|
// completed.
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're usable, do exactly what we should have when the decoder
|
|
||||||
// completed.
|
|
||||||
if (usable) {
|
|
||||||
if (mInFrame) {
|
if (mInFrame) {
|
||||||
PostFrameStop();
|
PostFrameStop();
|
||||||
}
|
}
|
||||||
PostDecodeDone();
|
PostDecodeDone();
|
||||||
} else {
|
} else {
|
||||||
|
// We're not usable. Record some final progress indicating the error.
|
||||||
if (!IsSizeDecode()) {
|
if (!IsSizeDecode()) {
|
||||||
mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
|
mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
|
||||||
}
|
}
|
||||||
|
@ -235,9 +299,21 @@ Decoder::Finish(ShutdownReason aReason)
|
||||||
// DecodingComplete calls Optimize().
|
// DecodingComplete calls Optimize().
|
||||||
mImageMetadata.SetOnImage(mImage);
|
mImageMetadata.SetOnImage(mImage);
|
||||||
|
|
||||||
if (mDecodeDone) {
|
if (HasSize()) {
|
||||||
|
SetSizeOnImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDecodeDone && !IsSizeDecode()) {
|
||||||
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
|
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
|
||||||
mImage->DecodingComplete(mCurrentFrame.get());
|
|
||||||
|
// If this image wasn't animated and isn't a transient image, mark its frame
|
||||||
|
// as optimizable. We don't support optimizing animated images and
|
||||||
|
// optimizing transient images isn't worth it.
|
||||||
|
if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) {
|
||||||
|
mCurrentFrame->SetOptimizable();
|
||||||
|
}
|
||||||
|
|
||||||
|
mImage->OnDecodingComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,6 +466,12 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
|
||||||
aFrameNum),
|
aFrameNum),
|
||||||
Lifetime::Persistent);
|
Lifetime::Persistent);
|
||||||
if (outcome != InsertOutcome::SUCCESS) {
|
if (outcome != InsertOutcome::SUCCESS) {
|
||||||
|
// We either hit InsertOutcome::FAILURE, which is a temporary failure due to
|
||||||
|
// low memory (we know it's not permanent because we checked CanHold()
|
||||||
|
// above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that
|
||||||
|
// another decoder beat us to decoding this frame. Either way, we should
|
||||||
|
// abort this decoder rather than treat this as a real error.
|
||||||
|
mDecodeAborted = true;
|
||||||
ref->Abort();
|
ref->Abort();
|
||||||
return RawAccessFrameRef();
|
return RawAccessFrameRef();
|
||||||
}
|
}
|
||||||
|
@ -431,9 +513,12 @@ Decoder::SetSizeOnImage()
|
||||||
MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
|
MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
|
||||||
MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
|
MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
|
||||||
|
|
||||||
mImage->SetSize(mImageMetadata.GetWidth(),
|
nsresult rv = mImage->SetSize(mImageMetadata.GetWidth(),
|
||||||
mImageMetadata.GetHeight(),
|
mImageMetadata.GetHeight(),
|
||||||
mImageMetadata.GetOrientation());
|
mImageMetadata.GetOrientation());
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
PostResizeError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -12,13 +12,14 @@
|
||||||
#include "DecodePool.h"
|
#include "DecodePool.h"
|
||||||
#include "ImageMetadata.h"
|
#include "ImageMetadata.h"
|
||||||
#include "Orientation.h"
|
#include "Orientation.h"
|
||||||
|
#include "SourceBuffer.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
namespace image {
|
namespace image {
|
||||||
|
|
||||||
class Decoder
|
class Decoder : public IResumable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -26,8 +27,6 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize an image decoder. Decoders may not be re-initialized.
|
* Initialize an image decoder. Decoders may not be re-initialized.
|
||||||
*
|
|
||||||
* Notifications Sent: TODO
|
|
||||||
*/
|
*/
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
@ -42,28 +41,30 @@ public:
|
||||||
RawAccessFrameRef&& aFrameRef);
|
RawAccessFrameRef&& aFrameRef);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes data to the decoder.
|
* Decodes, reading all data currently available in the SourceBuffer. If more
|
||||||
*
|
|
||||||
* If aBuffer is null and aCount is 0, Write() flushes any buffered data to
|
* 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
|
* 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,
|
* decode it because it needed a new frame. If it's necessary to flush data,
|
||||||
* NeedsToFlushData() will return true.
|
* NeedsToFlushData() will return true.
|
||||||
*
|
*
|
||||||
* @param aBuffer buffer containing the data to be written
|
* data is needed, Decode() automatically ensures that it will be called again
|
||||||
* @param aCount the number of bytes to write
|
* on a DecodePool thread when the data becomes available.
|
||||||
*
|
*
|
||||||
* Any errors are reported by setting the appropriate state on the decoder.
|
* Any errors are reported by setting the appropriate state on the decoder.
|
||||||
*
|
|
||||||
* Notifications Sent: TODO
|
|
||||||
*/
|
*/
|
||||||
void Write(const char* aBuffer, uint32_t aCount);
|
nsresult Decode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the decoder that all the data has been written.
|
* Cleans up the decoder's state and notifies our image about success or
|
||||||
*
|
* failure. May only be called on the main thread.
|
||||||
* Notifications Sent: TODO
|
|
||||||
*/
|
*/
|
||||||
void Finish(ShutdownReason aReason);
|
void Finish();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a maximum number of bytes we're willing to decode, @aByteLimit,
|
||||||
|
* returns true if we should attempt to run this decoder synchronously.
|
||||||
|
*/
|
||||||
|
bool ShouldSyncDecode(size_t aByteLimit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the shared decoder that all the data has been written.
|
* Informs the shared decoder that all the data has been written.
|
||||||
|
@ -98,9 +99,20 @@ public:
|
||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there's any progress to report.
|
||||||
|
*/
|
||||||
|
bool HasProgress() const
|
||||||
|
{
|
||||||
|
return mProgress != NoProgress || !mInvalidRect.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
// 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_THREADSAFE_REFCOUNTING(Decoder)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
|
||||||
|
|
||||||
|
// Implement IResumable.
|
||||||
|
virtual void Resume() MOZ_OVERRIDE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* State.
|
* State.
|
||||||
*/
|
*/
|
||||||
|
@ -111,7 +123,7 @@ public:
|
||||||
bool IsSizeDecode() { return mSizeDecode; }
|
bool IsSizeDecode() { return mSizeDecode; }
|
||||||
void SetSizeDecode(bool aSizeDecode)
|
void SetSizeDecode(bool aSizeDecode)
|
||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(!mInitialized, "Can't set size decode after Init()!");
|
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
|
||||||
mSizeDecode = aSizeDecode;
|
mSizeDecode = aSizeDecode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +145,32 @@ public:
|
||||||
mSendPartialInvalidations = aSend;
|
mSendPartialInvalidations = aSend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an iterator to the SourceBuffer which will feed data to this decoder.
|
||||||
|
*
|
||||||
|
* This should be called for almost all decoders; the exceptions are the
|
||||||
|
* contained decoders of an nsICODecoder, which will be fed manually via Write
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* This must be called before Init() is called.
|
||||||
|
*/
|
||||||
|
void SetIterator(SourceBufferIterator&& aIterator)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
|
||||||
|
mIterator.emplace(Move(aIterator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this decoder is associated with a transient image. The decoder
|
||||||
|
* may choose to avoid certain optimizations that don't pay off for
|
||||||
|
* short-lived images in this case.
|
||||||
|
*/
|
||||||
|
void SetImageIsTransient(bool aIsTransient)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
|
||||||
|
mImageIsTransient = aIsTransient;
|
||||||
|
}
|
||||||
|
|
||||||
size_t BytesDecoded() const { return mBytesDecoded; }
|
size_t BytesDecoded() const { return mBytesDecoded; }
|
||||||
|
|
||||||
// The amount of time we've spent inside Write() so far for this decoder.
|
// The amount of time we've spent inside Write() so far for this decoder.
|
||||||
|
@ -149,15 +187,28 @@ public:
|
||||||
uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; }
|
uint32_t GetCompleteFrameCount() { return mInFrame ? mFrameCount - 1 : mFrameCount; }
|
||||||
|
|
||||||
// Error tracking
|
// Error tracking
|
||||||
bool HasError() { return HasDataError() || HasDecoderError(); }
|
bool HasError() const { return HasDataError() || HasDecoderError(); }
|
||||||
bool HasDataError() { return mDataError; }
|
bool HasDataError() const { return mDataError; }
|
||||||
bool HasDecoderError() { return NS_FAILED(mFailCode); }
|
bool HasDecoderError() const { return NS_FAILED(mFailCode); }
|
||||||
nsresult GetDecoderError() { return mFailCode; }
|
nsresult GetDecoderError() const { return mFailCode; }
|
||||||
void PostResizeError() { PostDataError(); }
|
void PostResizeError() { PostDataError(); }
|
||||||
bool GetDecodeDone() const {
|
|
||||||
return mDecodeDone;
|
bool GetDecodeDone() const
|
||||||
|
{
|
||||||
|
return mDecodeDone || (mSizeDecode && HasSize()) || HasError() || mDataDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this decoder was aborted.
|
||||||
|
*
|
||||||
|
* This may happen due to a low-memory condition, or because another decoder
|
||||||
|
* was racing with this one to decode the same frames with the same flags and
|
||||||
|
* this decoder lost the race. Either way, this is not a permanent situation
|
||||||
|
* and does not constitute an error, so we don't report any errors when this
|
||||||
|
* happens.
|
||||||
|
*/
|
||||||
|
bool WasAborted() const { return mDecodeAborted; }
|
||||||
|
|
||||||
// flags. Keep these in sync with imgIContainer.idl.
|
// flags. Keep these in sync with imgIContainer.idl.
|
||||||
// SetDecodeFlags must be called before Init(), otherwise
|
// SetDecodeFlags must be called before Init(), otherwise
|
||||||
// default flags are assumed.
|
// default flags are assumed.
|
||||||
|
@ -313,11 +364,22 @@ protected:
|
||||||
gfx::SurfaceFormat aFormat,
|
gfx::SurfaceFormat aFormat,
|
||||||
uint8_t aPaletteDepth,
|
uint8_t aPaletteDepth,
|
||||||
imgFrame* aPreviousFrame);
|
imgFrame* aPreviousFrame);
|
||||||
|
/**
|
||||||
|
* Writes data to the decoder.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
void Write(const char* aBuffer, uint32_t aCount);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Member variables.
|
* Member variables.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
nsRefPtr<RasterImage> mImage;
|
nsRefPtr<RasterImage> mImage;
|
||||||
|
Maybe<SourceBufferIterator> mIterator;
|
||||||
RawAccessFrameRef mCurrentFrame;
|
RawAccessFrameRef mCurrentFrame;
|
||||||
ImageMetadata mImageMetadata;
|
ImageMetadata mImageMetadata;
|
||||||
nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
|
nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
|
||||||
|
@ -335,8 +397,11 @@ protected:
|
||||||
uint32_t mDecodeFlags;
|
uint32_t mDecodeFlags;
|
||||||
size_t mBytesDecoded;
|
size_t mBytesDecoded;
|
||||||
bool mSendPartialInvalidations;
|
bool mSendPartialInvalidations;
|
||||||
|
bool mDataDone;
|
||||||
bool mDecodeDone;
|
bool mDecodeDone;
|
||||||
bool mDataError;
|
bool mDataError;
|
||||||
|
bool mDecodeAborted;
|
||||||
|
bool mImageIsTransient;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t mFrameCount; // Number of frames, including anything in-progress
|
uint32_t mFrameCount; // Number of frames, including anything in-progress
|
||||||
|
|
|
@ -290,6 +290,11 @@ int32_t
|
||||||
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
|
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
|
||||||
{
|
{
|
||||||
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
|
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
|
||||||
|
if (!frame) {
|
||||||
|
NS_WARNING("No frame; called GetTimeoutForFrame too early?");
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
AnimationData data = frame->GetAnimationData();
|
AnimationData data = frame->GetAnimationData();
|
||||||
|
|
||||||
// Ensure a minimal time between updates so we don't throttle the UI thread.
|
// Ensure a minimal time between updates so we don't throttle the UI thread.
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -29,7 +29,6 @@
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/ReentrantMonitor.h"
|
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
#include "mozilla/TypedEnum.h"
|
#include "mozilla/TypedEnum.h"
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
|
@ -132,6 +131,13 @@ namespace image {
|
||||||
|
|
||||||
class Decoder;
|
class Decoder;
|
||||||
class FrameAnimator;
|
class FrameAnimator;
|
||||||
|
class SourceBuffer;
|
||||||
|
|
||||||
|
MOZ_BEGIN_ENUM_CLASS(DecodeStrategy, uint8_t)
|
||||||
|
ASYNC,
|
||||||
|
SYNC_FOR_SMALL_IMAGES,
|
||||||
|
SYNC_IF_POSSIBLE
|
||||||
|
MOZ_END_ENUM_CLASS(DecodeStrategy)
|
||||||
|
|
||||||
class RasterImage MOZ_FINAL : public ImageResource
|
class RasterImage MOZ_FINAL : public ImageResource
|
||||||
, public nsIProperties
|
, public nsIProperties
|
||||||
|
@ -162,10 +168,10 @@ public:
|
||||||
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE;
|
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE;
|
||||||
|
|
||||||
// Raster-specific methods
|
// Raster-specific methods
|
||||||
static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure,
|
static NS_METHOD WriteToSourceBuffer(nsIInputStream* aIn, void* aClosure,
|
||||||
const char* aFromRawSegment,
|
const char* aFromRawSegment,
|
||||||
uint32_t aToOffset, uint32_t aCount,
|
uint32_t aToOffset, uint32_t aCount,
|
||||||
uint32_t* aWriteCount);
|
uint32_t* aWriteCount);
|
||||||
|
|
||||||
/* The total number of frames in this image. */
|
/* The total number of frames in this image. */
|
||||||
uint32_t GetNumFrames() const { return mFrameCount; }
|
uint32_t GetNumFrames() const { return mFrameCount; }
|
||||||
|
@ -196,24 +202,36 @@ public:
|
||||||
*/
|
*/
|
||||||
void SetLoopCount(int32_t aLoopCount);
|
void SetLoopCount(int32_t aLoopCount);
|
||||||
|
|
||||||
/* notification that the entire image has been decoded */
|
/// Notification that the entire image has been decoded.
|
||||||
void DecodingComplete(imgFrame* aFinalFrame);
|
void OnDecodingComplete();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the provided progress notifications to ProgressTracker.
|
||||||
|
*
|
||||||
|
* Main-thread only.
|
||||||
|
*
|
||||||
|
* @param aProgress The progress notifications to send.
|
||||||
|
* @param aInvalidRect An invalidation rect to send.
|
||||||
|
* @param aFlags The decode flags used by the decoder that generated
|
||||||
|
* these notifications, or DECODE_FLAGS_DEFAULT if the
|
||||||
|
* notifications don't come from a decoder.
|
||||||
|
*/
|
||||||
|
void NotifyProgress(Progress aProgress,
|
||||||
|
const nsIntRect& aInvalidRect = nsIntRect(),
|
||||||
|
uint32_t aFlags = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records telemetry and does final teardown of the provided decoder.
|
||||||
|
*
|
||||||
|
* Main-thread only.
|
||||||
|
*/
|
||||||
|
void FinalizeDecoder(Decoder* aDecoder);
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Network callbacks.
|
// Network callbacks.
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/* Add compressed source data to the imgContainer.
|
|
||||||
*
|
|
||||||
* The decoder will use this data, either immediately or at draw time, to
|
|
||||||
* decode the image.
|
|
||||||
*
|
|
||||||
* XXX This method's only caller (WriteToContainer) ignores the return
|
|
||||||
* value. Should this just return void?
|
|
||||||
*/
|
|
||||||
nsresult AddSourceData(const char *aBuffer, uint32_t aCount);
|
|
||||||
|
|
||||||
virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
|
virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
|
||||||
nsISupports* aContext,
|
nsISupports* aContext,
|
||||||
nsIInputStream* aInStr,
|
nsIInputStream* aInStr,
|
||||||
|
@ -237,7 +255,7 @@ public:
|
||||||
* Thus, pre-allocation simplifies code and reduces the total number of
|
* Thus, pre-allocation simplifies code and reduces the total number of
|
||||||
* allocations.
|
* allocations.
|
||||||
*/
|
*/
|
||||||
nsresult SetSourceSizeHint(uint32_t sizeHint);
|
nsresult SetSourceSizeHint(uint32_t aSizeHint);
|
||||||
|
|
||||||
/* Provide a hint for the requested resolution of the resulting image. */
|
/* Provide a hint for the requested resolution of the resulting image. */
|
||||||
void SetRequestedResolution(const nsIntSize requestedResolution) {
|
void SetRequestedResolution(const nsIntSize requestedResolution) {
|
||||||
|
@ -267,14 +285,6 @@ public:
|
||||||
static void Initialize();
|
static void Initialize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DecodePool;
|
|
||||||
friend class DecodeWorker;
|
|
||||||
friend class FrameNeededWorker;
|
|
||||||
friend class NotifyProgressWorker;
|
|
||||||
|
|
||||||
nsresult FinishedSomeDecoding(ShutdownReason aReason = ShutdownReason::DONE,
|
|
||||||
Progress aProgress = NoProgress);
|
|
||||||
|
|
||||||
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
||||||
gfxContext* aContext,
|
gfxContext* aContext,
|
||||||
const nsIntSize& aSize,
|
const nsIntSize& aSize,
|
||||||
|
@ -304,44 +314,34 @@ private:
|
||||||
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
|
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
|
||||||
MallocSizeOf aMallocSizeOf) const;
|
MallocSizeOf aMallocSizeOf) const;
|
||||||
|
|
||||||
nsresult DoImageDataComplete();
|
|
||||||
|
|
||||||
already_AddRefed<layers::Image> GetCurrentImage();
|
already_AddRefed<layers::Image> GetCurrentImage();
|
||||||
void UpdateImageContainer();
|
void UpdateImageContainer();
|
||||||
|
|
||||||
enum RequestDecodeType {
|
|
||||||
ASYNCHRONOUS,
|
|
||||||
SYNCHRONOUS_NOTIFY,
|
|
||||||
SYNCHRONOUS_NOTIFY_AND_SOME_DECODE
|
|
||||||
};
|
|
||||||
NS_IMETHOD RequestDecodeCore(RequestDecodeType aDecodeType);
|
|
||||||
|
|
||||||
// We would like to just check if we have a zero lock count, but we can't do
|
// We would like to just check if we have a zero lock count, but we can't do
|
||||||
// that for animated images because in EnsureAnimExists we lock the image and
|
// that for animated images because in EnsureAnimExists we lock the image and
|
||||||
// never unlock so that animated images always have their lock count >= 1. In
|
// never unlock so that animated images always have their lock count >= 1. In
|
||||||
// that case we use our animation consumers count as a proxy for lock count.
|
// that case we use our animation consumers count as a proxy for lock count.
|
||||||
bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); }
|
bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); }
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Decoding.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
already_AddRefed<Decoder> CreateDecoder(bool aDoSizeDecode, uint32_t aFlags);
|
||||||
|
|
||||||
|
void WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
||||||
|
|
||||||
|
NS_IMETHOD Decode(DecodeStrategy aStrategy, uint32_t aFlags,
|
||||||
|
bool aDoSizeDecode = false);
|
||||||
|
|
||||||
private: // data
|
private: // data
|
||||||
nsIntSize mSize;
|
nsIntSize mSize;
|
||||||
Orientation mOrientation;
|
Orientation mOrientation;
|
||||||
|
|
||||||
// Whether our frames were decoded using any special flags.
|
|
||||||
// Some flags (e.g. unpremultiplied data) may not be compatible
|
|
||||||
// with the browser's needs for displaying the image to the user.
|
|
||||||
// As such, we may need to redecode if we're being asked for
|
|
||||||
// a frame with different flags. 0 indicates default flags.
|
|
||||||
//
|
|
||||||
// Valid flag bits are imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
|
|
||||||
// and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION.
|
|
||||||
uint32_t mFrameDecodeFlags;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIProperties> mProperties;
|
nsCOMPtr<nsIProperties> mProperties;
|
||||||
|
|
||||||
//! All the frames of the image.
|
/// If this image is animated, a FrameAnimator which manages its animation.
|
||||||
// IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
|
|
||||||
// that the frames actually exist (they may have been discarded to save memory, or
|
|
||||||
// we maybe decoding on draw).
|
|
||||||
UniquePtr<FrameAnimator> mAnim;
|
UniquePtr<FrameAnimator> mAnim;
|
||||||
|
|
||||||
// Image locking.
|
// Image locking.
|
||||||
|
@ -370,18 +370,8 @@ 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
|
// The source data for this image.
|
||||||
// at once, and hence need to be locked by mDecodingMonitor.
|
nsRefPtr<SourceBuffer> mSourceBuffer;
|
||||||
|
|
||||||
// BEGIN LOCKED MEMBER VARIABLES
|
|
||||||
ReentrantMonitor mDecodingMonitor;
|
|
||||||
|
|
||||||
FallibleTArray<char> mSourceData;
|
|
||||||
|
|
||||||
// Decoder and friends
|
|
||||||
nsRefPtr<Decoder> mDecoder;
|
|
||||||
DecodeStatus mDecodeStatus;
|
|
||||||
// END LOCKED MEMBER VARIABLES
|
|
||||||
|
|
||||||
// The number of frames this image has.
|
// The number of frames this image has.
|
||||||
uint32_t mFrameCount;
|
uint32_t mFrameCount;
|
||||||
|
@ -397,10 +387,7 @@ private: // data
|
||||||
bool mTransient:1; // Is the image short-lived?
|
bool mTransient:1; // Is the image short-lived?
|
||||||
bool mDiscardable:1; // Is container discardable?
|
bool mDiscardable:1; // Is container discardable?
|
||||||
bool mHasSourceData:1; // Do we have source data?
|
bool mHasSourceData:1; // Do we have source data?
|
||||||
|
bool mHasBeenDecoded:1; // Decoded at least once?
|
||||||
// Do we have the frames in decoded form?
|
|
||||||
bool mDecoded:1;
|
|
||||||
bool mHasBeenDecoded:1;
|
|
||||||
|
|
||||||
// Whether we're waiting to start animation. If we get a StartAnimation() call
|
// Whether we're waiting to start animation. If we get a StartAnimation() call
|
||||||
// but we don't yet have more than one frame, mPendingAnimation is set so that
|
// but we don't yet have more than one frame, mPendingAnimation is set so that
|
||||||
|
@ -415,27 +402,11 @@ private: // data
|
||||||
// off a full decode.
|
// off a full decode.
|
||||||
bool mWantFullDecode:1;
|
bool mWantFullDecode:1;
|
||||||
|
|
||||||
// Set when a decode worker detects an error off-main-thread. Once the error
|
|
||||||
// is handled on the main thread, mError is set, but mPendingError is used to
|
|
||||||
// stop decode work immediately.
|
|
||||||
bool mPendingError:1;
|
|
||||||
|
|
||||||
// Decoding
|
|
||||||
nsresult RequestDecodeIfNeeded(nsresult aStatus, ShutdownReason aReason,
|
|
||||||
bool aDone, bool aWasSize);
|
|
||||||
nsresult WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
|
||||||
nsresult SyncDecode();
|
|
||||||
nsresult InitDecoder(bool aDoSizeDecode);
|
|
||||||
nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount);
|
|
||||||
nsresult DecodeSomeData(size_t aMaxBytes);
|
|
||||||
bool IsDecodeFinished();
|
|
||||||
TimeStamp mDrawStartTime;
|
TimeStamp mDrawStartTime;
|
||||||
|
|
||||||
// Initializes ProgressTracker and resets it on RasterImage destruction.
|
// Initializes ProgressTracker and resets it on RasterImage destruction.
|
||||||
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
|
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
|
||||||
|
|
||||||
nsresult ShutdownDecoder(ShutdownReason aReason);
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Scaling.
|
// Scaling.
|
||||||
|
@ -476,7 +447,6 @@ private: // data
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
bool CanDiscard();
|
bool CanDiscard();
|
||||||
bool StoringSourceData() const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit RasterImage(ProgressTracker* aProgressTracker = nullptr,
|
explicit RasterImage(ProgressTracker* aProgressTracker = nullptr,
|
||||||
|
|
|
@ -3785,9 +3785,6 @@ pref("image.mem.allow_locking_in_content_processes", true);
|
||||||
// Chunk size for calls to the image decoders
|
// Chunk size for calls to the image decoders
|
||||||
pref("image.mem.decode_bytes_at_a_time", 16384);
|
pref("image.mem.decode_bytes_at_a_time", 16384);
|
||||||
|
|
||||||
// The longest time we can spend in an iteration of an async decode
|
|
||||||
pref("image.mem.max_ms_before_yield", 5);
|
|
||||||
|
|
||||||
// Minimum timeout for expiring unused images from the surface cache, in
|
// Minimum timeout for expiring unused images from the surface cache, in
|
||||||
// milliseconds. This controls how long we store cached temporary surfaces.
|
// milliseconds. This controls how long we store cached temporary surfaces.
|
||||||
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60ms
|
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60ms
|
||||||
|
|
Загрузка…
Ссылка в новой задаче