1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 01:48:00 +00:00

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
parent 87040f6fd2
commit c3ef30c883

View File

@ -1137,6 +1137,13 @@ struct pangofont {
* Data passed in to unifont_create(). * Data passed in to unifont_create().
*/ */
int bold, shadowoffset, shadowalways; 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 = { static const struct unifont_vtable pangofont_vtable = {
@ -1260,6 +1267,8 @@ static unifont *pangofont_create_internal(GtkWidget *widget,
pfont->bold = bold; pfont->bold = bold;
pfont->shadowoffset = shadowoffset; pfont->shadowoffset = shadowoffset;
pfont->shadowalways = shadowalways; pfont->shadowalways = shadowalways;
pfont->widthcache = NULL;
pfont->nwidthcache = 0;
pango_font_metrics_unref(metrics); pango_font_metrics_unref(metrics);
@ -1313,10 +1322,40 @@ static void pangofont_destroy(unifont *font)
{ {
struct pangofont *pfont = (struct pangofont *)font; struct pangofont *pfont = (struct pangofont *)font;
pango_font_description_free(pfont->desc); pango_font_description_free(pfont->desc);
sfree(pfont->widthcache);
g_object_unref(pfont->fset); g_object_unref(pfont->fset);
sfree(font); 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) static int pangofont_has_glyph(unifont *font, wchar_t glyph)
{ {
/* Pango implements font fallback, so assume it has everything */ /* Pango implements font fallback, so assume it has everything */
@ -1429,39 +1468,34 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
clen++; clen++;
n = 1; n = 1;
/* if (is_rtl(string[0]) ||
* If it's a right-to-left character, we must display it on pangofont_char_width(layout, pfont, string[n-1],
* its own, to stop Pango helpfully re-reversing our already utfptr, clen) != cellwidth) {
* reversed text.
*/
if (!is_rtl(string[0])) {
/* /*
* See if that character has the width we expect. * If this character is a right-to-left one, or has an
* unusual width, then we must display it on its own.
*/ */
pango_layout_set_text(layout, utfptr, clen); } else {
pango_layout_get_pixel_extents(layout, NULL, &rect); /*
* Try to amalgamate a contiguous string of characters
if (rect.width == cellwidth) { * with the expected sensible width, for the common case
/* * in which we're using a monospaced font and everything
* Try extracting more characters, for as long as they * works as expected.
* stay well-behaved. */
*/ while (clen < utflen) {
while (clen < utflen) { int oldclen = clen;
int oldclen = clen; clen++; /* skip UTF-8 introducer byte */
clen++; /* skip UTF-8 introducer byte */ while (clen < utflen &&
while (clen < utflen && (unsigned char)utfptr[clen] >= 0x80 &&
(unsigned char)utfptr[clen] >= 0x80 && (unsigned char)utfptr[clen] < 0xC0)
(unsigned char)utfptr[clen] < 0xC0) clen++;
clen++; n++;
n++; if (pangofont_char_width(layout, pfont,
pango_layout_set_text(layout, utfptr, clen); string[n-1], utfptr + oldclen,
pango_layout_get_pixel_extents(layout, NULL, &rect); clen - oldclen) != cellwidth) {
if (rect.width != n * cellwidth) { clen = oldclen;
clen = oldclen; n--;
n--; break;
break;
}
} }
} }
} }