From 15ed0e043bf4c811b6a5d13b80edf4db77e84b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 25 May 2021 17:36:55 +0200 Subject: [PATCH] =?UTF-8?q?particle/string:=20don=E2=80=99t=20thrash=20the?= =?UTF-8?q?=20text-run=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a5bbf0b7693db2fcd3c7e3b07dcb3e4276f9abfd introduced text-run shaping. Do avoid having to re-shape non-changing strings every time the bar is refreshed, the *particle* (i.e. not the exposable) caches the last shaped text-run. Then, in expose(), it then assumes that that cached text-run is the *same* text-run as returned from begin_expose(). This is true in most cases, but *not* when a single particle is re-used to instantiate multiple exposables, as is commonly done by modules generating dynlists. For example, the i3/sway module. This fixes it, by making the cache growable, and by adding a “lock” to each cache entry. The lock is set in begin_expose(), to indicate that this particular cache entry is needed in expose(). If we can’t find a matching cache entry, we first try to find a free “slot” by searching for either unused, or used-but-not-locked cache entries. If that fails, we grow the cache and add a new entry. In expose(), we unset the lock. Closes #47 --- particles/string.c | 81 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/particles/string.c b/particles/string.c index abb581e..653c8ef 100644 --- a/particles/string.c +++ b/particles/string.c @@ -10,21 +10,26 @@ #include "../particle.h" #include "../plugin.h" +struct text_run_cache { + uint64_t hash; + struct fcft_text_run *run; + int width; + bool in_use; +}; + struct private { char *text; size_t max_len; - struct { - uint64_t hash; - struct fcft_text_run *run; - int width; - } cached; + size_t cache_size; + struct text_run_cache *cache; }; struct eprivate { /* Set when instantiating */ char *text; + ssize_t cache_idx; const struct fcft_glyph **glyphs; const struct fcft_glyph **allocated_glyphs; long *kern_x; @@ -66,20 +71,27 @@ begin_expose(struct exposable *exposable) e->glyphs = e->allocated_glyphs = NULL; e->num_glyphs = 0; e->kern_x = NULL; + e->cache_idx = -1; uint64_t hash = sdbm_hash(e->text); - if (p->cached.hash == hash) { - e->glyphs = p->cached.run->glyphs; - e->num_glyphs = p->cached.run->count; - e->kern_x = calloc(p->cached.run->count, sizeof(e->kern_x[0])); + for (size_t i = 0; i < p->cache_size; i++) { + if (p->cache[i].hash == hash) { + assert(p->cache[i].run != NULL); - exposable->width = - exposable->particle->left_margin + - p->cached.width + - exposable->particle->right_margin; + p->cache[i].in_use = true; + e->cache_idx = i; + e->glyphs = p->cache[i].run->glyphs; + e->num_glyphs = p->cache[i].run->count; + e->kern_x = calloc(p->cache[i].run->count, sizeof(e->kern_x[0])); - return exposable->width; + exposable->width = + exposable->particle->left_margin + + p->cache[i].width + + exposable->particle->right_margin; + + return exposable->width; + } } size_t chars = mbstowcs(NULL, e->text, 0); @@ -96,11 +108,32 @@ begin_expose(struct exposable *exposable) for (size_t i = 0; i < run->count; i++) w += run->glyphs[i]->advance.x; - fcft_text_run_destroy(p->cached.run); - p->cached.hash = hash; - p->cached.run = run; - p->cached.width = w; + ssize_t cache_idx = -1; + for (size_t i = 0; i < p->cache_size; i++) { + if (p->cache[i].run == NULL || !p->cache[i].in_use) { + fcft_text_run_destroy(p->cache[i].run); + cache_idx = i; + break; + } + } + if (cache_idx < 0) { + size_t new_size = p->cache_size + 1; + struct text_run_cache *new_cache = realloc( + p->cache, new_size * sizeof(new_cache[0])); + + p->cache_size = new_size; + p->cache = new_cache; + cache_idx = new_size - 1; + } + + assert(cache_idx >= 0 && cache_idx < p->cache_size); + p->cache[cache_idx].hash = hash; + p->cache[cache_idx].run = run; + p->cache[cache_idx].width = w; + p->cache[cache_idx].in_use = true; + + e->cache_idx = cache_idx; e->num_glyphs = run->count; e->glyphs = run->glyphs; } @@ -147,6 +180,11 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int const struct eprivate *e = exposable->private; const struct fcft_font *font = exposable->particle->font; + if (e->cache_idx >= 0) { + struct private *priv = exposable->particle->private; + priv->cache[e->cache_idx].in_use = false; + } + if (e->num_glyphs == 0) return; @@ -247,7 +285,9 @@ static void particle_destroy(struct particle *particle) { struct private *p = particle->private; - fcft_text_run_destroy(p->cached.run); + for (size_t i = 0; i < p->cache_size; i++) + fcft_text_run_destroy(p->cache[i].run); + free(p->cache); free(p->text); free(p); particle_default_destroy(particle); @@ -259,7 +299,8 @@ string_new(struct particle *common, const char *text, size_t max_len) struct private *p = calloc(1, sizeof(*p)); p->text = strdup(text); p->max_len = max_len; - p->cached.hash = -1; + p->cache_size = 0; + p->cache = NULL; common->private = p; common->destroy = &particle_destroy;