Performance: cache character widths returned from Pango.

Profiling reveals that pterm in Pango rendering mode uses an absurd
amount of CPU when it's not even actually _drawing_ the text, because
of all the calls to pango_layout_get_pixel_extents() while
pangofont_draw_text tries to work out which characters it can safely
draw as part of a long string. Caching the results speeds things up
greatly.
This commit is contained in:
Simon Tatham 2015-08-23 14:13:30 +01:00
Родитель 87040f6fd2
Коммит c3ef30c883
1 изменённых файлов: 65 добавлений и 31 удалений

Просмотреть файл

@ -1137,6 +1137,13 @@ struct pangofont {
* Data passed in to unifont_create().
*/
int bold, shadowoffset, shadowalways;
/*
* Cache of character widths, indexed by Unicode code point. In
* pixels; -1 means we haven't asked Pango about this character
* before.
*/
int *widthcache;
unsigned nwidthcache;
};
static const struct unifont_vtable pangofont_vtable = {
@ -1260,6 +1267,8 @@ static unifont *pangofont_create_internal(GtkWidget *widget,
pfont->bold = bold;
pfont->shadowoffset = shadowoffset;
pfont->shadowalways = shadowalways;
pfont->widthcache = NULL;
pfont->nwidthcache = 0;
pango_font_metrics_unref(metrics);
@ -1313,10 +1322,40 @@ static void pangofont_destroy(unifont *font)
{
struct pangofont *pfont = (struct pangofont *)font;
pango_font_description_free(pfont->desc);
sfree(pfont->widthcache);
g_object_unref(pfont->fset);
sfree(font);
}
static int pangofont_char_width(PangoLayout *layout, struct pangofont *pfont,
wchar_t uchr, const char *utfchr, int utflen)
{
/*
* Here we check whether a character has the same width as the
* character cell it'll be drawn in. Because profiling showed that
* pango_layout_get_pixel_extents() was a huge bottleneck when we
* were calling it every time we needed to know this, we instead
* call it only on characters we don't already know about, and
* cache the results.
*/
if ((unsigned)uchr >= pfont->nwidthcache) {
unsigned newsize = ((int)uchr + 0x100) & ~0xFF;
pfont->widthcache = sresize(pfont->widthcache, newsize, int);
while (pfont->nwidthcache < newsize)
pfont->widthcache[pfont->nwidthcache++] = -1;
}
if (pfont->widthcache[uchr] < 0) {
PangoRectangle rect;
pango_layout_set_text(layout, utfchr, utflen);
pango_layout_get_pixel_extents(layout, NULL, &rect);
pfont->widthcache[uchr] = rect.width;
}
return pfont->widthcache[uchr];
}
static int pangofont_has_glyph(unifont *font, wchar_t glyph)
{
/* Pango implements font fallback, so assume it has everything */
@ -1429,23 +1468,19 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
clen++;
n = 1;
if (is_rtl(string[0]) ||
pangofont_char_width(layout, pfont, string[n-1],
utfptr, clen) != cellwidth) {
/*
* If it's a right-to-left character, we must display it on
* its own, to stop Pango helpfully re-reversing our already
* reversed text.
* If this character is a right-to-left one, or has an
* unusual width, then we must display it on its own.
*/
if (!is_rtl(string[0])) {
} else {
/*
* See if that character has the width we expect.
*/
pango_layout_set_text(layout, utfptr, clen);
pango_layout_get_pixel_extents(layout, NULL, &rect);
if (rect.width == cellwidth) {
/*
* Try extracting more characters, for as long as they
* stay well-behaved.
* Try to amalgamate a contiguous string of characters
* with the expected sensible width, for the common case
* in which we're using a monospaced font and everything
* works as expected.
*/
while (clen < utflen) {
int oldclen = clen;
@ -1455,16 +1490,15 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
(unsigned char)utfptr[clen] < 0xC0)
clen++;
n++;
pango_layout_set_text(layout, utfptr, clen);
pango_layout_get_pixel_extents(layout, NULL, &rect);
if (rect.width != n * cellwidth) {
if (pangofont_char_width(layout, pfont,
string[n-1], utfptr + oldclen,
clen - oldclen) != cellwidth) {
clen = oldclen;
n--;
break;
}
}
}
}
pango_layout_set_text(layout, utfptr, clen);
pango_layout_get_pixel_extents(layout, NULL, &rect);