Repository navigation
Cache custom font family resolution on iOS Fabric - #59153
Open
hryhoriiK97 wants to merge 1 commit into
Open
hryhoriiK97 wants to merge 1 commit into
hryhoriiK97 wants to merge 1 commit into
Conversation
Resolving a custom `fontFamily` in `RCTFontWithFontProperties` enumerated the font database and instantiated every face of the family on each call, once per text fragment per attributed string build. Cache the family's face list and the resolved font per family, size, weight and style, cleared when fonts are registered at runtime, as the legacy `RCTFont.mm` already does. The system font cache keeps its string key but drops a mutex that `NSCache` makes redundant. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
RCTFontWithFontProperties(ReactCommon/react/renderer/textlayoutmanager/platform/ios/.../RCTFontUtils.mm) resolves theUIFontfor every text fragment when a paragraph'sNSAttributedStringis built. The system font is cached, but a customfontFamilyis not.Current behavior. For a custom family, every call enumerates the font database with
+[UIFont fontNamesForFamilyName:], then instantiates every face in the family and reads its traits and weight to pick the closest one. That runs once per fragment per attributed-string build, so a list of 1000 labels in a custom font resolves the same font 1000 times on the layout thread, and again on the main thread whenever the attributed-string cache (256 entries) has evicted the paragraph before it is drawn.With this change. Two caches, both
NSCache(thread-safe, so the layout and main threads share them) and both cleared onkCTFontManagerRegisteredFontsChangedNotification, so a font registered at runtime does not stay cached as unknown:fontNamesForFamilyName:calls;Font variants and variation settings are applied after the cached lookup, as before, so they are not part of the key. The legacy
React/Views/RCTFont.mmalready has both caches with the same invalidation; this brings the Fabric path in line. The existing system-font cache keeps its key and drops a mutex thatNSCachemakes redundant; its lazy initialization moves todispatch_once.Why. Custom fonts are the common case in production apps, and this cost scales with the number of text fragments on screen rather than with the number of distinct fonts, which is usually a handful.
Changelog:
[IOS] [CHANGED] - Cache custom font family resolution for Fabric text, so a font family is resolved once per family, size, weight and style instead of once per text fragment
Test Plan:
node scripts/clang-format.js --checkon the changed file passes.xcodebuildfor the iPhone 16 Pro simulator (Debug) and for a physical iPhone 15 Pro (Release); the Text examples render as before.TransactionTelemetry) and a timer around-[RCTParagraphTextView drawRect:]. iPhone 16 Pro simulator, Debug. Layout and measurement are medians of 5 runs; the draw column is the median of the runs whose display pass was captured (2 before, 5 after):fontFamily: 'Avenir',fontWeight: '500'drawRecttotal (main thread)Two further runs of the "this PR" arm on other days gave 52.2 ms and 60.6 ms of layout for the Avenir case, so run-to-run spread is several milliseconds; the medians above are from the run interleaved with the baseline. The same scenario with the system font is unchanged within that noise (61.3 ms before; 60.4 to 66.0 ms across three later runs), as expected since the system font was already cached. The draw-side gain comes from paragraphs whose attributed string had been evicted from the 256-entry cache before drawing; a list with fewer than 256 distinct strings would see only the layout-side gain.
🤖 Generated with Claude Code