drw_text: improve performance when there's no match 22511c41
this was the last piece of the puzzle, the case where we can't find any
font to draw the codepoint.

in such cases, we use XftFontMatch() which is INSANELY slow. but that's
not the real problem. the real problem was we were continuously trying
to match the same thing over and over again.

this patch introduces a small cache, which keeps track a couple
codepoints for which we know we won't find any matches.

with this, i can dump lots of emojies into dmenu where some of them
don't have any matching font, and still not have dmenu lag insanely or
FREEZE completely when scrolling up and down.

this also improves startup time, which will of course depend on the
system and all installed fonts; but on my system and test case i see the
following startup time drop:

before -> after
60ms   -> 34ms
NRK · 2022-03-24 00:37 1 file(s) · +12 −1
drw.c +12 −1
251 251
int
252 252
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
253 253
{
254 -
	int ty, ellipsis_x = 0;
254 +
	int i, ty, ellipsis_x = 0;
255 255
	unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, ellipsis_width;
256 256
	XftDraw *d = NULL;
257 257
	Fnt *usedfont, *curfont, *nextfont;
263 263
	FcPattern *match;
264 264
	XftResult result;
265 265
	int charexists = 0, overflow = 0;
266 +
	/* keep track of a couple codepoints for which we have no match. */
267 +
	enum { nomatches_len = 64 };
268 +
	static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
266 269
267 270
	if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
268 271
		return 0;
346 349
			 * character must be drawn. */
347 350
			charexists = 1;
348 351
352 +
			for (i = 0; i < nomatches_len; ++i) {
353 +
				/* avoid calling XftFontMatch if we know we won't find a match */
354 +
				if (utf8codepoint == nomatches.codepoint[i])
355 +
					goto no_match;
356 +
			}
357 +
349 358
			fccharset = FcCharSetCreate();
350 359
			FcCharSetAddChar(fccharset, utf8codepoint);
351 360
374 383
					curfont->next = usedfont;
375 384
				} else {
376 385
					xfont_free(usedfont);
386 +
					nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
387 +
no_match:
377 388
					usedfont = drw->fonts;
378 389
				}
379 390
			}