mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
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.
This commit is contained in:
parent
607df23a92
commit
d04029d703
1 changed files with 74 additions and 103 deletions
177
main.c
177
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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue