[Fonts]Notes on Pango Xft backend
- From: Owen Taylor <otaylor redhat com>
- To: keithp keithp com, fonts xfree86 org, gtk-i18n-list gnome org
- Subject: [Fonts]Notes on Pango Xft backend
- Date: Mon, 27 May 2002 20:09:20 -0400 (EDT)
I'm not particularly happy with the way the Xft backend for Pango
works at the moment ... it's complex, memory hungry, and relatively
slow. (Pango shaping with Xft is about twice as slow as Pango shaping
with the old X backend.)
So, I thought I'd write up a dump of:
a) What operations Pango needs to do
b) How it is implementing the operations currently
c) How that might be improved on the Pango side
d) What might be useful on the fontconfig side
If you have time to glance this over and give your thoughts, I'd
appreciate it. I'm not at all attached of the particular ways things
are implemented now or my ideas for possible improvements; what I'm
most interested in is having a reasonably efficient way of achieving
the end result.
The fundamental font operation in the Pango XFT backend (or any Pango
backend) is to map from a unicode codepoint and a PangoFontDescription
(family, style, weight, stretch, variant) to a particular PangoXftFont.
The Pango interfaces break this down into two pieces:
A) PangoFontDescription,PangoContext => PangoFontSet
B) PangoFontSet,codepoint => PangoXftFont
Operation A) is performed once for each segment of text with a different
PangoShaper or attributes; these segments of text range in length
from a few characters to an entire paragraph.
Operation B) is performed for every character in the source text.
The current Xft) implementation of operation A is somewhat complex -
a) The PangoFontDescription is turned into a XftPattern, and
FcConfigSubstitute() and XftDefaultSubsitute() are called on
this pattern to integrate in the effects of the config file, and of
b) The resulting pattern is split apart into multiple patterns -- one for
each family name in the XftPattern; (that is, a pattern that has N
family names is copied N times, with the family names replaced with
each individual family name).
c) Each of these "subpatterns" is matched against the font database, and
if the match succeeds a PangoXftFont s created for the pattern and
added to the list in the PangoFontSet. (Success is defined by the result
having the same family name as the original subpattern),
Operation B) is simpler -
For each PangoXftFont in the PangoXftFontSet in turn:
A) If the coverage information for that font hasn't previously been
retrieved, get it.
B) See, if the font has the given codepoint, if so, return it.
Operation A) is far too slow to implement into its entirety for each
small segment of text, so the bulk of the operation is cached -
a "infinite sized" map from PangoFontDescription to a list of patterns
is kept. (So, basically, everything but actually creating the PangoXftFont
objects and adding them to the fontset is cached.))
A second cache is kept of PangoXftFont; when creating a PangoXftFont
for a pattern, instead of always creating it from scratch, we instead
check to see if there is already an outstanding font for the pattern,
and, if so, return it. We also keep a small number of fonts around
after the last refcount is dropped (MAX_FREED_FONTS, currently 16.)
Issues with the current implementation:
* The PangoFontDescription => [list of patterns] map really can
grow without bond, because each size is stored independently
(and Pango allows very fine-grained sizes.) So, an app that
allowed arbitrary zooming could easily end with a huge amount
of cached lists of patterns.
If we could cache patterns independent of size, then keeping
around all patterns would be reasonable; however, fonts.conf
can do arbitrary editing of patterns with conditionals based
* XftPattern is a bulky and inconvienient structure to work
with; the end result of PangoFontDescription => [list of patterns]
is stored as a pattern, but what we really are interested
in are only the fields that actually effect display on
the screen (and coverage/metrics) ... that is, the fields
that Xft internally stores per font:
A place where this clearly shows up is in the
Pattern => PangoXftFont cache; we need to implement hash and
equality functions on large PangoXftPattern objects; the equality
operation ends up taking 15-20% of the total execution time
for layout-intensive operaitons.
* The Pattern => PangoXftFont duplicates work that Xft does internally;
When opening a pattern, Xft already checks to see if there is
another XftFont open for the same pattern.
(So, first Pango does an hash lookup to make sure that
the no PangoXftFont is already open; if none is, then Xft will
do a linear lookup to the same effect.)
Possible PangoXft improvements:
* We could conceivably limit the size of the
PangoFontDescription => [list of patterns] map; this could result in
a major loss of performance if the set of active font descriptions
exceeded the size of the cache; but it prevents unbounded memory
* The PangoFontDescription => [list of patterns] map could be changed into
a PangoFontDescription => [list of XftFont] map. This is only really
feasible in conjunction with limiting the size of the map, since
keeping the XftFont objects around would increase the effects
of a bloated cache.
* We could drop the pattern => PangoXftFont cache and assume that
creating a PangoXftFont for a given patterns is:
a) Sufficiently fast
b) Cheap in memory (because XftFont is already doing duplicate
reduction in XftFontOpenPattern.
Looking at the code of XftFontOpenPattern(), a) is unlikely to be
the case, because a large number of fields have to be individually
extracted from the pattern, and there is a linear lookup for
(Even if it isn't all the efficient, if we keep the number of
these operations small, it doesn't matter.)
A combination of these ideas is:
* Drop the two current caches, instead:
Keep a size-limited cache:
PangoFontDescription => PangoXftFontSet
When creating the PangoXftFontSet, do everything longhand, create
a separate PangoXftFont for XftFont.
[ One problem with dropping the pattern => PangoXftFont cache, is
that you will end up creating many PangoXftFont objects for the
same font at the same size, even though Xft will only create
one XftFont object. ]
Possible Xft/fontconfig improvements:
* fontconfig could conceivably export a function like FcConfigSubstitute
that produced not just the final match but also a range of sizes
that it applied to by simply changing the size in the result.
This would allow limiting the size of the
PangoFontDescription => [list of patterns] map.
* Xft could export the idea of the "core" of a pattern ... an opaque
With operations to:
c) create a XftFont from the "pattern core"
This would allow both the PangoFontDescription => [List of patterns] cache
and the Pattern => PangoXftFont cache to be implemented much more efficiently.
Can't you just use XftFont for this? Why not just store a cache
of PangoFontDescription => [List of XftFont]
a) XftFont is fairly large. (It stores the complete pattern, for one thing)
b) When you open an XftFont, it immediately opens the FT_Face
for the font. We'd really like to avoid having to ever open the FT_Face
for fonts that are in our patterns but not used.
* A simpler variant on the preceding would be for Xft to
export a "trim a pattern" operation that trimmed a pattern down
to the core fields.
* Xft could export the necessary user-data mechanisms so that we
could do reverse lookups from XftFont => PangoXftFont; this would
allow is to avoid creating duplicate PangoXftFont for a single
XftFont without keeping our own cache of PangoXftFont.
* If the solution we take results in a large number of open XftFonts, it
might be necessary to replace the linear lookup in XftFontOpenPattern
with something smarter.
Fonts mailing list
Fonts XFree86 Org
] [Thread Prev