tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mgfx/ipc/GfxMessageUtils.h | 11+++++++++++
Mgfx/thebes/CoreTextFontList.cpp | 8++++----
Mgfx/thebes/gfxDWriteFontList.cpp | 8++++----
Mgfx/thebes/gfxDWriteFonts.cpp | 4++--
Mgfx/thebes/gfxFT2FontList.cpp | 4++--
Mgfx/thebes/gfxFcPlatformFontList.cpp | 4++--
Mgfx/thebes/gfxFontEntry.cpp | 2+-
Mgfx/thebes/gfxFontEntry.h | 3++-
Mgfx/thebes/gfxFontUtils.h | 54++++++++++++++++++++++++++++++++++--------------------
Mlayout/style/FontFaceImpl.cpp | 2+-
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); }