commit 7252cb0aa54684bb7dde137be692a1483027aa78
parent f1c7fc7b532c50ee270e0509539f67116cbe36ad
Author: Jonathan Kew <jkew@mozilla.com>
Date: Mon, 10 Nov 2025 22:10:58 +0000
Bug 1999164 - Micro-optimize gfxSparseBitSet / gfxCharacterMap construction to reduce repeated re-allocation overhead. r=gfx-reviewers,lsalzman
In local testing, this reduces time spent under FontFaceImpl::GetUnicodeRangeAsCharacterMap
by around 20-25% for the testcase here, by reducing array reallocations while the bitset
is being populated.
(The following patch makes this redundant for the common case when unicode-range is absent,
but this will still benefit cases with a large but not-universal unicode-range descriptor.)
Differential Revision: https://phabricator.services.mozilla.com/D272044
Diffstat:
10 files changed, 63 insertions(+), 37 deletions(-)
diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h
@@ -1204,6 +1204,17 @@ struct ParamTraits<gfxSparseBitSet> {
};
template <>
+struct ParamTraits<gfxSparseBitSet::BlockIndex> {
+ typedef gfxSparseBitSet::BlockIndex paramType;
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mIndex);
+ }
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mIndex);
+ }
+};
+
+template <>
struct ParamTraits<gfxSparseBitSet::Block> {
typedef gfxSparseBitSet::Block paramType;
static void Write(MessageWriter* aWriter, const paramType& aParam) {
diff --git a/gfx/thebes/CoreTextFontList.cpp b/gfx/thebes/CoreTextFontList.cpp
@@ -256,7 +256,7 @@ nsresult CTFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
rv = NS_OK;
} else {
uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(256);
AutoTable cmapTable(this, kCMAP);
if (cmapTable) {
@@ -353,7 +353,7 @@ nsresult CTFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
mHasCmapTable = true;
} else {
// if error occurred, initialize to null cmap
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(0);
mHasCmapTable = false;
}
if (setCharMap) {
@@ -1639,7 +1639,7 @@ void CTFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
if (cmapTable) {
const uint8_t* cmapData = (const uint8_t*)CFDataGetBytePtr(cmapTable);
uint32_t cmapLen = CFDataGetLength(cmapTable);
- RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256);
uint32_t offset;
nsresult rv;
@@ -1758,7 +1758,7 @@ void CoreTextFontList::AddFaceInitData(
AutoCFTypeRef<CFDataRef> data(CGFontCopyTableForTag(font, kCMAP));
if (data) {
uint32_t offset;
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(256);
gfxFontUtils::ReadCMAP(CFDataGetBytePtr(data), CFDataGetLength(data),
*charmap, offset);
}
diff --git a/gfx/thebes/gfxDWriteFontList.cpp b/gfx/thebes/gfxDWriteFontList.cpp
@@ -529,7 +529,7 @@ nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
rv = NS_OK;
} else {
uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(256);
AutoTable cmapTable(this, kCMAP);
if (cmapTable) {
@@ -566,7 +566,7 @@ nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
mHasCmapTable = true;
} else {
// if error occurred, initialize to null cmap
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(0);
mHasCmapTable = false;
}
if (setCharMap) {
@@ -1363,7 +1363,7 @@ void gfxDWriteFontList::GetFacesInitDataForFamily(
if (SUCCEEDED(dwFontFace->TryGetFontTable(
kCMAP, (const void**)&data, &size, &context, &exists)) &&
exists) {
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(256);
uint32_t offset;
gfxFontUtils::ReadCMAP((const uint8_t*)data, size, *charmap,
offset);
@@ -2509,7 +2509,7 @@ void DirectWriteFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
if (SUCCEEDED(hr) && exists) {
bool cmapLoaded = false;
- RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256);
uint32_t offset;
MOZ_SEH_TRY {
if (cmapData && cmapSize > 0 &&
diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp
@@ -741,7 +741,7 @@ gfxFloat gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph) {
// Mark the font as invalid, and wipe the fontEntry's charmap so that font
// selection will skip it; we'll use a fallback font instead.
mIsValid = false;
- GetFontEntry()->mCharacterMap = new gfxCharacterMap();
+ GetFontEntry()->mCharacterMap = new gfxCharacterMap(0);
GetFontEntry()->mShmemCharacterMap = nullptr;
gfxCriticalError() << "Exception occurred measuring glyph width for "
<< GetFontEntry()->Name().get();
@@ -775,7 +775,7 @@ bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
// Mark the font as invalid, and wipe the fontEntry's charmap so that font
// selection will skip it; we'll use a fallback font instead.
mIsValid = false;
- GetFontEntry()->mCharacterMap = new gfxCharacterMap();
+ GetFontEntry()->mCharacterMap = new gfxCharacterMap(0);
GetFontEntry()->mShmemCharacterMap = nullptr;
gfxCriticalError() << "Exception occurred measuring glyph bounds for "
<< GetFontEntry()->Name().get();
diff --git a/gfx/thebes/gfxFT2FontList.cpp b/gfx/thebes/gfxFT2FontList.cpp
@@ -379,7 +379,7 @@ nsresult FT2FontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
return NS_OK;
}
- RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256);
nsresult rv = NS_ERROR_NOT_AVAILABLE;
uint32_t uvsOffset = 0;
@@ -452,7 +452,7 @@ nsresult FT2FontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
mHasCmapTable = true;
} else {
// if error occurred, initialize to null cmap
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(0);
mHasCmapTable = false;
}
if (setCharMap) {
diff --git a/gfx/thebes/gfxFcPlatformFontList.cpp b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -437,7 +437,7 @@ nsresult gfxFontconfigFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
rv = NS_OK;
} else {
uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(256);
AutoTable cmapTable(this, kCMAP);
if (cmapTable) {
@@ -466,7 +466,7 @@ nsresult gfxFontconfigFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
mHasCmapTable = true;
} else {
// if error occurred, initialize to null cmap
- charmap = new gfxCharacterMap();
+ charmap = new gfxCharacterMap(0);
mHasCmapTable = false;
}
if (setCharMap) {
diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp
@@ -217,7 +217,7 @@ bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags,
nsresult gfxFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
MOZ_ASSERT(false, "using default no-op implementation of ReadCMAP");
- RefPtr<gfxCharacterMap> cmap = new gfxCharacterMap();
+ RefPtr<gfxCharacterMap> cmap = new gfxCharacterMap(0);
if (mCharacterMap.compareExchange(nullptr, cmap.get())) {
cmap.forget().leak(); // mCharacterMap now owns the reference
}
diff --git a/gfx/thebes/gfxFontEntry.h b/gfx/thebes/gfxFontEntry.h
@@ -125,7 +125,8 @@ class gfxCharacterMap : public gfxSparseBitSet {
}
}
- gfxCharacterMap() = default;
+ explicit gfxCharacterMap(uint32_t aReserveBlocks)
+ : gfxSparseBitSet(aReserveBlocks) {}
explicit gfxCharacterMap(const gfxSparseBitSet& aOther)
: gfxSparseBitSet(aOther) {}
diff --git a/gfx/thebes/gfxFontUtils.h b/gfx/thebes/gfxFontUtils.h
@@ -60,18 +60,34 @@ class gfxSparseBitSet {
enum { BLOCK_SIZE_BITS = BLOCK_SIZE * 8 };
enum { NO_BLOCK = 0xffff }; // index value indicating missing (empty) block
+ // The BlockIndex type is just a uint16_t, except it will default-construct
+ // with the value NO_BLOCK so that we can do AppendElements(size_t) to grow
+ // the index.
+ struct BlockIndex {
+ BlockIndex() : mIndex(NO_BLOCK) {}
+ explicit BlockIndex(uint16_t aIndex) : mIndex(aIndex) {}
+
+ operator uint16_t() const { return mIndex; }
+
+ uint16_t mIndex;
+ };
+
struct Block {
- explicit Block(unsigned char memsetValue = 0) {
+ Block() { memset(mBits, 0, BLOCK_SIZE); }
+ explicit Block(unsigned char memsetValue) {
memset(mBits, memsetValue, BLOCK_SIZE);
}
uint8_t mBits[BLOCK_SIZE];
};
friend struct IPC::ParamTraits<gfxSparseBitSet>;
+ friend struct IPC::ParamTraits<BlockIndex>;
friend struct IPC::ParamTraits<Block>;
public:
gfxSparseBitSet() = default;
+ explicit gfxSparseBitSet(uint32_t aReserveCapacity)
+ : mBlockIndex(aReserveCapacity), mBlocks(aReserveCapacity) {}
bool Equals(const gfxSparseBitSet* aOther) const {
if (mBlockIndex.Length() != aOther->mBlockIndex.Length()) {
@@ -79,8 +95,8 @@ class gfxSparseBitSet {
}
size_t n = mBlockIndex.Length();
for (size_t i = 0; i < n; ++i) {
- uint32_t b1 = mBlockIndex[i];
- uint32_t b2 = aOther->mBlockIndex[i];
+ uint16_t b1 = mBlockIndex[i];
+ uint16_t b2 = aOther->mBlockIndex[i];
if ((b1 == NO_BLOCK) != (b2 == NO_BLOCK)) {
return false;
}
@@ -175,13 +191,13 @@ class gfxSparseBitSet {
void set(uint32_t aIndex) {
uint32_t i = aIndex / BLOCK_SIZE_BITS;
- while (i >= mBlockIndex.Length()) {
- mBlockIndex.AppendElement(NO_BLOCK);
+ if (i >= mBlockIndex.Length()) {
+ mBlockIndex.AppendElements(i - mBlockIndex.Length() + 1);
}
if (mBlockIndex[i] == NO_BLOCK) {
mBlocks.AppendElement();
MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
- mBlockIndex[i] = static_cast<uint16_t>(mBlocks.Length() - 1);
+ mBlockIndex[i].mIndex = static_cast<uint16_t>(mBlocks.Length() - 1);
}
Block& block = mBlocks[mBlockIndex[i]];
block.mBits[(aIndex >> 3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
@@ -199,8 +215,8 @@ class gfxSparseBitSet {
const uint32_t startIndex = aStart / BLOCK_SIZE_BITS;
const uint32_t endIndex = aEnd / BLOCK_SIZE_BITS;
- while (endIndex >= mBlockIndex.Length()) {
- mBlockIndex.AppendElement(NO_BLOCK);
+ if (endIndex >= mBlockIndex.Length()) {
+ mBlockIndex.AppendElements(endIndex - mBlockIndex.Length() + 1);
}
for (uint32_t i = startIndex; i <= endIndex; ++i) {
@@ -209,9 +225,9 @@ class gfxSparseBitSet {
if (mBlockIndex[i] == NO_BLOCK) {
bool fullBlock = (aStart <= blockFirstBit && aEnd >= blockLastBit);
- mBlocks.AppendElement(Block(fullBlock ? 0xFF : 0));
+ mBlocks.AppendElement(fullBlock ? Block(0xFF) : Block());
MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
- mBlockIndex[i] = static_cast<uint16_t>(mBlocks.Length() - 1);
+ mBlockIndex[i].mIndex = static_cast<uint16_t>(mBlocks.Length() - 1);
if (fullBlock) {
continue;
}
@@ -235,9 +251,7 @@ class gfxSparseBitSet {
return;
}
if (mBlockIndex[i] == NO_BLOCK) {
- mBlocks.AppendElement();
- MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
- mBlockIndex[i] = static_cast<uint16_t>(mBlocks.Length() - 1);
+ return;
}
Block& block = mBlocks[mBlockIndex[i]];
block.mBits[(aIndex >> 3) & (BLOCK_SIZE - 1)] &= ~(1 << (aIndex & 0x7));
@@ -286,10 +300,10 @@ class gfxSparseBitSet {
// set this bitset to the union of its current contents and another
void Union(const gfxSparseBitSet& aBitset) {
- // ensure mBlocks is large enough
+ // ensure mBlockIndex is large enough
uint32_t blockCount = aBitset.mBlockIndex.Length();
- while (blockCount > mBlockIndex.Length()) {
- mBlockIndex.AppendElement(NO_BLOCK);
+ if (blockCount > mBlockIndex.Length()) {
+ mBlockIndex.AppendElements(blockCount - mBlockIndex.Length());
}
// for each block that may be present in aBitset...
for (uint32_t i = 0; i < blockCount; ++i) {
@@ -301,7 +315,7 @@ class gfxSparseBitSet {
if (mBlockIndex[i] == NO_BLOCK) {
mBlocks.AppendElement(aBitset.mBlocks[aBitset.mBlockIndex[i]]);
MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
- mBlockIndex[i] = static_cast<uint16_t>(mBlocks.Length() - 1);
+ mBlockIndex[i].mIndex = static_cast<uint16_t>(mBlocks.Length() - 1);
continue;
}
// else set existing block to the union of both
@@ -334,8 +348,8 @@ class gfxSparseBitSet {
return check;
}
- private:
- CopyableTArray<uint16_t> mBlockIndex;
+ protected:
+ CopyableTArray<BlockIndex> mBlockIndex;
CopyableTArray<Block> mBlocks;
};
@@ -483,7 +497,7 @@ inline void gfxSparseBitSet::Union(const SharedBitSet& aBitset) {
if (mBlockIndex[i] == NO_BLOCK) {
mBlocks.AppendElement(blocks[blockIndex[i]]);
MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow");
- mBlockIndex[i] = uint16_t(mBlocks.Length() - 1);
+ mBlockIndex[i].mIndex = uint16_t(mBlocks.Length() - 1);
continue;
}
// Else set existing target block to the union of both.
diff --git a/layout/style/FontFaceImpl.cpp b/layout/style/FontFaceImpl.cpp
@@ -609,7 +609,7 @@ static already_AddRefed<gfxCharacterMap> ComputeCharacterMap(
if (ranges.IsEmpty()) {
return nullptr;
}
- auto charMap = MakeRefPtr<gfxCharacterMap>();
+ auto charMap = MakeRefPtr<gfxCharacterMap>(256);
for (auto& range : ranges) {
charMap->SetRange(range.start, range.end);
}