]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Performance: cache character widths returned from Pango.
authorSimon Tatham <anakin@pobox.com>
Sun, 23 Aug 2015 13:13:30 +0000 (14:13 +0100)
committerSimon Tatham <anakin@pobox.com>
Sat, 17 Oct 2015 16:30:17 +0000 (17:30 +0100)
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.

(cherry picked from commit c3ef30c883e3657ff57679fb611f1f6ee6f33dba)

unix/gtkfont.c

index d241db0e8960d9604806a9bd43bba2644e4ce812..b152f2bbf8e9436b593841d1326074221a3a67b8 100644 (file)
@@ -870,6 +870,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 = {
@@ -986,6 +993,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);
 
@@ -1039,10 +1048,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 */
@@ -1125,39 +1164,34 @@ static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
            clen++;
        n = 1;
 
-        /*
-         * 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 (!is_rtl(string[0])) {
-
+        if (is_rtl(string[0]) ||
+            pangofont_char_width(layout, pfont, string[n-1],
+                                 utfptr, clen) != cellwidth) {
             /*
-             * 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);
-            pango_layout_get_pixel_extents(layout, NULL, &rect);
-
-            if (rect.width == cellwidth) {
-                /*
-                 * Try extracting more characters, for as long as they
-                 * stay well-behaved.
-                 */
-                while (clen < utflen) {
-                    int oldclen = clen;
-                    clen++;                   /* skip UTF-8 introducer byte */
-                    while (clen < utflen &&
-                           (unsigned char)utfptr[clen] >= 0x80 &&
-                           (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) {
-                        clen = oldclen;
-                        n--;
-                        break;
-                    }
+        } else {
+            /*
+             * 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;
+                clen++;                       /* skip UTF-8 introducer byte */
+                while (clen < utflen &&
+                       (unsigned char)utfptr[clen] >= 0x80 &&
+                       (unsigned char)utfptr[clen] < 0xC0)
+                    clen++;
+                n++;
+                if (pangofont_char_width(layout, pfont,
+                                         string[n-1], utfptr + oldclen,
+                                         clen - oldclen) != cellwidth) {
+                    clen = oldclen;
+                    n--;
+                    break;
                 }
             }
         }