commit b23aa8a7ae3481a9fc759e7afcb2c853dfc568cd
parent d852361059f6ab860189145cedef6d5d41783a78
Author: Jonathan Kew <jkew@mozilla.com>
Date: Thu, 13 Nov 2025 21:21:19 +0000
Bug 1999608 - Optimize face management in user font families with many faces. r=gfx-reviewers,lsalzman
Two things we can do to improve performance when adding large numbers of faces
to a user font family:
(a) Append to the array of faces, rather than inserting at the beginning.
To maintain unchanged font matching behavior, we will then search from the
end of the array in FindAllFontsForStyle, so that the most recently-added
face takes precedence.
(b) Don't bother to search for an existing copy of the face in AddFontEntry;
this is expensive. Duplication is likely to be rare, and would be harmless
as FindAllFontsForStyle will end up filtering them.
Differential Revision: https://phabricator.services.mozilla.com/D272436
Diffstat:
2 files changed, 31 insertions(+), 21 deletions(-)
diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp
@@ -1503,8 +1503,8 @@ size_t gfxFontEntry::ComputedSizeOfExcludingThis(
//
//////////////////////////////////////////////////////////////////////////////
-// we consider faces with mStandardFace == true to be "less than" those with
-// false, because during style matching, earlier entries are tried first
+// We consider faces with mStandardFace == true to be "greater than" those with
+// false, because during style matching, later entries are preferred.
class FontEntryStandardFaceComparator {
public:
bool Equals(const RefPtr<gfxFontEntry>& a,
@@ -1513,7 +1513,7 @@ class FontEntryStandardFaceComparator {
}
bool LessThan(const RefPtr<gfxFontEntry>& a,
const RefPtr<gfxFontEntry>& b) const {
- return (a->mStandardFace == true && b->mStandardFace == false);
+ return (a->mStandardFace == false && b->mStandardFace == true);
}
};
@@ -1650,10 +1650,11 @@ void gfxFontFamily::FindAllFontsForStyle(
double minDistance = INFINITY;
gfxFontEntry* matched = nullptr;
- // iterate in forward order so that faces like 'Bold' are matched before
- // matching style distance faces such as 'Bold Outline' (see bug 1185812)
- for (uint32_t i = 0; i < count; i++) {
- fe = mAvailableFonts[i];
+ // Iterate in reverse order so that faces like 'Bold' are matched before
+ // matching style-distance faces such as 'Bold Outline' (see bug 1185812;
+ // note that faces are sorted with "standard" faces later in the list.
+ for (uint32_t i = count; i > 0;) {
+ fe = mAvailableFonts[--i];
// weight/style/stretch priority: stretch >> style >> weight
double distance = WeightStyleStretchDistance(fe, aFontStyle);
if (distance < minDistance) {
@@ -1663,7 +1664,7 @@ void gfxFontFamily::FindAllFontsForStyle(
}
minDistance = distance;
} else if (distance == minDistance) {
- if (matched) {
+ if (matched && matched != fe) {
aFontEntryList.AppendElement(matched);
}
matched = fe;
@@ -1860,9 +1861,9 @@ void gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch* aMatchData) {
if (!mFamilyCharacterMap.test(aMatchData->mCh)) {
return;
}
- uint32_t i, numFonts = mAvailableFonts.Length();
- for (i = 0; i < numFonts; i++) {
- gfxFontEntry* fe = mAvailableFonts[i];
+ uint32_t numFonts = mAvailableFonts.Length();
+ for (uint32_t i = numFonts; i > 0;) {
+ gfxFontEntry* fe = mAvailableFonts[--i];
if (fe && fe->HasCharacter(aMatchData->mCh)) {
float distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
if (aMatchData->mPresentation != FontPresentation::Any) {
@@ -2156,8 +2157,8 @@ gfxFontEntry* gfxFontFamily::FindFont(const nsACString& aFontName,
// find the font using a simple linear search
AutoReadLock lock(mLock);
uint32_t numFonts = mAvailableFonts.Length();
- for (uint32_t i = 0; i < numFonts; i++) {
- gfxFontEntry* fe = mAvailableFonts[i].get();
+ for (uint32_t i = numFonts; i > 0;) {
+ gfxFontEntry* fe = mAvailableFonts[--i].get();
if (fe && fe->Name().Equals(aFontName, aCmp)) {
return fe;
}
diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h
@@ -182,17 +182,26 @@ class gfxUserFontFamily : public gfxFontFamily {
virtual ~gfxUserFontFamily();
- // add the given font entry to the end of the family's list
+ // Add the given font entry to the end of the family's list.
void AddFontEntry(gfxFontEntry* aFontEntry) {
mozilla::AutoWriteLock lock(mLock);
MOZ_ASSERT(!mIsSimpleFamily, "not valid for user-font families");
- // keep ref while removing existing entry
- RefPtr<gfxFontEntry> fe = aFontEntry;
- // remove existing entry, if already present
- mAvailableFonts.RemoveElement(aFontEntry);
- // insert at the beginning so that the last-defined font is the first
- // one in the fontlist used for matching, as per CSS Fonts spec
- mAvailableFonts.InsertElementAt(0, aFontEntry);
+
+ // If this entry is already the last in the list, we can bail out without
+ // doing any work.
+ if (!mAvailableFonts.IsEmpty()) {
+ if (mAvailableFonts.LastElement() == aFontEntry) {
+ return;
+ }
+ }
+
+ // Append new entry: we will search faces from the end, so that the last-
+ // defined font is the first one in the fontlist used for matching, as per
+ // CSS Fonts spec.
+ // (It is possible that the entry is already present earlier in the list,
+ // but duplication is harmless and it's not worth the cost of searching for
+ // an existing entry here.)
+ mAvailableFonts.AppendElement(aFontEntry);
if (aFontEntry->mFamilyName.IsEmpty()) {
aFontEntry->mFamilyName = Name();