Bug 934710 - add various text performance metrics and a log to handle these. r=jfkthame

This commit is contained in:
John Daggett 2013-11-25 13:59:56 +09:00
parent 146de18587
commit 11931c9dfc
16 changed files with 200 additions and 25 deletions

View File

@ -2208,11 +2208,13 @@ CanvasRenderingContext2D::SetFont(const nsAString& font,
fontStyle->mFont.AddFontFeaturesToStyle(&style);
nsPresContext *c = presShell->GetPresContext();
CurrentState().fontGroup =
gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
&style,
presShell->GetPresContext()->GetUserFontSet());
c->GetUserFontSet());
NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
CurrentState().fontGroup->SetTextPerfMetrics(c->GetTextPerfMetrics());
// The font getter is required to be reserialized based on what we
// parsed (including having line-height removed). (Older drafts of
@ -2806,6 +2808,12 @@ gfxFontGroup *CanvasRenderingContext2D::GetCurrentFontStyle()
nullptr);
if (CurrentState().fontGroup) {
CurrentState().font = kDefaultFontStyle;
nsIPresShell* presShell = GetPresShell();
if (presShell) {
CurrentState().fontGroup->SetTextPerfMetrics(
presShell->GetPresContext()->GetTextPerfMetrics());
}
} else {
NS_ERROR("Default canvas font is invalid");
}

View File

@ -66,6 +66,7 @@ public:
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics);
void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
@ -123,6 +124,7 @@ nsFontCache::Observe(nsISupports*, const char* aTopic, const PRUnichar*)
nsresult
nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics)
{
if (!aLanguage)
@ -152,7 +154,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
fm = new nsFontMetrics();
NS_ADDREF(fm);
nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet);
nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
if (NS_SUCCEEDED(rv)) {
// the mFontMetrics list has the "head" at the end, because append
// is cheaper than insert
@ -171,7 +173,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
Compact();
fm = new nsFontMetrics();
NS_ADDREF(fm);
rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet);
rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
if (NS_SUCCEEDED(rv)) {
mFontMetrics.AppendElement(fm);
aMetrics = fm;
@ -260,6 +262,7 @@ nsresult
nsDeviceContext::GetMetricsFor(const nsFont& aFont,
nsIAtom* aLanguage,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics)
{
if (!mFontCache) {
@ -268,7 +271,8 @@ nsDeviceContext::GetMetricsFor(const nsFont& aFont,
mFontCache->Init(this);
}
return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet, aMetrics);
return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet,
aTextPerf, aMetrics);
}
nsresult

View File

@ -20,6 +20,7 @@
#include "mozilla/AppUnits.h" // for AppUnits
class gfxASurface;
class gfxTextPerfMetrics;
class gfxUserFontSet;
class nsFont;
class nsFontCache;
@ -119,6 +120,7 @@ public:
*/
nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
gfxUserFontSet* aUserFontSet,
gfxTextPerfMetrics* aTextPerf,
nsFontMetrics*& aMetrics);
/**

View File

@ -96,7 +96,8 @@ nsFontMetrics::~nsFontMetrics()
nsresult
nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
nsDeviceContext *aContext,
gfxUserFontSet *aUserFontSet)
gfxUserFontSet *aUserFontSet,
gfxTextPerfMetrics *aTextPerf)
{
NS_ABORT_IF_FALSE(mP2A == 0, "already initialized");
@ -119,6 +120,7 @@ nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
mFontGroup = gfxPlatform::GetPlatform()->
CreateFontGroup(aFont.name, &style, aUserFontSet);
mFontGroup->SetTextPerfMetrics(aTextPerf);
if (mFontGroup->FontListLength() < 1)
return NS_ERROR_UNEXPECTED;

View File

@ -19,6 +19,7 @@
#include "nscore.h" // for PRUnichar
class gfxUserFontSet;
class gfxTextPerfMetrics;
class nsDeviceContext;
class nsIAtom;
class nsRenderingContext;
@ -58,7 +59,8 @@ public:
*/
nsresult Init(const nsFont& aFont, nsIAtom* aLanguage,
nsDeviceContext *aContext,
gfxUserFontSet *aUserFontSet = nullptr);
gfxUserFontSet *aUserFontSet,
gfxTextPerfMetrics *aTextPerf);
/**
* Destroy this font metrics. This breaks the association between

View File

@ -3083,6 +3083,12 @@ HashMix(uint32_t aHash, PRUnichar aCh)
return (aHash >> 28) ^ (aHash << 4) ^ aCh;
}
#ifdef __GNUC__
#define GFX_MAYBE_UNUSED __attribute__((unused))
#else
#define GFX_MAYBE_UNUSED
#endif
template<typename T>
gfxShapedWord*
gfxFont::GetShapedWord(gfxContext *aContext,
@ -3091,7 +3097,8 @@ gfxFont::GetShapedWord(gfxContext *aContext,
uint32_t aHash,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags)
uint32_t aFlags,
gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
{
// if the cache is getting too big, flush it and start over
uint32_t wordCacheMaxEntries =
@ -3121,12 +3128,23 @@ gfxFont::GetShapedWord(gfxContext *aContext,
Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT :
Telemetry::WORD_CACHE_HITS_CHROME),
aLength);
#ifndef RELEASE_BUILD
if (aTextPerf) {
aTextPerf->current.wordCacheHit++;
}
#endif
return sw;
}
Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT :
Telemetry::WORD_CACHE_MISSES_CHROME),
aLength);
#ifndef RELEASE_BUILD
if (aTextPerf) {
aTextPerf->current.wordCacheMiss++;
}
#endif
sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
aRunScript,
aAppUnitsPerDevUnit,
@ -3371,6 +3389,12 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
return ok;
}
#ifndef RELEASE_BUILD
#define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
#else
#define TEXT_PERF_INCR(tp, m)
#endif
inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
inline static bool IsChar8Bit(PRUnichar aCh) { return aCh < 0x100; }
@ -3387,7 +3411,25 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
return true;
}
gfxTextPerfMetrics *tp = nullptr;
#ifndef RELEASE_BUILD
tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
if (tp) {
if (mStyle.systemFont) {
tp->current.numChromeTextRuns++;
} else {
tp->current.numContentTextRuns++;
}
tp->current.numChars += aRunLength;
if (aRunLength > tp->current.maxTextRunLen) {
tp->current.maxTextRunLen = aRunLength;
}
}
#endif
if (BypassShapedWordCache(aRunScript)) {
TEXT_PERF_INCR(tp, wordCacheSpaceRules);
return ShapeTextWithoutWordCache(aContext, aString + aRunStart,
aRunStart, aRunLength, aRunScript,
aTextRun);
@ -3436,6 +3478,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
// For words longer than the limit, we don't use the
// font's word cache but just shape directly into the textrun.
if (length > wordCacheCharLimit) {
TEXT_PERF_INCR(tp, wordCacheLong);
bool ok = ShapeFragmentWithoutWordCache(aContext,
text + wordStart,
aRunStart + wordStart,
@ -3459,7 +3502,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
text + wordStart, length,
hash, aRunScript,
appUnitsPerDevUnit,
wordFlags);
wordFlags, tp);
if (sw) {
aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
} else {
@ -3478,7 +3521,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
&space, 1,
HashMix(0, ' '), aRunScript,
appUnitsPerDevUnit,
flags | gfxTextRunFactory::TEXT_IS_8BIT);
flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
if (sw) {
aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
} else {
@ -4017,6 +4060,7 @@ gfxFontGroup::gfxFontGroup(const nsAString& aFamilies,
, mStyle(*aStyle)
, mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
, mHyphenWidth(-1)
, mTextPerf(nullptr)
{
mUserFontSet = nullptr;
SetUserFontSet(aUserFontSet);
@ -4177,7 +4221,9 @@ gfxFontGroup::~gfxFontGroup()
gfxFontGroup *
gfxFontGroup::Copy(const gfxFontStyle *aStyle)
{
return new gfxFontGroup(mFamilies, aStyle, mUserFontSet);
gfxFontGroup *fg = new gfxFontGroup(mFamilies, aStyle, mUserFontSet);
fg->SetTextPerfMetrics(mTextPerf);
return fg;
}
bool
@ -5013,6 +5059,16 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
nsRefPtr<gfxFont> font =
FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
#ifndef RELEASE_BUILD
if (MOZ_UNLIKELY(mTextPerf)) {
if (matchType == gfxTextRange::kPrefsFallback) {
mTextPerf->current.fallbackPrefs++;
} else if (matchType == gfxTextRange::kSystemFallback) {
mTextPerf->current.fallbackSystem++;
}
}
#endif
prevCh = ch;
if (lastRangeIndex == -1) {
@ -5569,6 +5625,13 @@ gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
MOZ_COUNT_CTOR(gfxTextRun);
NS_ADDREF(mFontGroup);
#ifndef RELEASE_BUILD
gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
if (tp) {
tp->current.textrunConst++;
}
#endif
mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
if (aParams->mSkipChars) {
@ -5596,6 +5659,12 @@ gfxTextRun::~gfxTextRun()
// been told to release its reference to the group, so we mustn't do that
// again here.
if (!mReleasedFontGroup) {
#ifndef RELEASE_BUILD
gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
if (tp) {
tp->current.textrunDestr++;
}
#endif
NS_RELEASE(mFontGroup);
}
@ -6648,7 +6717,8 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
mAppUnitsPerDevUnit,
gfxTextRunFactory::TEXT_IS_8BIT |
gfxTextRunFactory::TEXT_IS_ASCII |
gfxTextRunFactory::TEXT_IS_PERSISTENT);
gfxTextRunFactory::TEXT_IS_PERSISTENT,
nullptr);
if (sw) {
AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
CopyGlyphDataFrom(sw, aCharIndex);

View File

@ -1014,6 +1014,59 @@ protected:
nsCOMPtr<nsITimer> mWordCacheExpirationTimer;
};
class gfxTextPerfMetrics {
public:
struct TextCounts {
uint32_t numContentTextRuns;
uint32_t numChromeTextRuns;
uint32_t numChars;
uint32_t maxTextRunLen;
uint32_t wordCacheSpaceRules;
uint32_t wordCacheLong;
uint32_t wordCacheHit;
uint32_t wordCacheMiss;
uint32_t fallbackPrefs;
uint32_t fallbackSystem;
uint32_t textrunConst;
uint32_t textrunDestr;
};
uint32_t reflowCount;
// counts per reflow operation
TextCounts current;
// totals for the lifetime of a document
TextCounts cumulative;
gfxTextPerfMetrics() {
memset(this, 0, sizeof(gfxTextPerfMetrics));
}
// add current totals to cumulative ones
void Accumulate() {
if (current.numChars == 0) {
return;
}
cumulative.numContentTextRuns += current.numContentTextRuns;
cumulative.numChromeTextRuns += current.numChromeTextRuns;
cumulative.numChars += current.numChars;
if (current.maxTextRunLen > cumulative.maxTextRunLen) {
cumulative.maxTextRunLen = current.maxTextRunLen;
}
cumulative.wordCacheSpaceRules += current.wordCacheSpaceRules;
cumulative.wordCacheLong += current.wordCacheLong;
cumulative.wordCacheHit += current.wordCacheHit;
cumulative.wordCacheMiss += current.wordCacheMiss;
cumulative.fallbackPrefs += current.fallbackPrefs;
cumulative.fallbackSystem += current.fallbackSystem;
cumulative.textrunConst += current.textrunConst;
cumulative.textrunDestr += current.textrunDestr;
memset(&current, 0, sizeof(current));
}
};
class gfxTextRunFactory {
NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
@ -1645,7 +1698,8 @@ public:
uint32_t aHash,
int32_t aRunScript,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags);
uint32_t aFlags,
gfxTextPerfMetrics *aTextPerf);
// Ensure the ShapedWord cache is initialized. This MUST be called before
// any attempt to use GetShapedWord().
@ -3456,6 +3510,10 @@ public:
// with no @font-face rule, this always returns 0.
uint64_t GetGeneration();
// used when logging text performance
gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; }
void SetTextPerfMetrics(gfxTextPerfMetrics *aTextPerf) { mTextPerf = aTextPerf; }
// If there is a user font set, check to see whether the font list or any
// caches need updating.
virtual void UpdateFontList();
@ -3486,6 +3544,8 @@ protected:
gfxUserFontSet* mUserFontSet;
uint64_t mCurrGeneration; // track the current user font set generation, rebuild font list if needed
gfxTextPerfMetrics *mTextPerf;
// Cache a textrun representing an ellipsis (useful for CSS text-overflow)
// at a specific appUnitsPerDevPixel size
nsAutoPtr<gfxTextRun> mCachedEllipsisTextRun;

View File

@ -120,6 +120,7 @@ static PRLogModuleInfo *sFontInitLog = nullptr;
static PRLogModuleInfo *sTextrunLog = nullptr;
static PRLogModuleInfo *sTextrunuiLog = nullptr;
static PRLogModuleInfo *sCmapDataLog = nullptr;
static PRLogModuleInfo *sTextPerfLog = nullptr;
#endif
/* Class to listen for pref changes so that chrome code can dynamically
@ -364,11 +365,12 @@ gfxPlatform::Init()
gEverInitialized = true;
#ifdef PR_LOGGING
sFontlistLog = PR_NewLogModule("fontlist");;
sFontInitLog = PR_NewLogModule("fontinit");;
sTextrunLog = PR_NewLogModule("textrun");;
sTextrunuiLog = PR_NewLogModule("textrunui");;
sCmapDataLog = PR_NewLogModule("cmapdata");;
sFontlistLog = PR_NewLogModule("fontlist");
sFontInitLog = PR_NewLogModule("fontinit");
sTextrunLog = PR_NewLogModule("textrun");
sTextrunuiLog = PR_NewLogModule("textrunui");
sCmapDataLog = PR_NewLogModule("cmapdata");
sTextPerfLog = PR_NewLogModule("textperf");
#endif
gGfxPlatformPrefsLock = new Mutex("gfxPlatform::gGfxPlatformPrefsLock");
@ -1945,6 +1947,9 @@ gfxPlatform::GetLog(eGfxLog aWhichLog)
case eGfxLog_cmapdata:
return sCmapDataLog;
break;
case eGfxLog_textperf:
return sTextPerfLog;
break;
default:
break;
}

View File

@ -117,7 +117,9 @@ enum eGfxLog {
// dump text runs, font matching, system fallback for chrome
eGfxLog_textrunui = 3,
// dump cmap coverage data as they are loaded
eGfxLog_cmapdata = 4
eGfxLog_cmapdata = 4,
// text perf data
eGfxLog_textperf = 5
};
// when searching through pref langs, max number of pref langs

View File

@ -2599,7 +2599,9 @@ nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext,
float aInflation)
{
// pass the user font set object into the device context to pass along to CreateFontGroup
gfxUserFontSet* fs = aStyleContext->PresContext()->GetUserFontSet();
nsPresContext* pc = aStyleContext->PresContext();
gfxUserFontSet* fs = pc->GetUserFontSet();
gfxTextPerfMetrics* tp = pc->GetTextPerfMetrics();
nsFont font = aStyleContext->StyleFont()->mFont;
// We need to not run font.size through floats when it's large since
@ -2608,9 +2610,9 @@ nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext,
if (aInflation != 1.0f) {
font.size = NSToCoordRound(font.size * aInflation);
}
return aStyleContext->PresContext()->DeviceContext()->GetMetricsFor(
return pc->DeviceContext()->GetMetricsFor(
font, aStyleContext->StyleFont()->mLanguage,
fs, *aFontMetrics);
fs, tp, *aFontMetrics);
}
nsIFrame*

View File

@ -235,6 +235,12 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
mUserFontSet = nullptr;
mUserFontSetDirty = true;
// if text perf logging enabled, init stats struct
PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf);
if (log && PR_LOG_TEST(log, PR_LOG_WARNING)) {
mTextPerf = new gfxTextPerfMetrics();
}
PR_INIT_CLIST(&mDOMMediaQueryLists);
}

View File

@ -56,6 +56,7 @@ struct nsStyleBackground;
struct nsStyleBorder;
class nsIRunnable;
class gfxUserFontSet;
class gfxTextPerfMetrics;
class nsUserFontSet;
struct nsFontFaceRuleContainer;
class nsObjectFrame;
@ -788,6 +789,8 @@ public:
*/
const nscoord* GetBorderWidthTable() { return mBorderWidthTable; }
gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; }
bool IsDynamic() { return (mType == eContext_PageLayout || mType == eContext_Galley); }
bool IsScreen() { return (mMedium == nsGkAtoms::screen ||
mType == eContext_PageLayout ||
@ -1194,6 +1197,9 @@ protected:
// container for per-context fonts (downloadable, SVG, etc.)
nsUserFontSet* mUserFontSet;
// text performance metrics
nsAutoPtr<gfxTextPerfMetrics> mTextPerf;
nsRect mVisibleArea;
nsSize mPageSize;
float mPageScale;

View File

@ -9141,7 +9141,9 @@ void ReflowCountMgr::PaintCount(const char* aName,
// We have one frame, therefore we must have a root...
aPresContext->GetPresShell()->GetRootFrame()->
StyleFont()->mLanguage,
aPresContext->GetUserFontSet(), *getter_AddRefs(fm));
aPresContext->GetUserFontSet(),
aPresContext->GetTextPerfMetrics(),
*getter_AddRefs(fm));
aRenderingContext->SetFont(fm);
char buf[16];

View File

@ -575,6 +575,7 @@ nsPageFrame::PaintHeaderFooter(nsRenderingContext& aRenderingContext,
nsRefPtr<nsFontMetrics> fontMet;
pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, nullptr,
pc->GetUserFontSet(),
pc->GetTextPerfMetrics(),
*getter_AddRefs(fontMet));
aRenderingContext.SetFont(fontMet);

View File

@ -924,6 +924,7 @@ SetFontFamily(nsStyleContext* aStyleContext,
aRenderingContext.DeviceContext()->GetMetricsFor(font,
aStyleContext->StyleFont()->mLanguage,
aStyleContext->PresContext()->GetUserFontSet(),
aStyleContext->PresContext()->GetTextPerfMetrics(),
*getter_AddRefs(fm));
// Set the font if it is an unicode table
// or if the same family name has been found
@ -1312,7 +1313,8 @@ nsMathMLChar::StretchInternal(nsPresContext* aPresContext,
nsRefPtr<nsFontMetrics> fm;
aRenderingContext.DeviceContext()->GetMetricsFor(font,
mStyleContext->StyleFont()->mLanguage,
aPresContext->GetUserFontSet(), *getter_AddRefs(fm));
aPresContext->GetUserFontSet(),
aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm));
aRenderingContext.SetFont(fm);
aDesiredStretchSize =
aRenderingContext.GetBoundingMetrics(mData.get(), uint32_t(mData.Length()));
@ -1868,7 +1870,7 @@ nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
nsRefPtr<nsFontMetrics> fm;
aRenderingContext.DeviceContext()->GetMetricsFor(theFont,
styleContext->StyleFont()->mLanguage,
aPresContext->GetUserFontSet(),
aPresContext->GetUserFontSet(), aPresContext->GetTextPerfMetrics(),
*getter_AddRefs(fm));
aRenderingContext.SetFont(fm);

View File

@ -221,10 +221,11 @@ GetMetricsFor(nsPresContext* aPresContext,
if (aUseUserFontSet) {
fs = aPresContext->GetUserFontSet();
}
gfxTextPerfMetrics *tp = aPresContext->GetTextPerfMetrics();
nsRefPtr<nsFontMetrics> fm;
aPresContext->DeviceContext()->GetMetricsFor(font,
aStyleFont->mLanguage,
fs, *getter_AddRefs(fm));
fs, tp, *getter_AddRefs(fm));
return fm.forget();
}