<div dir="ltr"><div>Hey,<br></div><div><br></div><div>Behdad and I have a longstanding discussion about better apis for font enumeration.</div><div><br></div><div>Looking at <a href="https://gitlab.gnome.org/GNOME/pango/-/issues/53">https://gitlab.gnome.org/GNOME/pango/-/issues/53</a>, it appears to go back</div><div>as far as 2006. I've revisited fontconfig performance last summer. Some of the results</div><div>from that work are mentioned in the last comments in that Pango issue.</div><div><br></div><div>To summarize the performance problem:</div><div><br></div><div>- Sorting all the system fonts is too expensive to it multiple times per paragraph, when</div><div>  the goal is to render text at 60fps. As a datapoint, after installing all the Noto fonts, my</div><div>  system has ~4000 fonts, and even after the aforementioned performance fixes, a</div><div>  single FcFontSort call averages 7ms - half of a frame.<br></div><div><br></div><div>- Most of the sorting work is wasted, since we only access the first few members of</div><div>  the returned list (in the vast majority of cases, Pango only ever uses the first one or</div><div>  two members of the sorted list).<br></div><div><br></div><div>- In the case where we do access more than the first few members, the exact order</div><div>  of fonts in 'the long tail' hardly matters - all of these are fonts that have little relation</div><div>  to the pattern we are interested in, and we might just as well have a generic fallback.</div><div><br></div><div>Our thoughts on improving this generally revolve around doing less work up front.</div><div>Basically, switch to an incremental sorting approach, where Pango can ask for the</div><div>"next best" font, one at a time. <br></div><div><br></div><div>As very rough api sketch could be something like this:</div><div><br></div><div>/* Create a sorted fontset that is sorted by matching against p */<br></div><div>FcSortedFontSet *FcSortedFontSetCreate (FcFontSet *s, FcPattern *p);<br></div><div><br></div><div>/* Return the n-th element of the sorted fontset s */<br></div><div>FcPattern *FcSortedFontSetGet (FcSortedFontSet *s, int n);<br></div><div><br></div><div>With this api, FcFontSetMatch could be implemented like this:<br></div><div><br></div><div>FcPattern *</div><div>FcFontSetMatch (FcFontSet *s, FcPattern *p)</div><div>{</div><div>  FcSortedFontSet *sorted;</div><div>  sorted = FcSortedFontSetCreate (s, p); <br></div><div>  match = FcSortedFontSet (sorted, 0);<br></div><div>  FcSortedFontSetDestroy (sorted);</div><div>  return match;<br></div><div>}</div><div><br></div><div>-----------------<br></div><div><br></div><div>In a recent call, Behdad pointed out a related, but distinct issue with the current</div><div>pipeline approach to font rendering, as implemented in pango:<br></div><div><br></div><div>First, pick fonts for all characters ("itemize")</div><div>Second, select glyphs for the resulting runs ("Shape")<br></div><div><br></div><div>The first step is where we use fontconfig to find candidate fonts, and then we</div><div>check their coverage for each character to make a choice. fontconfig currently uses</div><div>freetype to determine coverage information for fonts.<br></div><div><br></div><div>But what characters are reasonably 'covered' by font very much depends on what use <br></div><div>the second step makes of the font. The harfbuzz shaper can decompose and compose</div><div>glyphs and thereby extend coverage in ways that freetype has no idea about.</div><div><br></div><div>There are different ways in which this could be improved. One would be to give up <br></div><div>on the approach of making all font selection choices up-front, and instead use a recursive</div><div>approach in which the shaper can ask for more fonts if it can make do with the ones it</div><div>was given initially.</div><div><br></div><div>Another, maybe simpler approach would be to make fontconfig use harfbuzz instead</div><div>of freetype for finding font coverage.</div><div><br></div><div>Matthias<br></div></div>