mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 703100 - pt 4 - add timed expiration of cached gfxShapedWord records. r=roc
This commit is contained in:
parent
d5a2294830
commit
39b0dea87f
@ -48,6 +48,7 @@
|
|||||||
#include "nsExpirationTracker.h"
|
#include "nsExpirationTracker.h"
|
||||||
#include "nsILanguageAtomService.h"
|
#include "nsILanguageAtomService.h"
|
||||||
#include "nsIMemoryReporter.h"
|
#include "nsIMemoryReporter.h"
|
||||||
|
#include "nsITimer.h"
|
||||||
|
|
||||||
#include "gfxFont.h"
|
#include "gfxFont.h"
|
||||||
#include "gfxPlatform.h"
|
#include "gfxPlatform.h"
|
||||||
@ -981,6 +982,36 @@ gfxFontCache::Shutdown()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gfxFontCache::gfxFontCache()
|
||||||
|
: nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
|
||||||
|
{
|
||||||
|
mFonts.Init();
|
||||||
|
mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||||
|
if (mWordCacheExpirationTimer) {
|
||||||
|
mWordCacheExpirationTimer->
|
||||||
|
InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
|
||||||
|
SHAPED_WORD_TIMEOUT_SECONDS * 1000,
|
||||||
|
nsITimer::TYPE_REPEATING_SLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gfxFontCache::~gfxFontCache()
|
||||||
|
{
|
||||||
|
if (mWordCacheExpirationTimer) {
|
||||||
|
mWordCacheExpirationTimer->Cancel();
|
||||||
|
mWordCacheExpirationTimer = nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire everything that has a zero refcount, so we don't leak them.
|
||||||
|
AgeAllGenerations();
|
||||||
|
// All fonts should be gone.
|
||||||
|
NS_WARN_IF_FALSE(mFonts.Count() == 0,
|
||||||
|
"Fonts still alive while shutting down gfxFontCache");
|
||||||
|
// Note that we have to delete everything through the expiration
|
||||||
|
// tracker, since there might be fonts not in the hashtable but in
|
||||||
|
// the tracker.
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
|
gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
|
||||||
{
|
{
|
||||||
@ -1055,6 +1086,22 @@ gfxFontCache::DestroyFont(gfxFont *aFont)
|
|||||||
delete aFont;
|
delete aFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*static*/
|
||||||
|
PLDHashOperator
|
||||||
|
gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
|
||||||
|
{
|
||||||
|
aHashEntry->mFont->AgeCachedWords();
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/
|
||||||
|
void
|
||||||
|
gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
|
||||||
|
{
|
||||||
|
gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
|
||||||
|
cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
|
gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
|
||||||
{
|
{
|
||||||
@ -1096,6 +1143,20 @@ gfxFont::~gfxFont()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*static*/
|
||||||
|
PLDHashOperator
|
||||||
|
gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
|
||||||
|
{
|
||||||
|
if (!aEntry->mShapedWord) {
|
||||||
|
NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
|
||||||
|
return PL_DHASH_REMOVE;
|
||||||
|
}
|
||||||
|
if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
|
||||||
|
return PL_DHASH_REMOVE;
|
||||||
|
}
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
hb_blob_t *
|
hb_blob_t *
|
||||||
gfxFont::GetFontTable(PRUint32 aTag) {
|
gfxFont::GetFontTable(PRUint32 aTag) {
|
||||||
hb_blob_t *blob;
|
hb_blob_t *blob;
|
||||||
@ -1767,40 +1828,43 @@ gfxFont::GetShapedWord(gfxContext *aContext,
|
|||||||
aFlags);
|
aFlags);
|
||||||
|
|
||||||
CacheHashEntry *entry = mWordCache.PutEntry(key);
|
CacheHashEntry *entry = mWordCache.PutEntry(key);
|
||||||
if (entry->mShapedWord) {
|
gfxShapedWord *sw = entry->mShapedWord;
|
||||||
return entry->mShapedWord;
|
if (sw) {
|
||||||
|
sw->ResetAge();
|
||||||
|
return sw;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
|
sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
|
||||||
aRunScript,
|
aRunScript,
|
||||||
aAppUnitsPerDevUnit,
|
aAppUnitsPerDevUnit,
|
||||||
aFlags);
|
aFlags);
|
||||||
if (!entry->mShapedWord) {
|
NS_ASSERTION(sw != nsnull,
|
||||||
NS_WARNING("failed to create gfxShapedWord - expect missing text");
|
"failed to create gfxShapedWord - expect missing text");
|
||||||
|
if (!sw) {
|
||||||
return nsnull;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok;
|
bool ok;
|
||||||
if (sizeof(T) == sizeof(PRUnichar)) {
|
if (sizeof(T) == sizeof(PRUnichar)) {
|
||||||
ok = ShapeWord(aContext, entry->mShapedWord, (const PRUnichar*)aText);
|
ok = ShapeWord(aContext, sw, (const PRUnichar*)aText);
|
||||||
} else {
|
} else {
|
||||||
nsAutoString utf16;
|
nsAutoString utf16;
|
||||||
AppendASCIItoUTF16((const char*)aText, utf16);
|
AppendASCIItoUTF16((const char*)aText, utf16);
|
||||||
ok = ShapeWord(aContext, entry->mShapedWord, utf16.BeginReading());
|
ok = ShapeWord(aContext, sw, utf16.BeginReading());
|
||||||
}
|
}
|
||||||
NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
|
NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
|
||||||
|
|
||||||
for (PRUint32 i = 0; i < aLength; ++i) {
|
for (PRUint32 i = 0; i < aLength; ++i) {
|
||||||
if (aText[i] == ' ') {
|
if (aText[i] == ' ') {
|
||||||
entry->mShapedWord->SetIsSpace(i);
|
sw->SetIsSpace(i);
|
||||||
} else if (i > 0 &&
|
} else if (i > 0 &&
|
||||||
NS_IS_HIGH_SURROGATE(aText[i - 1]) &&
|
NS_IS_HIGH_SURROGATE(aText[i - 1]) &&
|
||||||
NS_IS_LOW_SURROGATE(aText[i])) {
|
NS_IS_LOW_SURROGATE(aText[i])) {
|
||||||
entry->mShapedWord->SetIsLowSurrogate(i);
|
sw->SetIsLowSurrogate(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry->mShapedWord;
|
return sw;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -671,22 +671,27 @@ struct gfxTextRange {
|
|||||||
* We're using 3 generations with a ten-second generation interval, so
|
* We're using 3 generations with a ten-second generation interval, so
|
||||||
* zero-refcount fonts will be deleted 20-30 seconds after their refcount
|
* zero-refcount fonts will be deleted 20-30 seconds after their refcount
|
||||||
* goes to zero, if timer events fire in a timely manner.
|
* goes to zero, if timer events fire in a timely manner.
|
||||||
|
*
|
||||||
|
* The font cache also handles timed expiration of cached ShapedWords
|
||||||
|
* for "persistent" fonts: it has a repeating timer, and notifies
|
||||||
|
* each cached font to "age" its shaped words. The words will be released
|
||||||
|
* by the fonts if they get aged three times without being re-used in the
|
||||||
|
* meantime.
|
||||||
|
*
|
||||||
|
* Note that the ShapedWord timeout is much larger than the font timeout,
|
||||||
|
* so that in the case of a short-lived font, we'll discard the gfxFont
|
||||||
|
* completely, with all its words, and avoid the cost of aging the words
|
||||||
|
* individually. That only happens with longer-lived fonts.
|
||||||
*/
|
*/
|
||||||
class THEBES_API gfxFontCache : public nsExpirationTracker<gfxFont,3> {
|
class THEBES_API gfxFontCache : public nsExpirationTracker<gfxFont,3> {
|
||||||
public:
|
public:
|
||||||
enum { TIMEOUT_SECONDS = 10 };
|
enum {
|
||||||
gfxFontCache()
|
FONT_TIMEOUT_SECONDS = 10,
|
||||||
: nsExpirationTracker<gfxFont,3>(TIMEOUT_SECONDS*1000) { mFonts.Init(); }
|
SHAPED_WORD_TIMEOUT_SECONDS = 60
|
||||||
~gfxFontCache() {
|
};
|
||||||
// Expire everything that has a zero refcount, so we don't leak them.
|
|
||||||
AgeAllGenerations();
|
gfxFontCache();
|
||||||
// All fonts should be gone.
|
~gfxFontCache();
|
||||||
NS_WARN_IF_FALSE(mFonts.Count() == 0,
|
|
||||||
"Fonts still alive while shutting down gfxFontCache");
|
|
||||||
// Note that we have to delete everything through the expiration
|
|
||||||
// tracker, since there might be fonts not in the hashtable but in
|
|
||||||
// the tracker.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the global gfxFontCache. You must call Init() before
|
* Get the global gfxFontCache. You must call Init() before
|
||||||
@ -703,7 +708,7 @@ public:
|
|||||||
// Look up a font in the cache. Returns an addrefed pointer, or null
|
// Look up a font in the cache. Returns an addrefed pointer, or null
|
||||||
// if there's nothing matching in the cache
|
// if there's nothing matching in the cache
|
||||||
already_AddRefed<gfxFont> Lookup(const gfxFontEntry *aFontEntry,
|
already_AddRefed<gfxFont> Lookup(const gfxFontEntry *aFontEntry,
|
||||||
const gfxFontStyle *aFontGroup);
|
const gfxFontStyle *aStyle);
|
||||||
// We created a new font (presumably because Lookup returned null);
|
// We created a new font (presumably because Lookup returned null);
|
||||||
// put it in the cache. The font's refcount should be nonzero. It is
|
// put it in the cache. The font's refcount should be nonzero. It is
|
||||||
// allowable to add a new font even if there is one already in the
|
// allowable to add a new font even if there is one already in the
|
||||||
@ -761,6 +766,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
nsTHashtable<HashEntry> mFonts;
|
nsTHashtable<HashEntry> mFonts;
|
||||||
|
|
||||||
|
static PLDHashOperator AgeCachedWordsForFont(HashEntry* aHashEntry, void*);
|
||||||
|
static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache);
|
||||||
|
nsCOMPtr<nsITimer> mWordCacheExpirationTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
class THEBES_API gfxTextRunFactory {
|
class THEBES_API gfxTextRunFactory {
|
||||||
@ -1386,6 +1395,14 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called by the gfxFontCache timer to increment the age of all the words,
|
||||||
|
// so that they'll expire after a sufficient period of non-use
|
||||||
|
void AgeCachedWords() {
|
||||||
|
if (mWordCache.IsInitialized()) {
|
||||||
|
(void)mWordCache.EnumerateEntries(AgeCacheEntry, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Call the appropriate shaper to generate glyphs for aText and store
|
// Call the appropriate shaper to generate glyphs for aText and store
|
||||||
// them into aShapedWord.
|
// them into aShapedWord.
|
||||||
@ -1472,6 +1489,9 @@ protected:
|
|||||||
|
|
||||||
nsTHashtable<CacheHashEntry> mWordCache;
|
nsTHashtable<CacheHashEntry> mWordCache;
|
||||||
|
|
||||||
|
static PLDHashOperator AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData);
|
||||||
|
static const PRUint32 kShapedWordCacheMaxAge = 3;
|
||||||
|
|
||||||
bool mIsValid;
|
bool mIsValid;
|
||||||
|
|
||||||
// use synthetic bolding for environments where this is not supported
|
// use synthetic bolding for environments where this is not supported
|
||||||
@ -1891,6 +1911,13 @@ public:
|
|||||||
return mAppUnitsPerDevUnit;
|
return mAppUnitsPerDevUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetAge() {
|
||||||
|
mAgeCounter = 0;
|
||||||
|
}
|
||||||
|
PRUint32 IncrementAge() {
|
||||||
|
return ++mAgeCounter;
|
||||||
|
}
|
||||||
|
|
||||||
void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
|
void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
|
||||||
NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
|
NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
|
||||||
if (mCharacterGlyphs) {
|
if (mCharacterGlyphs) {
|
||||||
@ -1954,6 +1981,7 @@ private:
|
|||||||
, mFlags(aFlags | gfxTextRunFactory::TEXT_IS_8BIT)
|
, mFlags(aFlags | gfxTextRunFactory::TEXT_IS_8BIT)
|
||||||
, mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
|
, mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
|
||||||
, mScript(aRunScript)
|
, mScript(aRunScript)
|
||||||
|
, mAgeCounter(0)
|
||||||
{
|
{
|
||||||
memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
|
memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
|
||||||
PRUint8 *text = reinterpret_cast<PRUint8*>(&mCharacterGlyphs[aLength]);
|
PRUint8 *text = reinterpret_cast<PRUint8*>(&mCharacterGlyphs[aLength]);
|
||||||
@ -1967,6 +1995,7 @@ private:
|
|||||||
, mFlags(aFlags)
|
, mFlags(aFlags)
|
||||||
, mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
|
, mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
|
||||||
, mScript(aRunScript)
|
, mScript(aRunScript)
|
||||||
|
, mAgeCounter(0)
|
||||||
{
|
{
|
||||||
memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
|
memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
|
||||||
PRUnichar *text = reinterpret_cast<PRUnichar*>(&mCharacterGlyphs[aLength]);
|
PRUnichar *text = reinterpret_cast<PRUnichar*>(&mCharacterGlyphs[aLength]);
|
||||||
@ -2111,6 +2140,8 @@ private:
|
|||||||
PRInt32 mAppUnitsPerDevUnit;
|
PRInt32 mAppUnitsPerDevUnit;
|
||||||
PRInt32 mScript;
|
PRInt32 mScript;
|
||||||
|
|
||||||
|
PRUint32 mAgeCounter;
|
||||||
|
|
||||||
// The mCharacterGlyphs array is actually a variable-size member;
|
// The mCharacterGlyphs array is actually a variable-size member;
|
||||||
// when the ShapedWord is created, its size will be increased as necessary
|
// when the ShapedWord is created, its size will be increased as necessary
|
||||||
// to allow the proper number of glyphs to be stored.
|
// to allow the proper number of glyphs to be stored.
|
||||||
|
Loading…
Reference in New Issue
Block a user