Use a cache on Haiku to avoid constantly reading fonts during font lookup

* src/haiku_font_support.cc (struct font_object_cache_bucket):
New struct.
(language_code_points): Make `int'.
(hash_string): New function.
(cache_font_object_data, lookup_font_object_data)
(font_object_has_chars): New functions.
(font_check_wanted_chars, font_check_one_of)
(font_check_language): Lookup in cached font object instead.
(be_init_font_data, be_evict_font_cache): New functions.

* src/haiku_support.h (struct haiku_font_pattern): Make
`uint32_t's ints instead.
* src/haikufont.c (haikufont_apply_registry, syms_of_haikufont):
Adjust for those changes.

* src/haikuterm.c (haiku_frame_up_to_date): Clear font lookup
cache every 50 updates.
This commit is contained in:
Po Lu 2022-04-21 03:09:22 +00:00
parent ab530ddeb5
commit 8c282d68bd
4 changed files with 205 additions and 32 deletions

View file

@ -27,15 +27,111 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "haiku_support.h"
/* Cache used during font lookup. It contains an opened font object
we can look inside, and some previously determined information. */
struct font_object_cache_bucket
{
struct font_object_cache_bucket *next;
unsigned int hash;
BFont *font_object;
};
static struct font_object_cache_bucket *font_object_cache[2048];
/* Haiku doesn't expose font language data in BFont objects. Thus, we
select a few representative characters for each supported `:lang'
(currently Chinese, Korean and Japanese,) and test for those
instead. */
static uint32_t language_code_points[MAX_LANGUAGE][4] =
{{20154, 20754, 22996, 0}, /* Chinese. */
{51312, 49440, 44544, 0}, /* Korean. */
{26085, 26412, 12371, 0}, /* Japanese. */};
static int language_code_points[MAX_LANGUAGE][3] =
{{20154, 20754, 22996}, /* Chinese. */
{51312, 49440, 44544}, /* Korean. */
{26085, 26412, 12371}, /* Japanese. */};
static unsigned int
hash_string (const char *name_or_style)
{
unsigned int i;
i = 3323198485ul;
for (; *name_or_style; ++name_or_style)
{
i ^= *name_or_style;
i *= 0x5bd1e995;
i ^= i >> 15;
}
return i;
}
static struct font_object_cache_bucket *
cache_font_object_data (const char *family, const char *style,
BFont *font_object)
{
uint32_t hash;
struct font_object_cache_bucket *bucket, *next;
hash = hash_string (family) ^ hash_string (style);
bucket = font_object_cache[hash % 2048];
for (next = bucket; next; next = next->next)
{
if (next->hash == hash)
{
delete next->font_object;
next->font_object = font_object;
return next;
}
}
next = new struct font_object_cache_bucket;
next->font_object = font_object;
next->hash = hash;
next->next = bucket;
font_object_cache[hash % 2048] = next;
return next;
}
static struct font_object_cache_bucket *
lookup_font_object_data (const char *family, const char *style)
{
uint32_t hash;
struct font_object_cache_bucket *bucket, *next;
hash = hash_string (family) ^ hash_string (style);
bucket = font_object_cache[hash % 2048];
for (next = bucket; next; next = next->next)
{
if (next->hash == hash)
return next;
}
return NULL;
}
static bool
font_object_has_chars (struct font_object_cache_bucket *cached,
int *chars, int nchars, bool just_one_of)
{
int i;
for (i = 0; i < nchars; ++i)
{
if (just_one_of
&& cached->font_object->IncludesBlock (chars[i],
chars[i]))
return true;
if (!just_one_of
&& !cached->font_object->IncludesBlock (chars[i],
chars[i]))
return false;
}
return !just_one_of;
}
static void
estimate_font_ascii (BFont *font, int *max_width,
@ -299,54 +395,86 @@ static bool
font_check_wanted_chars (struct haiku_font_pattern *pattern, font_family family,
char *style)
{
BFont ft;
BFont *ft;
static struct font_object_cache_bucket *cached;
unicode_block wanted_block;
if (ft.SetFamilyAndStyle (family, style) != B_OK)
return false;
cached = lookup_font_object_data (family, style);
if (cached)
ft = cached->font_object;
else
{
ft = new BFont;
for (int i = 0; i < pattern->want_chars_len; ++i)
if (!ft.IncludesBlock (pattern->wanted_chars[i],
pattern->wanted_chars[i]))
return false;
if (ft->SetFamilyAndStyle (family, style) != B_OK)
{
delete ft;
return false;
}
return true;
cached = cache_font_object_data (family, style, ft);
}
return font_object_has_chars (cached, pattern->wanted_chars,
pattern->want_chars_len, false);
}
static bool
font_check_one_of (struct haiku_font_pattern *pattern, font_family family,
char *style)
{
BFont ft;
BFont *ft;
static struct font_object_cache_bucket *cached;
unicode_block wanted_block;
if (ft.SetFamilyAndStyle (family, style) != B_OK)
return false;
cached = lookup_font_object_data (family, style);
if (cached)
ft = cached->font_object;
else
{
ft = new BFont;
for (int i = 0; i < pattern->need_one_of_len; ++i)
if (ft.IncludesBlock (pattern->need_one_of[i],
pattern->need_one_of[i]))
return true;
if (ft->SetFamilyAndStyle (family, style) != B_OK)
{
delete ft;
return false;
}
return false;
cached = cache_font_object_data (family, style, ft);
}
return font_object_has_chars (cached, pattern->need_one_of,
pattern->need_one_of_len, true);
}
static bool
font_check_language (struct haiku_font_pattern *pattern, font_family family,
char *style)
{
BFont ft;
BFont *ft;
static struct font_object_cache_bucket *cached;
if (ft.SetFamilyAndStyle (family, style) != B_OK)
return false;
cached = lookup_font_object_data (family, style);
if (cached)
ft = cached->font_object;
else
{
ft = new BFont;
if (ft->SetFamilyAndStyle (family, style) != B_OK)
{
delete ft;
return false;
}
cached = cache_font_object_data (family, style, ft);
}
if (pattern->language == MAX_LANGUAGE)
return false;
for (uint32_t *ch = (uint32_t *)
&language_code_points[pattern->language]; *ch; ch++)
if (!ft.IncludesBlock (*ch, *ch))
return false;
return true;
return font_object_has_chars (cached, language_code_points[pattern->language],
3, false);
}
static bool
@ -645,3 +773,33 @@ be_list_font_families (size_t *length)
return array;
}
void
be_init_font_data (void)
{
memset (&font_object_cache, 0, sizeof font_object_cache);
}
/* Free the font object cache. This is called every 50 updates of a
frame. */
void
be_evict_font_cache (void)
{
struct font_object_cache_bucket *bucket, *last;
int i;
for (i = 0; i < 2048; ++i)
{
bucket = font_object_cache[i];
while (bucket)
{
last = bucket;
bucket = bucket->next;
delete last->font_object;
delete last;
}
font_object_cache[i] = NULL;
}
}

View file

@ -304,8 +304,8 @@ struct haiku_font_pattern
enum haiku_font_slant slant;
enum haiku_font_width width;
enum haiku_font_language language;
uint32_t *wanted_chars;
uint32_t *need_one_of;
int *wanted_chars;
int *need_one_of;
int oblique_seen_p;
};
@ -633,6 +633,8 @@ extern void BMenu_add_title (void *, const char *);
extern int be_plain_font_height (void);
extern int be_string_width_with_plain_font (const char *);
extern void be_init_font_data (void);
extern void be_evict_font_cache (void);
extern int be_get_display_screens (void);
extern bool be_use_subpixel_antialiasing (void);
extern const char *be_find_setting (const char *);

View file

@ -137,7 +137,7 @@ haikufont_apply_registry (struct haiku_font_pattern *pattern,
for (l = 0; uniquifier[l]; ++l);
uint32_t *a = xmalloc (l * sizeof *a);
int *a = xmalloc (l * sizeof *a);
for (l = 0; uniquifier[l]; ++l)
a[l] = uniquifier[l];
@ -1111,4 +1111,6 @@ syms_of_haikufont (void)
font_cache = list (Qnil);
staticpro (&font_cache);
be_init_font_data ();
}

View file

@ -46,6 +46,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
struct haiku_display_info *x_display_list = NULL;
extern frame_parm_handler haiku_frame_parm_handlers[];
/* This is used to determine when to evict the font lookup cache,
which we do every 50 updates. */
static int up_to_date_count;
static void **fringe_bmps;
static int max_fringe_bmp = 0;
@ -231,6 +235,13 @@ haiku_frame_up_to_date (struct frame *f)
FRAME_MOUSE_UPDATE (f);
if (FRAME_DIRTY_P (f) && !buffer_flipping_blocked_p ())
haiku_flip_buffers (f);
up_to_date_count++;
if (up_to_date_count == 50)
{
be_evict_font_cache ();
up_to_date_count = 0;
}
unblock_input ();
}