Bug 397734. More parsimonious storage of glyph extents ... trying to fix private-bytes regression. r=vlad,a=pavlov

This commit is contained in:
roc+@cs.cmu.edu 2007-10-01 14:29:19 -07:00
parent 742f0cdd5b
commit 1b1c97e9d8
4 changed files with 160 additions and 26 deletions

View File

@ -246,8 +246,10 @@ class THEBES_API gfxGlyphExtents {
public:
gfxGlyphExtents(PRUint32 aAppUnitsPerDevUnit) :
mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) {
MOZ_COUNT_CTOR(gfxGlyphExtents);
mTightGlyphExtents.Init();
}
~gfxGlyphExtents();
enum { INVALID_WIDTH = 0xFFFF };
@ -256,16 +258,12 @@ public:
// and the result is its width measured from the baseline origin, in
// appunits.
PRUint16 GetContainedGlyphWidthAppUnits(PRUint32 aGlyphID) const {
if (aGlyphID >= mWidthsForContainedGlyphsAppUnits.Length())
return INVALID_WIDTH;
return mWidthsForContainedGlyphsAppUnits[aGlyphID];
return mContainedGlyphWidths.Get(aGlyphID);
}
PRBool IsGlyphKnown(PRUint32 aGlyphID) const {
if (aGlyphID < mWidthsForContainedGlyphsAppUnits.Length() &&
mWidthsForContainedGlyphsAppUnits[aGlyphID] != INVALID_WIDTH)
return PR_TRUE;
return mTightGlyphExtents.GetEntry(aGlyphID) != nsnull;
return mContainedGlyphWidths.Get(aGlyphID) != INVALID_WIDTH ||
mTightGlyphExtents.GetEntry(aGlyphID) != nsnull;
}
PRBool IsGlyphKnownWithTightExtents(PRUint32 aGlyphID) const {
@ -275,7 +273,9 @@ public:
// Get glyph extents; a rectangle relative to the left baseline origin
gfxRect GetTightGlyphExtentsAppUnits(gfxFont *aFont, gfxContext *aContext, PRUint32 aGlyphID);
void SetContainedGlyphWidthAppUnits(PRUint32 aGlyphID, PRUint16 aWidth);
void SetContainedGlyphWidthAppUnits(PRUint32 aGlyphID, PRUint16 aWidth) {
mContainedGlyphWidths.Set(aGlyphID, aWidth);
}
void SetTightGlyphExtents(PRUint32 aGlyphID, const gfxRect& aExtentsAppUnits);
PRUint32 GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
@ -293,7 +293,52 @@ private:
float x, y, width, height;
};
nsTArray<PRUint16> mWidthsForContainedGlyphsAppUnits;
typedef unsigned long PtrBits;
enum { BLOCK_SIZE_BITS = 7, BLOCK_SIZE = 1 << BLOCK_SIZE_BITS }; // 128-glyph blocks
class GlyphWidths {
public:
void Set(PRUint32 aIndex, PRUint16 aValue);
PRUint16 Get(PRUint32 aIndex) const {
PRUint32 block = aIndex >> BLOCK_SIZE_BITS;
if (block >= mBlocks.Length())
return INVALID_WIDTH;
PtrBits bits = mBlocks[block];
if (!bits)
return INVALID_WIDTH;
PRUint32 indexInBlock = aIndex & (BLOCK_SIZE - 1);
if (bits & 0x1) {
if (GetGlyphOffset(bits) != indexInBlock)
return INVALID_WIDTH;
return GetWidth(bits);
}
PRUint16 *widths = reinterpret_cast<PRUint16 *>(bits);
return widths[indexInBlock];
}
#ifdef DEBUG
PRUint32 ComputeSize();
#endif
~GlyphWidths();
private:
static PRUint32 GetGlyphOffset(PtrBits aBits) {
NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
return (aBits >> 1) & ((1 << BLOCK_SIZE_BITS) - 1);
}
static PRUint32 GetWidth(PtrBits aBits) {
NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
return aBits >> (1 + BLOCK_SIZE_BITS);
}
static PtrBits MakeSingle(PRUint32 aGlyphOffset, PRUint16 aWidth) {
return (aWidth << (1 + BLOCK_SIZE_BITS)) + (aGlyphOffset << 1) + 1;
}
nsTArray<PtrBits> mBlocks;
};
GlyphWidths mContainedGlyphWidths;
nsTHashtable<HashEntry> mTightGlyphExtents;
PRUint32 mAppUnitsPerDevUnit;
};

View File

@ -64,6 +64,13 @@ gfxFontCache *gfxFontCache::gGlobalCache = nsnull;
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
static PRUint32 gTextRunStorageHighWaterMark = 0;
static PRUint32 gTextRunStorage = 0;
static PRUint32 gFontCount = 0;
static PRUint32 gGlyphExtentsCount = 0;
static PRUint32 gGlyphExtentsWidthsTotalSize = 0;
static PRUint32 gGlyphExtentsSetupEagerSimple = 0;
static PRUint32 gGlyphExtentsSetupEagerTight = 0;
static PRUint32 gGlyphExtentsSetupLazyTight = 0;
static PRUint32 gGlyphExtentsSetupFallBackToTight = 0;
#endif
nsresult
@ -82,6 +89,14 @@ gfxFontCache::Shutdown()
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
printf("Total number of fonts=%d\n", gFontCount);
printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
#endif
}
@ -160,6 +175,9 @@ gfxFontCache::DestroyFont(gfxFont *aFont)
gfxFont::gfxFont(const nsAString &aName, const gfxFontStyle *aFontStyle) :
mName(aName), mStyle(*aFontStyle)
{
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
++gFontCount;
#endif
}
gfxFont::~gfxFont()
@ -331,8 +349,7 @@ UnionWithXPoint(gfxRect *aRect, double aX)
static PRBool
NeedsGlyphExtents(gfxTextRun *aTextRun)
{
return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED) == 0;
// return PR_TRUE;
return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) != 0;
}
gfxFont::RunMetrics
@ -480,6 +497,11 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, PRBool aNeed
return;
}
}
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
if (!aNeedTight) {
++gGlyphExtentsSetupFallBackToTight;
}
#endif
double d2a = appUnitsPerDevUnit;
gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
@ -487,6 +509,15 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, PRBool aNeed
aExtents->SetTightGlyphExtents(aGlyphID, bounds);
}
gfxGlyphExtents::~gfxGlyphExtents()
{
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
gGlyphExtentsWidthsTotalSize += mContainedGlyphWidths.ComputeSize();
gGlyphExtentsCount++;
#endif
MOZ_COUNT_DTOR(gfxGlyphExtents);
}
gfxRect
gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
gfxContext *aContext, PRUint32 aGlyphID)
@ -494,6 +525,9 @@ gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
if (!entry) {
aFont->SetupCairoFont(aContext);
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
++gGlyphExtentsSetupLazyTight;
#endif
aFont->SetupGlyphExtents(aContext, aGlyphID, PR_TRUE, this);
entry = mTightGlyphExtents.GetEntry(aGlyphID);
if (!entry) {
@ -505,20 +539,65 @@ gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
return gfxRect(entry->x, entry->y, entry->width, entry->height);
}
void
gfxGlyphExtents::SetContainedGlyphWidthAppUnits(PRUint32 aGlyphID, PRUint16 aWidth)
gfxGlyphExtents::GlyphWidths::~GlyphWidths()
{
PRUint32 len = mWidthsForContainedGlyphsAppUnits.Length();
if (aGlyphID >= len) {
PRUint16 *elems = mWidthsForContainedGlyphsAppUnits.AppendElements(aGlyphID + 1 - len);
if (!elems)
return;
PRUint32 i;
for (i = len; i < aGlyphID; ++i) {
mWidthsForContainedGlyphsAppUnits[i] = INVALID_WIDTH;
PRUint32 i;
for (i = 0; i < mBlocks.Length(); ++i) {
PtrBits bits = mBlocks[i];
if (bits && !(bits & 0x1)) {
delete[] reinterpret_cast<PRUint16 *>(bits);
}
}
mWidthsForContainedGlyphsAppUnits[aGlyphID] = aWidth;
}
#ifdef DEBUG
PRUint32
gfxGlyphExtents::GlyphWidths::ComputeSize()
{
PRUint32 i;
PRUint32 size = mBlocks.Capacity()*sizeof(PtrBits);
for (i = 0; i < mBlocks.Length(); ++i) {
PtrBits bits = mBlocks[i];
if (bits && !(bits & 0x1)) {
size += BLOCK_SIZE*sizeof(PRUint16);
}
}
return size;
}
#endif
void
gfxGlyphExtents::GlyphWidths::Set(PRUint32 aGlyphID, PRUint16 aWidth)
{
PRUint32 block = aGlyphID >> BLOCK_SIZE_BITS;
PRUint32 len = mBlocks.Length();
if (block >= len) {
PtrBits *elems = mBlocks.AppendElements(block + 1 - len);
if (!elems)
return;
memset(elems, 0, sizeof(PtrBits)*(block + 1 - len));
}
PtrBits bits = mBlocks[block];
PRUint32 glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
if (!bits) {
mBlocks[block] = MakeSingle(glyphOffset, aWidth);
return;
}
PRUint16 *newBlock;
if (bits & 0x1) {
// Expand the block to a real block. We could avoid this by checking
// glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
newBlock = new PRUint16[BLOCK_SIZE];
if (!newBlock)
return;
newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
mBlocks[block] = reinterpret_cast<PtrBits>(newBlock);
} else {
newBlock = reinterpret_cast<PRUint16 *>(bits);
}
newBlock[glyphOffset] = aWidth;
}
void
@ -1932,6 +2011,9 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
font->SetupCairoFont(aRefContext);
fontIsSetup = PR_TRUE;
}
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
++gGlyphExtentsSetupEagerSimple;
#endif
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_FALSE, extents);
}
}
@ -1944,6 +2026,9 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
font->SetupCairoFont(aRefContext);
fontIsSetup = PR_TRUE;
}
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
++gGlyphExtentsSetupEagerTight;
#endif
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_TRUE, extents);
}
if (details->mIsLastGlyph)

View File

@ -1262,8 +1262,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
PRBool anyTextTransformStyle = PR_FALSE;
nsIContent* lastContent = nsnull;
PRInt32 endOfLastContent = 0;
PRUint32 textFlags = gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX |
nsTextFrameUtils::TEXT_NO_BREAKS;
PRUint32 textFlags = nsTextFrameUtils::TEXT_NO_BREAKS;
if (mCurrentRunTrimLeadingWhitespace) {
textFlags |= nsTextFrameUtils::TEXT_INCOMING_WHITESPACE;
@ -1466,6 +1465,11 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
// frame's style is used, so use the last frame's
textFlags |= nsLayoutUtils::GetTextRunFlagsForStyle(lastStyleContext,
textStyle, fontStyle);
// XXX this is a bit of a hack. For performance reasons, if we're favouring
// performance over quality, don't try to get accurate glyph extents.
if (!(textFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED)) {
textFlags |= gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX;
}
gfxSkipChars skipChars;
skipChars.TakeFrom(&builder);

View File

@ -1340,8 +1340,8 @@ nsSVGGlyphFrame::GetTextRun(gfxContext *aCtx, const nsString &aText)
if (!mFontGroup)
return nsnull;
PRUint32 flags = nsLayoutUtils::GetTextRunFlagsForStyle(GetStyleContext(),
GetStyleText(), GetStyleFont());
PRUint32 flags = gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX |
nsLayoutUtils::GetTextRunFlagsForStyle(GetStyleContext(), GetStyleText(), GetStyleFont());
return gfxTextRunCache::MakeTextRun(aText.get(), aText.Length(),
mFontGroup, aCtx, 1, flags);
}