gpui_wgpu: Respect buffer_font_fallbacks setting#54878
Conversation
wires user configured `FontFallbacks` into the cosmic text path. the chain is resolved at font load time and stored on each `LoadedFont`. `layout_line` splits each `FontRun` into spans by codepoint coverage and emits one `Attrs` per slot so cosmic text shapes each span with the correct face. inheriting codepoints (marks, zwj, zwnj, variation selectors) stick to the current span so emoji zwj sequences and combining marks are not torn across faces. Closes zed-industries#17254
buffer_font_fallbacks settingbuffer_font_fallbacks setting
|
@Albab-Hasan thanks for this! I'm a little worried about performance. How expensive is looking up each character every time? How many microseconds does this add to rendering a typical page of code? Claude also noticed that this defaults to continuing the run in the fallback font, it should probably switch back to the main font more aggressively (e.g. I am not sure that we should here, but we do use the unicode-segmentation library to do glyph grouping in other places. Should we also use that here? |
|
@ConradIrwin thanks for the review. pushed the bug fix in the latest commit. as for performance i considered a few approaches before landing on the charmap probe:
i started with the charmap probe since its simple and already correct. happy to add a benchmark or switch to bitsets if you want numbers before merging. |
|
for the unicode segmentation yeah i think we should. the mark category heuristic misses hangul jamo, regional indicator pairs and indic prepend chars. switching to |
|
Sounds good to me. Are you able to run a micro benchmark? (e.g. Load a page full of code and see how many microseconds it takes to render a frame before and after with no font caching). If it's <5% that's seems ok; but if it's 10% or more we should think about optimization (like just ignoring ascii or something). |
|
ran a bench directly on
input is ~3 800 chars of ASCII code in one run (worst case — in practice Zed splits a line into many short runs). 6% is just over your threshold. the cost is one fn pick_covering_slot(...) -> Option<usize> {
if (ch as u32) <= 0x7F || is_inheriting_codepoint(ch) {
return current;
}
...
}the zero-overhead case (no fallbacks configured, the default) is unchanged. will open the unicode-segmentation follow-up as a separate pr. |
|
👍 Thank you for testing it! I think we should do both the latin optimization and the unicode segmentation so we can land this in one cohesive chunk. It'd be annoying to merge this and then have to rush follow up changes before the next release ships. |
|
@ConradIrwin any updates? |
|
Sorry I don't get notified when the PR is pushed to (and I'm super behind on my reviews). Happy to merge this, but looks like the linter is unhappy; what's the overhead with the optimization and unicode segmentation? |
|
@ConradIrwin My bad I'll fix all the issues. And yeah this pr just disappeared for some reason that might explain you not getting the notification. Benchmarks: No fallback configured | 3.87 ms | baseline Down from ~6% overhead before. The ASCII skip ( |
|
@ConradIrwin done |
|
Thank you! |
|
@zed-industries/approved |
wires user configured
FontFallbacksinto the cosmic text path. the chain is resolved at font load time and stored on eachLoadedFont.layout_linesplits eachFontRuninto spans by codepoint coverage and emits oneAttrsper slot so cosmic text shapes each span with the correct face. inheriting codepoints (marks, zwj, zwnj, variation selectors) stick to the current span so emoji zwj sequences and combining marks are not torn across faces.Closes #17254
Self-Review Checklist:
Release Notes:
buffer_font_fallbackson linux