From d04029d7035005b9747b8b4b3a48c61885c99052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 2 Jul 2019 19:44:02 +0200 Subject: [PATCH] render: revert glyph caching Instead, try to group as long sequences of glyphs as possible, as fewer calls to cairo_show_glyphs() is *much* faster. --- main.c | 177 ++++++++++++++++++++++++--------------------------------- 1 file changed, 74 insertions(+), 103 deletions(-) diff --git a/main.c b/main.c index feaa7f25..1e835a5b 100644 --- a/main.c +++ b/main.c @@ -45,16 +45,10 @@ struct wayland { struct xdg_toplevel *xdg_toplevel; }; -struct glyph_cache { - cairo_scaled_font_t *font; - cairo_surface_t *surf[256]; - cairo_pattern_t *cache[256]; -}; - struct context { bool quit; - struct glyph_cache fonts[4]; + cairo_scaled_font_t *fonts[4]; cairo_font_extents_t fextents; int width; @@ -74,13 +68,22 @@ static const struct wl_callback_listener frame_listener = { .done = &frame_callback, }; -static struct glyph_cache * +static cairo_scaled_font_t * attrs_to_font(struct context *c, const struct attributes *attrs) { int idx = attrs->italic << 1 | attrs->bold; - return &c->fonts[idx]; + return c->fonts[idx]; } +struct glyph_sequence { + cairo_glyph_t glyphs[10000]; + cairo_glyph_t *g; + int count; + + struct attributes attrs; + struct rgba foreground; +}; + static void grid_render_update(struct context *c, struct buffer *buf, const struct damage *dmg) { @@ -103,23 +106,25 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d const int cols = c->term.cols; + struct glyph_sequence gseq = {.g = gseq.glyphs}; + for (int linear_cursor = start, row = ((start - c->term.grid->offset) % c->term.grid->size) / cols, col = ((start - c->term.grid->offset) % c->term.grid->size) % cols; linear_cursor < start + length; linear_cursor++, - //col = (col + 1) % cols, col = col + 1 >= c->term.cols ? 0 : col + 1, row += col == 0 ? 1 : 0) { //LOG_DBG("UPDATE: %d (%dx%d)", linear_cursor, row, col); - //assert(linear_cursor < c->term.grid->size); - const struct cell *cell = &c->term.grid->cells[linear_cursor % c->term.grid->size]; + const struct cell *cell + = &c->term.grid->cells[linear_cursor % c->term.grid->size]; - bool has_cursor = c->term.cursor.linear == linear_cursor - c->term.grid->offset; - //bool has_cursor = false; + /* Cursor here? */ + bool has_cursor + = c->term.cursor.linear == linear_cursor - c->term.grid->offset; int x = col * c->term.cell_width; int y = row * c->term.cell_height; @@ -155,46 +160,58 @@ grid_render_update(struct context *c, struct buffer *buf, const struct damage *d if (cell->attrs.conceal) continue; - struct glyph_cache *cache = attrs_to_font(c, &cell->attrs); + /* + * cairo_show_glyphs() apparently works *much* faster when + * called once with a large array of glyphs, compared to + * multiple calls with a single glyph. + * + * So, collect glyphs until cell attributes change, then we + * 'flush' (render) the glyphs. + */ - cairo_set_source_rgba( - buf->cairo, foreground.r, foreground.g, foreground.b, foreground.a); + if (memcmp(&cell->attrs, &gseq.attrs, sizeof(cell->attrs)) != 0 || + gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10 || + memcmp(&gseq.foreground, &foreground, sizeof(foreground)) != 0) + { + if (gseq.count >= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - 10) + LOG_WARN("hit glyph limit"); + cairo_set_scaled_font(buf->cairo, attrs_to_font(c, &gseq.attrs)); + cairo_set_source_rgba( + buf->cairo, gseq.foreground.r, gseq.foreground.g, + gseq.foreground.b, gseq.foreground.a); - if (strlen(cell->c) == 1 && cache->cache[(int)cell->c[0]] != NULL) { - cairo_matrix_t matrix; - cairo_matrix_init_translate(&matrix, -x, -y); - - cairo_pattern_t *pat = cache->cache[(int)cell->c[0]]; - cairo_pattern_set_matrix(pat, &matrix); - -#if 1 - cairo_pattern_set_matrix(pat, &matrix); - cairo_mask(buf->cairo, pat); -#else /* TODO: blit image instead - but doesn't (yet) handle colors */ cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); - cairo_set_source(buf->cairo, pat); - cairo_rectangle(buf->cairo, x, y, width, height); - cairo_fill(buf->cairo); -#endif - } else { - cairo_glyph_t *glyphs = NULL; - int num_glyphs = 0; + cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count); - cairo_status_t status = cairo_scaled_font_text_to_glyphs( - cache->font, x, y + c->fextents.ascent, - cell->c, strlen(cell->c), &glyphs, &num_glyphs, - NULL, NULL, NULL); - - if (status != CAIRO_STATUS_SUCCESS) { - if (glyphs != NULL) - cairo_glyph_free(glyphs); - continue; - } - - cairo_set_scaled_font(buf->cairo, cache->font); - cairo_show_glyphs(buf->cairo, glyphs, num_glyphs); - cairo_glyph_free(glyphs); + gseq.g = gseq.glyphs; + gseq.count = 0; + gseq.attrs = cell->attrs; + gseq.foreground = foreground; } + + int new_glyphs + = sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0]) - gseq.count; + + cairo_status_t status = cairo_scaled_font_text_to_glyphs( + attrs_to_font(c, &cell->attrs), x, y + c->fextents.ascent, + cell->c, strlen(cell->c), &gseq.g, &new_glyphs, + NULL, NULL, NULL); + + if (status != CAIRO_STATUS_SUCCESS) + continue; + + gseq.g += new_glyphs; + gseq.count += new_glyphs; + assert(gseq.count <= sizeof(gseq.glyphs) / sizeof(gseq.glyphs[0])); + } + + if (gseq.count > 0) { + cairo_set_scaled_font(buf->cairo, attrs_to_font(c, &gseq.attrs)); + cairo_set_source_rgba( + buf->cairo, gseq.foreground.r, gseq.foreground.g, + gseq.foreground.b, gseq.foreground.a); + cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER); + cairo_show_glyphs(buf->cairo, gseq.glyphs, gseq.count); } wl_surface_damage_buffer( @@ -826,23 +843,23 @@ main(int argc, const char *const *argv) thrd_create(&keyboard_repeater_id, &keyboard_repeater, &c.term); const char *font_name = "Dina:pixelsize=12"; - c.fonts[0].font = font_from_name(font_name); - if (c.fonts[0].font == NULL) + c.fonts[0] = font_from_name(font_name); + if (c.fonts[0] == NULL) goto out; { char fname[1024]; snprintf(fname, sizeof(fname), "%s:style=bold", font_name); - c.fonts[1].font = font_from_name(fname); + c.fonts[1] = font_from_name(fname); snprintf(fname, sizeof(fname), "%s:style=italic", font_name); - c.fonts[2].font = font_from_name(fname); + c.fonts[2] = font_from_name(fname); snprintf(fname, sizeof(fname), "%s:style=bold italic", font_name); - c.fonts[3].font = font_from_name(fname); + c.fonts[3] = font_from_name(fname); } - cairo_scaled_font_extents(c.fonts[0].font, &c.fextents); + cairo_scaled_font_extents(c.fonts[0], &c.fextents); c.term.cell_width = (int)ceil(c.fextents.max_x_advance); c.term.cell_height = (int)ceil(c.fextents.height); @@ -850,46 +867,6 @@ main(int argc, const char *const *argv) c.fextents.height, c.fextents.max_x_advance); assert(c.fextents.max_y_advance == 0); - for (size_t i; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) { - cairo_scaled_font_t *font = c.fonts[i].font; - - for (size_t j = 0; j < 256; j++) { - const char text[2] = {(char)j, '\0'}; - - cairo_glyph_t *glyphs = NULL; - int num_glyphs = 0; - - cairo_status_t status = cairo_scaled_font_text_to_glyphs( - font, 0, 0 + c.fextents.ascent, - text, 1, &glyphs, &num_glyphs, NULL, NULL, NULL); - - if (status != CAIRO_STATUS_SUCCESS || num_glyphs != 1) { - if (glyphs != NULL) - cairo_glyph_free(glyphs); - continue; - } - - cairo_surface_t *surf = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, c.term.cell_width, c.term.cell_height); - cairo_t *ca = cairo_create(surf); - - cairo_set_scaled_font(ca, font); - cairo_set_source_rgba(ca, 1.0, 1.0, 1.0, 1.0); - cairo_show_glyphs(ca, glyphs, num_glyphs); - - cairo_glyph_free(glyphs); - cairo_destroy(ca); - - assert(surf != NULL); - assert(cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS); - c.fonts[i].surf[j] = surf; - - cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf); - assert(cairo_pattern_status(pat) == CAIRO_STATUS_SUCCESS); - c.fonts[i].cache[j] = pat; - } - } - if (c.term.ptmx == -1) { LOG_ERRNO("failed to open pseudo terminal"); goto out; @@ -1052,14 +1029,8 @@ out: free(c.term.alt.cells); for (size_t i = 0; i < sizeof(c.fonts) / sizeof(c.fonts[0]); i++) { - struct glyph_cache *fcache = &c.fonts[i]; - if (fcache->font != NULL) - cairo_scaled_font_destroy(fcache->font); - - for (size_t i = 0; i < sizeof(fcache->cache) / sizeof(fcache->cache[0]); i++) { - if (fcache->cache[i] != NULL) - cairo_pattern_destroy(fcache->cache[i]); - } + if (c.fonts[i] != NULL) + cairo_scaled_font_destroy(c.fonts[i]); } if (c.term.ptmx != -1)