зеркало из https://github.com/mozilla/gecko-dev.git
Bug 962670 - Store decoded images in VolatileBuffers, r=seth,jrmuizel
This commit is contained in:
Родитель
fbd5bc5ade
Коммит
ead4d27e3d
|
@ -26,6 +26,7 @@
|
|||
#include "nsSize.h" // for nsIntSize
|
||||
#include "nsTArray.h" // for nsTArray
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
@ -380,7 +381,7 @@ struct RemoteImageData {
|
|||
* updates the shared state to point to the new image and the old image
|
||||
* is immediately released (not true in Normal or Asynchronous modes).
|
||||
*/
|
||||
class ImageContainer {
|
||||
class ImageContainer : public SupportsWeakPtr<ImageContainer> {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
|
||||
public:
|
||||
|
||||
|
|
|
@ -922,8 +922,24 @@ RasterImage::GetFrame(uint32_t aWhichFrame,
|
|||
nsIntRect framerect = frame->GetRect();
|
||||
if (framerect.x == 0 && framerect.y == 0 &&
|
||||
framerect.width == mSize.width &&
|
||||
framerect.height == mSize.height)
|
||||
framerect.height == mSize.height) {
|
||||
frame->GetSurface(getter_AddRefs(framesurf));
|
||||
if (!framesurf && !frame->IsSinglePixel()) {
|
||||
// No reason to be optimized away here - the OS threw out the data
|
||||
if (!(aFlags & FLAG_SYNC_DECODE))
|
||||
return nullptr;
|
||||
|
||||
// Unconditionally call ForceDiscard() here because GetSurface can only
|
||||
// return null when we can forcibly discard and redecode. There are two
|
||||
// other cases where GetSurface() can return null - when it is a single
|
||||
// pixel image, which we check before getting here, or when this is an
|
||||
// indexed image, in which case we shouldn't be in this function at all.
|
||||
// The only remaining possibility is that SetDiscardable() was called on
|
||||
// this imgFrame, which implies the image can be redecoded.
|
||||
ForceDiscard();
|
||||
return GetFrame(aWhichFrame, aFlags);
|
||||
}
|
||||
}
|
||||
|
||||
// The image doesn't have a surface because it's been optimized away. Create
|
||||
// one.
|
||||
|
@ -947,7 +963,15 @@ RasterImage::GetCurrentImage()
|
|||
}
|
||||
|
||||
nsRefPtr<gfxASurface> imageSurface = GetFrame(FRAME_CURRENT, FLAG_NONE);
|
||||
NS_ENSURE_TRUE(imageSurface, nullptr);
|
||||
if (!imageSurface) {
|
||||
// The OS threw out some or all of our buffer. Start decoding again.
|
||||
// GetFrame will only return null in the case that the image was
|
||||
// discarded. We already checked that the image is decoded, so other
|
||||
// error paths are not possible.
|
||||
ForceDiscard();
|
||||
RequestDecodeCore(ASYNCHRONOUS);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mImageContainer) {
|
||||
mImageContainer = LayerManager::CreateImageContainer();
|
||||
|
@ -981,6 +1005,10 @@ RasterImage::GetImageContainer(LayerManager* aManager, ImageContainer **_retval)
|
|||
mStatusTracker->OnUnlockedDraw();
|
||||
}
|
||||
|
||||
if (!mImageContainer) {
|
||||
mImageContainer = mImageContainerCache;
|
||||
}
|
||||
|
||||
if (mImageContainer) {
|
||||
*_retval = mImageContainer;
|
||||
NS_ADDREF(*_retval);
|
||||
|
@ -995,6 +1023,13 @@ RasterImage::GetImageContainer(LayerManager* aManager, ImageContainer **_retval)
|
|||
|
||||
*_retval = mImageContainer;
|
||||
NS_ADDREF(*_retval);
|
||||
// We only need to be careful about holding on to the image when it is
|
||||
// discardable by the OS.
|
||||
if (CanForciblyDiscardAndRedecode()) {
|
||||
mImageContainerCache = mImageContainer->asWeakPtr();
|
||||
mImageContainer = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1392,6 +1427,12 @@ RasterImage::DecodingComplete()
|
|||
// We don't optimize the frame for multipart images because we reuse
|
||||
// the frame.
|
||||
if ((GetNumFrames() == 1) && !mMultipart) {
|
||||
// CanForciblyDiscard is used instead of CanForciblyDiscardAndRedecode
|
||||
// because we know decoding is complete at this point and this is not
|
||||
// an animation
|
||||
if (DiscardingEnabled() && CanForciblyDiscard()) {
|
||||
mFrameBlender.RawGetFrame(0)->SetDiscardable();
|
||||
}
|
||||
rv = mFrameBlender.RawGetFrame(0)->Optimize();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -2093,13 +2134,6 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
|||
// Figure out what kind of decode we were doing before we get rid of our decoder
|
||||
bool wasSizeDecode = mDecoder->IsSizeDecode();
|
||||
|
||||
// Unlock the last frame (if we have any). Our invariant is that, while we
|
||||
// have a decoder open, the last frame is always locked.
|
||||
if (GetNumFrames() > 0) {
|
||||
imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
|
||||
curframe->UnlockImageData();
|
||||
}
|
||||
|
||||
// Finalize the decoder
|
||||
// null out mDecoder, _then_ check for errors on the close (otherwise the
|
||||
// error routine might re-invoke ShutdownDecoder)
|
||||
|
@ -2112,6 +2146,13 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
|||
mInDecoder = false;
|
||||
mFinishing = false;
|
||||
|
||||
// Unlock the last frame (if we have any). Our invariant is that, while we
|
||||
// have a decoder open, the last frame is always locked.
|
||||
if (GetNumFrames() > 0) {
|
||||
imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
|
||||
curframe->UnlockImageData();
|
||||
}
|
||||
|
||||
// Kill off our decode request, if it's pending. (If not, this call is
|
||||
// harmless.)
|
||||
DecodePool::StopDecoding(this);
|
||||
|
@ -2677,6 +2718,17 @@ RasterImage::Draw(gfxContext *aContext,
|
|||
return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
|
||||
}
|
||||
|
||||
nsRefPtr<gfxASurface> surf;
|
||||
if (!frame->IsSinglePixel()) {
|
||||
frame->GetSurface(getter_AddRefs(surf));
|
||||
if (!surf) {
|
||||
// The OS threw out some or all of our buffer. Start decoding again.
|
||||
ForceDiscard();
|
||||
WantDecodedFrames();
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter, aUserSpaceToImageSpace, aFill, aSubimage, aFlags);
|
||||
|
||||
if (mDecoded && !mDrawStartTime.IsNull()) {
|
||||
|
|
|
@ -659,6 +659,9 @@ private: // data
|
|||
// Cached value for GetImageContainer.
|
||||
nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
|
||||
|
||||
// If not cached in mImageContainer, this might have our image container
|
||||
WeakPtr<mozilla::layers::ImageContainer> mImageContainerCache;
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t mFramesNotified;
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "gfx2DGlue.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "gfxAlphaRecovery.h"
|
||||
|
||||
static bool gDisableOptimize = false;
|
||||
|
||||
|
@ -35,6 +36,48 @@ using namespace mozilla;
|
|||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::image;
|
||||
|
||||
static cairo_user_data_key_t kVolatileBuffer;
|
||||
|
||||
static void
|
||||
VolatileBufferRelease(void *vbuf)
|
||||
{
|
||||
delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
|
||||
}
|
||||
|
||||
gfxImageSurface *
|
||||
LockedImageSurface::CreateSurface(VolatileBuffer *vbuf,
|
||||
const gfxIntSize& size,
|
||||
gfxImageFormat format)
|
||||
{
|
||||
VolatileBufferPtr<unsigned char> *vbufptr =
|
||||
new VolatileBufferPtr<unsigned char>(vbuf);
|
||||
MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
|
||||
|
||||
long stride = gfxImageSurface::ComputeStride(size, format);
|
||||
gfxImageSurface *img = new gfxImageSurface(*vbufptr, size, stride, format);
|
||||
if (!img || img->CairoStatus()) {
|
||||
delete img;
|
||||
delete vbufptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
img->SetData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
|
||||
return img;
|
||||
}
|
||||
|
||||
TemporaryRef<VolatileBuffer>
|
||||
LockedImageSurface::AllocateBuffer(const gfxIntSize& size,
|
||||
gfxImageFormat format)
|
||||
{
|
||||
long stride = gfxImageSurface::ComputeStride(size, format);
|
||||
RefPtr<VolatileBuffer> buf = new VolatileBuffer();
|
||||
if (buf->Init(stride * size.height,
|
||||
1 << gfxAlphaRecovery::GoodAlignmentLog2()))
|
||||
return buf;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Returns true if an image of aWidth x aHeight is allowed and legal.
|
||||
static bool AllowedImageSize(int32_t aWidth, int32_t aHeight)
|
||||
{
|
||||
|
@ -108,6 +151,7 @@ imgFrame::imgFrame() :
|
|||
mFormatChanged(false),
|
||||
mCompositingFailed(false),
|
||||
mNonPremult(false),
|
||||
mDiscardable(false),
|
||||
mInformedDiscardTracker(false),
|
||||
mDirty(false)
|
||||
{
|
||||
|
@ -174,12 +218,17 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
|
|||
}
|
||||
#endif
|
||||
|
||||
// For other platforms we create the image surface first and then
|
||||
// possibly wrap it in a device surface. This branch is also used
|
||||
// on Windows if we're not using device surfaces or if we couldn't
|
||||
// create one.
|
||||
if (!mImageSurface)
|
||||
mImageSurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
|
||||
// For other platforms, space for the image surface is first allocated in
|
||||
// a volatile buffer and then wrapped by a LockedImageSurface.
|
||||
// This branch is also used on Windows if we're not using device surfaces
|
||||
// or if we couldn't create one.
|
||||
if (!mImageSurface) {
|
||||
mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
|
||||
if (!mVBuf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
|
||||
}
|
||||
|
||||
if (!mImageSurface || mImageSurface->CairoStatus()) {
|
||||
mImageSurface = nullptr;
|
||||
|
@ -251,6 +300,7 @@ nsresult imgFrame::Optimize()
|
|||
mSinglePixel = true;
|
||||
|
||||
// blow away the older surfaces (if they exist), to release their memory
|
||||
mVBuf = nullptr;
|
||||
mImageSurface = nullptr;
|
||||
mOptSurface = nullptr;
|
||||
#ifdef USE_WIN_SURFACE
|
||||
|
@ -300,6 +350,7 @@ nsresult imgFrame::Optimize()
|
|||
mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
|
||||
|
||||
if (mOptSurface) {
|
||||
mVBuf = nullptr;
|
||||
mImageSurface = nullptr;
|
||||
#ifdef USE_WIN_SURFACE
|
||||
mWinSurface = nullptr;
|
||||
|
@ -483,10 +534,13 @@ uint32_t imgFrame::GetImageBytesPerRow() const
|
|||
if (mImageSurface)
|
||||
return mImageSurface->Stride();
|
||||
|
||||
if (mVBuf)
|
||||
return gfxImageSurface::ComputeStride(mSize, mFormat);
|
||||
|
||||
if (mPaletteDepth)
|
||||
return mSize.width;
|
||||
|
||||
NS_ERROR("GetImageBytesPerRow called with mImageSurface == null and mPaletteDepth == 0");
|
||||
NS_ERROR("GetImageBytesPerRow called with mImageSurface == null, mVBuf == null and mPaletteDepth == 0");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -569,28 +623,42 @@ nsresult imgFrame::LockImageData()
|
|||
if (mPalettedImageData)
|
||||
return NS_OK;
|
||||
|
||||
if ((mOptSurface || mSinglePixel) && !mImageSurface) {
|
||||
// Recover the pixels
|
||||
mImageSurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
|
||||
gfxImageFormat::ARGB32);
|
||||
if (!mImageSurface || mImageSurface->CairoStatus())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (!mImageSurface) {
|
||||
if (mVBuf) {
|
||||
VolatileBufferPtr<uint8_t> ref(mVBuf);
|
||||
if (ref.WasBufferPurged())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
gfxContext context(mImageSurface);
|
||||
context.SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
if (mSinglePixel)
|
||||
context.SetDeviceColor(mSinglePixelColor);
|
||||
else
|
||||
context.SetSource(mOptSurface);
|
||||
context.Paint();
|
||||
mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
|
||||
if (!mImageSurface || mImageSurface->CairoStatus())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
} else if (mOptSurface || mSinglePixel) {
|
||||
// Recover the pixels
|
||||
mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
|
||||
if (!mVBuf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mOptSurface = nullptr;
|
||||
mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
|
||||
if (!mImageSurface || mImageSurface->CairoStatus())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
gfxContext context(mImageSurface);
|
||||
context.SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
if (mSinglePixel)
|
||||
context.SetDeviceColor(mSinglePixelColor);
|
||||
else
|
||||
context.SetSource(mOptSurface);
|
||||
context.Paint();
|
||||
|
||||
mOptSurface = nullptr;
|
||||
#ifdef USE_WIN_SURFACE
|
||||
mWinSurface = nullptr;
|
||||
mWinSurface = nullptr;
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
mQuartzSurface = nullptr;
|
||||
mQuartzSurface = nullptr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// We might write to the bits in this image surface, so we need to make the
|
||||
|
@ -603,6 +671,12 @@ nsresult imgFrame::LockImageData()
|
|||
mWinSurface->Flush();
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
if (!mQuartzSurface && !ShouldUseImageSurfaces()) {
|
||||
mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -658,6 +732,13 @@ nsresult imgFrame::UnlockImageData()
|
|||
mQuartzSurface->Flush();
|
||||
#endif
|
||||
|
||||
if (mVBuf && mDiscardable) {
|
||||
mImageSurface = nullptr;
|
||||
#ifdef XP_MACOSX
|
||||
mQuartzSurface = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -697,6 +778,12 @@ void imgFrame::ApplyDirtToSurfaces()
|
|||
}
|
||||
}
|
||||
|
||||
void imgFrame::SetDiscardable()
|
||||
{
|
||||
MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
|
||||
mDiscardable = true;
|
||||
}
|
||||
|
||||
int32_t imgFrame::GetRawTimeout() const
|
||||
{
|
||||
return mTimeout;
|
||||
|
@ -795,8 +882,8 @@ imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocat
|
|||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
if (mQuartzSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
|
||||
n += mSize.width * mSize.height * 4;
|
||||
} else
|
||||
n += aMallocSizeOf(mQuartzSurface);
|
||||
}
|
||||
#endif
|
||||
if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) {
|
||||
size_t n2 = 0;
|
||||
|
@ -809,6 +896,11 @@ imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocat
|
|||
n += n2;
|
||||
}
|
||||
|
||||
if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
|
||||
n += aMallocSizeOf(mVBuf);
|
||||
n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
|
||||
size_t n2 = 0;
|
||||
if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/VolatileBuffer.h"
|
||||
#include "nsRect.h"
|
||||
#include "nsPoint.h"
|
||||
#include "nsSize.h"
|
||||
|
@ -24,6 +25,21 @@
|
|||
#include "imgIContainer.h"
|
||||
#include "gfxColor.h"
|
||||
|
||||
/*
|
||||
* This creates a gfxImageSurface which will unlock the buffer on destruction
|
||||
*/
|
||||
|
||||
class LockedImageSurface
|
||||
{
|
||||
public:
|
||||
static gfxImageSurface *
|
||||
CreateSurface(mozilla::VolatileBuffer *vbuf,
|
||||
const gfxIntSize& size,
|
||||
gfxImageFormat format);
|
||||
static mozilla::TemporaryRef<mozilla::VolatileBuffer>
|
||||
AllocateBuffer(const gfxIntSize& size, gfxImageFormat format);
|
||||
};
|
||||
|
||||
class imgFrame
|
||||
{
|
||||
public:
|
||||
|
@ -72,14 +88,16 @@ public:
|
|||
nsresult UnlockImageData();
|
||||
void ApplyDirtToSurfaces();
|
||||
|
||||
nsresult GetSurface(gfxASurface **aSurface) const
|
||||
void SetDiscardable();
|
||||
|
||||
nsresult GetSurface(gfxASurface **aSurface)
|
||||
{
|
||||
*aSurface = ThebesSurface();
|
||||
NS_IF_ADDREF(*aSurface);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult GetPattern(gfxPattern **aPattern) const
|
||||
nsresult GetPattern(gfxPattern **aPattern)
|
||||
{
|
||||
if (mSinglePixel)
|
||||
*aPattern = new gfxPattern(mSinglePixelColor);
|
||||
|
@ -89,7 +107,12 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
gfxASurface* ThebesSurface() const
|
||||
bool IsSinglePixel()
|
||||
{
|
||||
return mSinglePixel;
|
||||
}
|
||||
|
||||
gfxASurface* ThebesSurface()
|
||||
{
|
||||
if (mOptSurface)
|
||||
return mOptSurface;
|
||||
|
@ -100,7 +123,27 @@ public:
|
|||
if (mQuartzSurface)
|
||||
return mQuartzSurface;
|
||||
#endif
|
||||
return mImageSurface;
|
||||
if (mImageSurface)
|
||||
return mImageSurface;
|
||||
if (mVBuf) {
|
||||
mozilla::VolatileBufferPtr<uint8_t> ref(mVBuf);
|
||||
if (ref.WasBufferPurged())
|
||||
return nullptr;
|
||||
|
||||
gfxImageSurface *sur =
|
||||
LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
|
||||
#if defined(XP_MACOSX)
|
||||
return new gfxQuartzImageSurface(sur);
|
||||
#else
|
||||
return sur;
|
||||
#endif
|
||||
}
|
||||
// We can return null here if we're single pixel optimized
|
||||
// or a paletted image. However, one has to check for paletted
|
||||
// image data first before attempting to get a surface, so
|
||||
// this is only valid for single pixel optimized images
|
||||
MOZ_ASSERT(mSinglePixel, "No image surface and not a single pixel!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t SizeOfExcludingThisWithComputedFallbackIfHeap(
|
||||
|
@ -167,6 +210,8 @@ private: // data
|
|||
/** Indicates how many readers currently have locked this frame */
|
||||
int32_t mLockCount;
|
||||
|
||||
mozilla::RefPtr<mozilla::VolatileBuffer> mVBuf;
|
||||
|
||||
gfxImageFormat mFormat;
|
||||
uint8_t mPaletteDepth;
|
||||
int8_t mBlendMethod;
|
||||
|
@ -174,6 +219,7 @@ private: // data
|
|||
bool mFormatChanged;
|
||||
bool mCompositingFailed;
|
||||
bool mNonPremult;
|
||||
bool mDiscardable;
|
||||
|
||||
/** Have we called DiscardTracker::InformAllocation()? */
|
||||
bool mInformedDiscardTracker;
|
||||
|
|
Загрузка…
Ссылка в новой задаче