From 33de0f821f7b6ba8f772c3a6af8b54b2841b131a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 6 May 2021 12:46:23 +0530 Subject: [PATCH] Use uthash for the sprite position cache --- kitty/data-types.h | 1 + kitty/fonts.c | 61 +++++++++++----------------------------- kitty/fonts.h | 1 - kitty/glyph-cache.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ kitty/glyph-cache.h | 23 +++++++++++++++ kitty_tests/fonts.py | 1 + 6 files changed, 108 insertions(+), 46 deletions(-) create mode 100644 kitty/glyph-cache.c create mode 100644 kitty/glyph-cache.h diff --git a/kitty/data-types.h b/kitty/data-types.h index f6cb45be9..54bc4f994 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -48,6 +48,7 @@ typedef uint16_t hyperlink_id_type; typedef int key_type; #define HYPERLINK_MAX_NUMBER UINT16_MAX typedef uint16_t combining_type; +typedef uint16_t glyph_index; typedef uint32_t pixel; typedef unsigned int index_type; typedef uint16_t sprite_index; diff --git a/kitty/fonts.c b/kitty/fonts.c index c942e3248..65e1eb4f8 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -11,6 +11,7 @@ #include "emoji.h" #include "unicode-data.h" #include "charsets.h" +#include "glyph-cache.h" #define MISSING_GLYPH 4 #define MAX_NUM_EXTRA_GLYPHS 8u @@ -33,16 +34,6 @@ typedef struct { glyph_index data[MAX_NUM_EXTRA_GLYPHS]; } ExtraGlyphs; -typedef struct SpritePosition SpritePosition; -struct SpritePosition { - SpritePosition *next; - bool filled, rendered, colored; - sprite_index x, y, z; - uint8_t ligature_index; - glyph_index glyph; - ExtraGlyphs extra_glyphs; -}; - #define SPECIAL_FILLED_MASK 1 #define SPECIAL_VALUE_MASK 2 #define EMPTY_FILLED_MASK 4 @@ -81,7 +72,7 @@ typedef enum { SPACER_STARTEGY_UNKNOWN, SPACERS_BEFORE, SPACERS_AFTER } SpacerSt typedef struct { PyObject *face; // Map glyphs to sprite map co-ords - SpritePosition sprite_map[1024]; + SpritePosition *sprite_position_hash_table; hb_feature_t* ffs_hb_features; size_t num_ffs_hb_features; SpecialGlyphCache special_glyph_cache[SPECIAL_GLYPH_CACHE_SIZE]; @@ -151,7 +142,8 @@ free_maps(Font *font) { }\ memset(font->attr, 0, sizeof(font->attr)); \ } - free_a_map(SpritePosition, sprite_map); + free_sprite_position_hash_table(&font->sprite_position_hash_table); + font->sprite_position_hash_table = NULL; free_a_map(SpecialGlyphCache, special_glyph_cache); #undef free_a_map } @@ -258,42 +250,20 @@ do_increment(FontGroup *fg, int *error) { } -static inline bool -extra_glyphs_equal(ExtraGlyphs *a, ExtraGlyphs *b) { - for (size_t i = 0; i < MAX_NUM_EXTRA_GLYPHS; i++) { - if (a->data[i] != b->data[i]) return false; - if (a->data[i] == 0) return true; - } - return true; -} - - static SpritePosition* sprite_position_for(FontGroup *fg, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, uint8_t ligature_index, int *error) { - glyph_index idx = glyph & (SPECIAL_GLYPH_CACHE_SIZE - 1); - SpritePosition *s = font->sprite_map + idx; - // Optimize for the common case of glyph under 1024 already in the cache - if (LIKELY(s->glyph == glyph && s->filled && extra_glyphs_equal(&s->extra_glyphs, extra_glyphs) && s->ligature_index == ligature_index)) return s; // Cache hit - while(true) { - if (s->filled) { - if (s->glyph == glyph && extra_glyphs_equal(&s->extra_glyphs, extra_glyphs) && s->ligature_index == ligature_index) return s; // Cache hit - } else { - break; - } - if (!s->next) { - s->next = calloc(1, sizeof(SpritePosition)); - if (s->next == NULL) { *error = 1; return NULL; } - } - s = s->next; + static glyph_index glyphs[MAX_NUM_EXTRA_GLYPHS + 4]; + unsigned glyph_count = 1; glyphs[0] = glyph; + glyph_index *eg = extra_glyphs->data; + + while(*eg) { glyphs[glyph_count++] = *eg++; } + bool created; + SpritePosition *s = find_or_create_sprite_position(&font->sprite_position_hash_table, glyphs, glyph_count, ligature_index, &created); + if (!s) { *error = 1; return NULL; } + if (created) { + s->x = fg->sprite_tracker.x; s->y = fg->sprite_tracker.y; s->z = fg->sprite_tracker.z; + do_increment(fg, error); } - s->glyph = glyph; - memcpy(&s->extra_glyphs, extra_glyphs, sizeof(ExtraGlyphs)); - s->ligature_index = ligature_index; - s->filled = true; - s->rendered = false; - s->colored = false; - s->x = fg->sprite_tracker.x; s->y = fg->sprite_tracker.y; s->z = fg->sprite_tracker.z; - do_increment(fg, error); return s; } @@ -388,6 +358,7 @@ free_font_groups(void) { free(font_groups); font_groups = NULL; font_groups_capacity = 0; num_font_groups = 0; } + free_glyph_cache_global_resources(); } static void diff --git a/kitty/fonts.h b/kitty/fonts.h index 7a9cb6f21..a6a5397e2 100644 --- a/kitty/fonts.h +++ b/kitty/fonts.h @@ -19,7 +19,6 @@ typedef struct { } StringCanvas; // API that font backends need to implement -typedef uint16_t glyph_index; unsigned int glyph_id_for_codepoint(PyObject *, char_type); int get_glyph_width(PyObject *, glyph_index); bool is_glyph_empty(PyObject *, glyph_index); diff --git a/kitty/glyph-cache.c b/kitty/glyph-cache.c new file mode 100644 index 000000000..fd86f9319 --- /dev/null +++ b/kitty/glyph-cache.c @@ -0,0 +1,67 @@ +/* + * glyph-cache.c + * Copyright (C) 2021 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "glyph-cache.h" +#include "uthash.h" + + +#undef uthash_fatal +#define uthash_fatal(msg) fatal(msg) + + +typedef struct SpritePosItem { + SpritePositionHead + UT_hash_handle hh; + glyph_index key[]; +} SpritePosItem; + +static glyph_index *scratch = NULL; +static unsigned scratch_sz = 0; + + +void +free_glyph_cache_global_resources(void) { + free(scratch); + scratch = NULL; scratch_sz = 0; +} + + +static inline unsigned +key_size_for_glyph_count(unsigned count) { return count + 2; } + + +SpritePosition* +find_or_create_sprite_position(SpritePosition **head_, glyph_index *glyphs, glyph_index count, glyph_index ligature_index, bool *created) { + SpritePosItem **head = (SpritePosItem**)head_, *p; + const unsigned key_sz = key_size_for_glyph_count(count); + if (key_sz > scratch_sz) { + scratch = realloc(scratch, key_sz + 16); + if (!scratch) return NULL; + scratch_sz = key_sz + 16; + } + const unsigned key_sz_bytes = key_sz * sizeof(glyph_index); + scratch[0] = count; scratch[1] = ligature_index; + memcpy(scratch + 2, glyphs, count * sizeof(glyph_index)); + HASH_FIND(hh, *head, scratch, key_sz_bytes, p); + if (p) { *created = false; return (SpritePosition*)p; } + + p = calloc(1, sizeof(SpritePosItem) + key_sz_bytes); + if (!p) return NULL; + memcpy(p->key, scratch, key_sz_bytes); + HASH_ADD(hh, *head, key, key_sz_bytes, p); + *created = true; + return (SpritePosition*)p; +} + +void +free_sprite_position_hash_table(SpritePosition **head_) { + SpritePosItem **head = (SpritePosItem**)head_, *s, *tmp; + HASH_ITER(hh, *head, s, tmp) { + HASH_DEL(*head, s); + free(s); + } +} diff --git a/kitty/glyph-cache.h b/kitty/glyph-cache.h new file mode 100644 index 000000000..f7d17c00a --- /dev/null +++ b/kitty/glyph-cache.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#pragma once + +#include "data-types.h" + +#define SpritePositionHead \ + bool rendered, colored; \ + sprite_index x, y, z; \ + +typedef struct SpritePosition { + SpritePositionHead +} SpritePosition; + + +void free_glyph_cache_global_resources(void); +void free_sprite_position_hash_table(SpritePosition **head); +SpritePosition* +find_or_create_sprite_position(SpritePosition **head, glyph_index *glyphs, glyph_index count, glyph_index ligature_index, bool *created); diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index 9aaafb6c0..cae868bae 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -49,6 +49,7 @@ class Rendering(BaseTest): sprite_map_set_limits(10, 2) sprite_map_set_layout(5, 5) self.ae(test_sprite_position_for(0), (0, 0, 0)) + self.ae(test_sprite_position_for(0), (0, 0, 0)) self.ae(test_sprite_position_for(1), (1, 0, 0)) self.ae(test_sprite_position_for(2), (0, 1, 0)) self.ae(test_sprite_position_for(3), (1, 1, 0))