b=563701; add memory reporters for imglib, canvas, and some gfx surfaces; r=joe

This commit is contained in:
Vladimir Vukicevic 2010-05-21 21:10:14 -07:00
parent 6cd446aadb
commit 569d29edc3
12 changed files with 384 additions and 10 deletions

View File

@ -108,6 +108,8 @@
#include "CanvasUtils.h"
#include "nsIMemoryReporter.h"
using namespace mozilla;
using namespace mozilla::layers;
@ -168,6 +170,20 @@ static PRBool FloatValidate (double f1, double f2, double f3, double f4, double
#undef VALIDATE
/* Memory reporter stuff */
static nsIMemoryReporter *gCanvasMemoryReporter = nsnull;
static PRInt64 gCanvasMemoryUsed = 0;
static PRInt64 GetCanvasMemoryUsed(void *) {
return gCanvasMemoryUsed;
}
NS_MEMORY_REPORTER_IMPLEMENT(CanvasMemory,
"content/canvas/2d_pixel_bytes",
"Total memory used by 2D canvas (width * height * 4)",
GetCanvasMemoryUsed,
NULL)
/**
** nsCanvasGradient
**/
@ -728,6 +744,9 @@ nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D()
void
nsCanvasRenderingContext2D::Destroy()
{
if (mValid)
gCanvasMemoryUsed -= mWidth * mHeight * 4;
mSurface = nsnull;
mThebes = nsnull;
mValid = PR_FALSE;
@ -938,6 +957,16 @@ nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
surface = NULL;
}
}
if (surface) {
if (gCanvasMemoryReporter == nsnull) {
gCanvasMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasMemory);
NS_RegisterMemoryReporter(gCanvasMemoryReporter);
}
gCanvasMemoryUsed += width * height * 4;
}
return InitializeWithSurface(NULL, surface, width, height);
}

View File

@ -88,7 +88,8 @@ public:
SurfaceTypeQuartzImage,
SurfaceTypeScript,
SurfaceTypeQPainter,
SurfaceTypeDDraw
SurfaceTypeDDraw,
SurfaceTypeMax
} gfxSurfaceType;
typedef enum {
@ -147,8 +148,26 @@ public:
static gfxContentType ContentFromFormat(gfxImageFormat format);
/**
* Record number of bytes for given surface type. Use positive bytes
* for allocations and negative bytes for deallocations.
*/
static void RecordMemoryUsedForSurfaceType(gfxASurface::gfxSurfaceType aType,
PRInt32 aBytes);
/**
* Same as above, but use current surface type as returned by GetType().
* The bytes will be accumulated until RecordMemoryFreed is called,
* in which case the value that was recorded for this surface will
* be freed.
*/
void RecordMemoryUsed(PRInt32 aBytes);
void RecordMemoryFreed();
PRInt32 KnownMemoryUsed() { return mBytesRecorded; }
protected:
gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mSurfaceValid(PR_FALSE) { }
gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mBytesRecorded(0), mSurfaceValid(PR_FALSE) { }
static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);
static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf);
@ -156,12 +175,15 @@ protected:
void Init(cairo_surface_t *surface, PRBool existingSurface = PR_FALSE);
virtual ~gfxASurface() {
RecordMemoryFreed();
}
private:
static void SurfaceDestroyFunc(void *data);
cairo_surface_t *mSurface;
PRInt32 mFloatingRefs;
PRInt32 mBytesRecorded;
protected:
PRPackedBool mSurfaceValid;

View File

@ -36,6 +36,9 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsIMemoryReporter.h"
#include "nsMemory.h"
#include "gfxASurface.h"
#include "gfxImageSurface.h"
@ -398,3 +401,104 @@ gfxASurface::ContentFromFormat(gfxImageFormat format)
return CONTENT_COLOR;
}
}
/** Memory reporting **/
static const char *sSurfaceNamesForSurfaceType[] = {
"gfx/surface/image",
"gfx/surface/pdf",
"gfx/surface/ps",
"gfx/surface/xlib",
"gfx/surface/xcb",
"gfx/surface/glitz",
"gfx/surface/quartz",
"gfx/surface/win32",
"gfx/surface/beos",
"gfx/surface/directfb",
"gfx/surface/svg",
"gfx/surface/os2",
"gfx/surface/win32printing",
"gfx/surface/quartzimage",
"gfx/surface/script",
"gfx/surface/qpainter",
"gfx/surface/ddraw"
};
PR_STATIC_ASSERT(NS_ARRAY_LENGTH(sSurfaceNamesForSurfaceType) == gfxASurface::SurfaceTypeMax);
static const char *
SurfaceMemoryReporterPathForType(gfxASurface::gfxSurfaceType aType)
{
if (aType < 0 ||
aType >= gfxASurface::SurfaceTypeMax)
return "gfx/surface/unknown";
return sSurfaceNamesForSurfaceType[aType];
}
/* Surface size memory reporting */
static nsIMemoryReporter *gSurfaceMemoryReporters[gfxASurface::SurfaceTypeMax] = { 0 };
static PRInt64 gSurfaceMemoryUsed[gfxASurface::SurfaceTypeMax] = { 0 };
class SurfaceMemoryReporter :
public nsIMemoryReporter
{
public:
SurfaceMemoryReporter(gfxASurface::gfxSurfaceType aType)
: mType(aType)
{ }
NS_DECL_ISUPPORTS
NS_IMETHOD GetPath(char **memoryPath) {
*memoryPath = strdup(SurfaceMemoryReporterPathForType(mType));
return NS_OK;
}
NS_IMETHOD GetDescription(char **desc) {
*desc = strdup("Memory used by gfx surface of given type.");
return NS_OK;
}
NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) {
*memoryUsed = gSurfaceMemoryUsed[mType];
return NS_OK;
}
gfxASurface::gfxSurfaceType mType;
};
NS_IMPL_ISUPPORTS1(SurfaceMemoryReporter, nsIMemoryReporter)
void
gfxASurface::RecordMemoryUsedForSurfaceType(gfxASurface::gfxSurfaceType aType,
PRInt32 aBytes)
{
if (aType < 0 || aType >= SurfaceTypeMax) {
NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
return;
}
if (gSurfaceMemoryReporters[aType] == 0) {
gSurfaceMemoryReporters[aType] = new SurfaceMemoryReporter(aType);
NS_RegisterMemoryReporter(gSurfaceMemoryReporters[aType]);
}
gSurfaceMemoryUsed[aType] += aBytes;
}
void
gfxASurface::RecordMemoryUsed(PRInt32 aBytes)
{
RecordMemoryUsedForSurfaceType(GetType(), aBytes);
mBytesRecorded += aBytes;
}
void
gfxASurface::RecordMemoryFreed()
{
if (mBytesRecorded) {
RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
mBytesRecorded = 0;
}
}

View File

@ -113,7 +113,10 @@ gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format)
mSize.width,
mSize.height,
mStride);
Init(surface);
RecordMemoryUsed(mSize.height * ComputeStride() + sizeof(gfxImageSurface));
}
gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)

View File

@ -74,8 +74,11 @@ gfxWindowsSurface::gfxWindowsSurface(const gfxIntSize& size, gfxImageFormat imag
cairo_surface_t *surf = cairo_win32_surface_create_with_dib((cairo_format_t)imageFormat,
size.width, size.height);
Init(surf);
RecordMemoryUsed(size.width * size.height * 4 + sizeof(gfxWindowsSurface));
if (CairoStatus() == 0)
mDC = cairo_win32_surface_get_dc(CairoSurface());
else
@ -90,8 +93,13 @@ gfxWindowsSurface::gfxWindowsSurface(HDC dc, const gfxIntSize& size, gfxImageFor
cairo_surface_t *surf = cairo_win32_surface_create_with_ddb(dc, (cairo_format_t)imageFormat,
size.width, size.height);
Init(surf);
// DDBs will generally only use 3 bytes per pixel when RGB24
int bytesPerPixel = ((imageFormat == gfxASurface::ImageFormatRGB24) ? 3 : 4);
RecordMemoryUsed(size.width * size.height * bytesPerPixel + sizeof(gfxWindowsSurface));
if (CairoStatus() == 0)
mDC = cairo_win32_surface_get_dc(CairoSurface());
else

View File

@ -639,18 +639,30 @@ NS_IMETHODIMP imgContainer::GetDataSize(PRUint32 *_retval)
*_retval = 0;
// Account for any compressed source data
*_retval += mSourceData.Length();
*_retval += GetSourceDataSize();
NS_ABORT_IF_FALSE(StoringSourceData() || (*_retval == 0),
"Non-zero source data size when we aren't storing it?");
// Account for any uncompressed frames
*_retval += GetDecodedDataSize();
return NS_OK;
}
PRUint32 imgContainer::GetDecodedDataSize()
{
PRUint32 val = 0;
for (PRUint32 i = 0; i < mFrames.Length(); ++i) {
imgFrame *frame = mFrames.SafeElementAt(i, nsnull);
NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
*_retval += frame->GetImageDataLength();
val += frame->EstimateMemoryUsed();
}
return NS_OK;
return val;
}
PRUint32 imgContainer::GetSourceDataSize()
{
return mSourceData.Length();
}
void imgContainer::DeleteImgFrame(PRUint32 framenum)

View File

@ -154,6 +154,9 @@ public:
PRUint32 toOffset, PRUint32 count,
PRUint32 *writeCount);
PRUint32 GetDecodedDataSize();
PRUint32 GetSourceDataSize();
private:
struct Anim
{

View File

@ -768,16 +768,18 @@ PRUint32 imgFrame::GetImageBytesPerRow() const
{
if (mImageSurface)
return mImageSurface->Stride();
else
if (mPaletteDepth)
return mSize.width;
NS_ERROR("GetImageBytesPerRow called with mImageSurface == null and mPaletteDepth == 0");
return 0;
}
PRUint32 imgFrame::GetImageDataLength() const
{
if (mImageSurface)
return mImageSurface->Stride() * mSize.height;
else
return mSize.width * mSize.height;
return GetImageBytesPerRow() * mSize.height;
}
void imgFrame::GetImageData(PRUint8 **aData, PRUint32 *length) const
@ -980,3 +982,41 @@ gfxContext::GraphicsOperator imgFrame::OptimalFillOperator()
}
#endif
}
PRUint32 imgFrame::EstimateMemoryUsed() const
{
PRUint32 size = 0;
if (mSinglePixel) {
size += sizeof(gfxRGBA);
}
if (mPalettedImageData) {
size += GetImageDataLength() + PaletteDataLength();
}
#ifdef USE_WIN_SURFACE
if (mWinSurface) {
size += mWinSurface->KnownMemoryUsed();
} else
#endif
#ifdef XP_MACOSX
if (mQuartzSurface) {
size += mSize.width * mSize.height * 4;
} else
#endif
if (mImageSurface) {
size += mImageSurface->KnownMemoryUsed();
}
if (mOptSurface) {
size += mOptSurface->KnownMemoryUsed();
}
// fall back to pessimistic/approximate size
if (size == 0) {
size = mSize.width * mSize.height * 4;
}
return size;
}

View File

@ -134,6 +134,8 @@ public:
return mImageSurface;
}
// returns an estimate of the memory used by this imgFrame
PRUint32 EstimateMemoryUsed() const;
private: // methods
PRUint32 PaletteDataLength() const {

View File

@ -39,6 +39,13 @@
* ***** END LICENSE BLOCK ***** */
#include "imgLoader.h"
#include "imgContainer.h"
/* We end up pulling in windows.h because we eventually hit
* gfxWindowsSurface; it defines some crazy things, like LoadImage.
* We undefine it here so as to avoid problems later on.
*/
#undef LoadImage
#include "nsCOMPtr.h"
@ -70,6 +77,8 @@
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIMemoryReporter.h"
// we want to explore making the document own the load group
// so we can associate the document URI with the load group.
// until this point, we have an evil hack:
@ -116,6 +125,133 @@ static void PrintImageDecoders()
}
#endif
class imgMemoryReporter :
public nsIMemoryReporter
{
public:
enum ReporterType {
CHROME_BIT = PR_BIT(0),
USED_BIT = PR_BIT(1),
RAW_BIT = PR_BIT(2),
ChromeUsedRaw = CHROME_BIT | USED_BIT | RAW_BIT,
ChromeUsedUncompressed = CHROME_BIT | USED_BIT,
ChromeUnusedRaw = CHROME_BIT | RAW_BIT,
ChromeUnusedUncompressed = CHROME_BIT,
ContentUsedRaw = USED_BIT | RAW_BIT,
ContentUsedUncompressed = USED_BIT,
ContentUnusedRaw = RAW_BIT,
ContentUnusedUncompressed = 0
};
imgMemoryReporter(ReporterType aType)
: mType(aType)
{ }
NS_DECL_ISUPPORTS
NS_IMETHOD GetPath(char **memoryPath)
{
if (mType == ChromeUsedRaw) {
*memoryPath = strdup("images/chrome/used/raw");
} else if (mType == ChromeUsedUncompressed) {
*memoryPath = strdup("images/chrome/used/uncompressed");
} else if (mType == ChromeUnusedRaw) {
*memoryPath = strdup("images/chrome/unused/raw");
} else if (mType == ChromeUnusedUncompressed) {
*memoryPath = strdup("images/chrome/unused/uncompressed");
} else if (mType == ContentUsedRaw) {
*memoryPath = strdup("images/content/used/raw");
} else if (mType == ContentUsedUncompressed) {
*memoryPath = strdup("images/content/used/uncompressed");
} else if (mType == ContentUnusedRaw) {
*memoryPath = strdup("images/content/unused/raw");
} else if (mType == ContentUnusedUncompressed) {
*memoryPath = strdup("images/content/unused/uncompressed");
}
return NS_OK;
}
NS_IMETHOD GetDescription(char **desc)
{
if (mType == ChromeUsedRaw) {
*desc = strdup("Memory used by in-use chrome images, compressed data");
} else if (mType == ChromeUsedUncompressed) {
*desc = strdup("Memory used by in-use chrome images, uncompressed data");
} else if (mType == ChromeUnusedRaw) {
*desc = strdup("Memory used by not in-use chrome images, compressed data");
} else if (mType == ChromeUnusedUncompressed) {
*desc = strdup("Memory used by not in-use chrome images, uncompressed data");
} else if (mType == ContentUsedRaw) {
*desc = strdup("Memory used by in-use content images, compressed data");
} else if (mType == ContentUsedUncompressed) {
*desc = strdup("Memory used by in-use content images, uncompressed data");
} else if (mType == ContentUnusedRaw) {
*desc = strdup("Memory used by not in-use content images, compressed data");
} else if (mType == ContentUnusedUncompressed) {
*desc = strdup("Memory used by not in-use content images, uncompressed data");
}
return NS_OK;
}
struct EnumArg {
EnumArg(ReporterType aType)
: rtype(aType), value(0)
{ }
ReporterType rtype;
PRInt32 value;
};
static PLDHashOperator EnumEntries(const nsACString&,
imgCacheEntry *entry,
void *userArg)
{
EnumArg *arg = static_cast<EnumArg*>(userArg);
ReporterType rtype = arg->rtype;
if (rtype & USED_BIT) {
if (entry->HasNoProxies())
return PL_DHASH_NEXT;
} else {
if (!entry->HasNoProxies())
return PL_DHASH_NEXT;
}
nsRefPtr<imgRequest> req = entry->GetRequest();
imgContainer *container = (imgContainer*) req->mImage.get();
if (!container)
return PL_DHASH_NEXT;
if (rtype & RAW_BIT) {
arg->value += container->GetSourceDataSize();
} else {
arg->value += container->GetDecodedDataSize();
}
return PL_DHASH_NEXT;
}
NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed)
{
EnumArg arg(mType);
if (mType & CHROME_BIT) {
imgLoader::sChromeCache.EnumerateRead(EnumEntries, &arg);
} else {
imgLoader::sCache.EnumerateRead(EnumEntries, &arg);
}
*memoryUsed = arg.value;
return NS_OK;
}
ReporterType mType;
};
NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryReporter)
/**
* A class that implements nsIProgressEventSink and forwards all calls to it to
* the original notification callbacks of the channel. Also implements
@ -715,6 +851,15 @@ nsresult imgLoader::InitCache()
else
sCacheMaxSize = 5 * 1024 * 1024;
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUsedRaw));
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUsedUncompressed));
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUnusedRaw));
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUnusedUncompressed));
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUsedRaw));
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUsedUncompressed));
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUnusedRaw));
NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUnusedUncompressed));
return NS_OK;
}

View File

@ -219,6 +219,8 @@ private:
PRUint32 mSize;
};
class imgMemoryReporter;
class imgLoader : public imgILoader,
public nsIContentSniffer,
public imgICache,
@ -333,6 +335,7 @@ private: // methods
private: // data
friend class imgCacheEntry;
friend class imgMemoryReporter;
static imgCacheTable sCache;
static imgCacheQueue sCacheQueue;

View File

@ -66,6 +66,7 @@ class imgCacheValidator;
class imgRequestProxy;
class imgCacheEntry;
class imgMemoryReporter;
enum {
stateRequestStarted = PR_BIT(0),
@ -195,6 +196,8 @@ public:
NS_DECL_NSIINTERFACEREQUESTOR
private:
friend class imgMemoryReporter;
nsCOMPtr<nsIRequest> mRequest;
// The original URI we were loaded with.
nsCOMPtr<nsIURI> mURI;