From 9d77b5a0ed7bed48dcb7483e79945067666eac0b Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 4 Aug 2022 13:05:19 -0700 Subject: BLF: Implement FreeType Caching Implementation of the FreeType 2 cache subsystem, which limits the number of concurrently-opened FT_Face and FT_Size objects, as well as caching information like character maps to speed up glyph id lookups. See D13137 for much more detail. Differential Revision: https://developer.blender.org/D13137 Reviewed by Brecht Van Lommel --- source/blender/blenfont/intern/blf.c | 2 +- source/blender/blenfont/intern/blf_font.c | 168 ++++++++++++++------- source/blender/blenfont/intern/blf_glyph.c | 50 +++--- source/blender/blenfont/intern/blf_internal.h | 13 +- .../blender/blenfont/intern/blf_internal_types.h | 6 + 5 files changed, 149 insertions(+), 90 deletions(-) (limited to 'source/blender/blenfont') diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index a1fcc17ca3f..36475321d4c 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -122,7 +122,7 @@ bool BLF_has_glyph(int fontid, unsigned int unicode) { FontBLF *font = blf_get(fontid); if (font) { - return FT_Get_Char_Index(font->face, unicode) != FT_Err_Ok; + return blf_get_char_index(font, unicode) != FT_Err_Ok; } return false; } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 339df9eb269..372dc19d64a 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -17,6 +17,7 @@ #include #include FT_FREETYPE_H +#include FT_CACHE_H /* FreeType Cache. */ #include FT_GLYPH_H #include FT_MULTIPLE_MASTERS_H /* Variable font support. */ #include FT_TRUETYPE_IDS_H /* Codepoint coverage constants. */ @@ -54,7 +55,10 @@ BatchBLF g_batch; /* freetype2 handle ONLY for this file! */ -static FT_Library ft_lib; +static FT_Library ft_lib = NULL; +static FTC_Manager ftc_manager = NULL; +static FTC_CMapCache ftc_charmap_cache = NULL; + static SpinLock ft_lib_mutex; static SpinLock blf_glyph_cache_mutex; @@ -64,6 +68,53 @@ static void (*blf_draw_cache_flush)(void) = NULL; static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font); static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); +/* -------------------------------------------------------------------- */ +/** \name FreeType Caching + * \{ */ + +/* Called when a face is removed. FreeType will call FT_Done_Face itself. */ +static void blf_face_finalizer(void *object) +{ + FT_Face face = object; + FontBLF *font = (FontBLF *)face->generic.data; + font->face = NULL; +} + +/* Called in response to FTC_Manager_LookupFace. Add a face to our font. */ +FT_Error blf_cache_face_requester(FTC_FaceID faceID, + FT_Library lib, + FT_Pointer reqData, + FT_Face *face) +{ + FontBLF *font = (FontBLF *)faceID; + int err = FT_Err_Cannot_Open_Resource; + + BLI_spin_lock(font->ft_lib_mutex); + + if (font->filepath) { + err = FT_New_Face(lib, font->filepath, 0, face); + } + else if (font->mem) { + err = FT_New_Memory_Face(lib, font->mem, (FT_Long)font->mem_size, 0, face); + } + + BLI_spin_unlock(font->ft_lib_mutex); + + if (err == FT_Err_Ok) { + font->face = *face; + font->face->generic.data = font; + font->face->generic.finalizer = blf_face_finalizer; + } + + return err; +} + +/* Use cache, not blf_get_char_index, to return glyph id from charcode. */ +uint blf_get_char_index(struct FontBLF *font, uint charcode) +{ + return FTC_CMapCache_Lookup(ftc_charmap_cache, font, -1, charcode); +} + /* -------------------------------------------------------------------- */ /** \name FreeType Utilities (Internal) * \{ */ @@ -72,12 +123,12 @@ static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) { /* Scale value by font size using integer-optimized multiplication. */ - FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + FT_Long scaled = FT_MulFix(value, font->ft_size->metrics.x_scale); /* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */ /* kerning distances at small ppem values so that they don't become too big. */ - if (font->face->size->metrics.x_ppem < 25) { - scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + if (font->ft_size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->ft_size->metrics.x_ppem, 25); } return (ft_pix)scaled; @@ -296,7 +347,7 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph /* Small adjust if there is hinting. */ adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0); - if (FT_HAS_KERNING(font->face) && g_prev) { + if (FT_HAS_KERNING(font) && g_prev) { FT_Vector delta = {KERNING_ENTRY_UNSET}; /* Get unscaled kerning value from our cache if ASCII. */ @@ -305,7 +356,7 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph } /* If not ASCII or not found in cache, ask FreeType for kerning. */ - if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + if (UNLIKELY(font->face && delta.x == KERNING_ENTRY_UNSET)) { /* Note that this function sets delta values to zero on any error. */ FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); } @@ -925,8 +976,7 @@ static void blf_font_wrap_apply(FontBLF *font, int lines = 0; ft_pix pen_x_next = 0; - /* Space between lines needs to be aligned to the pixel grid (T97310). */ - ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font)); + ft_pix line_height = blf_font_height_max_ft_pix(font); GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -1090,7 +1140,7 @@ int blf_font_count_missing_chars(FontBLF *font, } else { c = BLI_str_utf8_as_unicode_step(str, str_len, &i); - if (FT_Get_Char_Index((font)->face, c) == 0) { + if (blf_get_char_index(font, c) == 0) { missing++; } } @@ -1107,18 +1157,8 @@ int blf_font_count_missing_chars(FontBLF *font, static ft_pix blf_font_height_max_ft_pix(FontBLF *font) { - ft_pix height_max; - FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - height_max = ft_pix_from_int((int)(face->ascender - face->descender) * - (int)face->size->metrics.y_ppem) / - (ft_pix)face->units_per_EM; - } - else { - height_max = (ft_pix)face->size->metrics.height; - } - /* can happen with size 1 fonts */ - return MAX2(height_max, ft_pix_from_int(1)); + /* Metrics.height is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.height, ft_pix_from_int(1)); } int blf_font_height_max(FontBLF *font) @@ -1128,18 +1168,8 @@ int blf_font_height_max(FontBLF *font) static ft_pix blf_font_width_max_ft_pix(FontBLF *font) { - ft_pix width_max; - const FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) * - (int)face->size->metrics.x_ppem) / - (ft_pix)face->units_per_EM; - } - else { - width_max = (ft_pix)face->size->metrics.max_advance; - } - /* can happen with size 1 fonts */ - return MAX2(width_max, ft_pix_from_int(1)); + /* Metrics.max_advance is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.max_advance, ft_pix_from_int(1)); } int blf_font_width_max(FontBLF *font) @@ -1149,12 +1179,12 @@ int blf_font_width_max(FontBLF *font) int blf_font_descender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.descender); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.descender); } int blf_font_ascender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.ascender); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.ascender); } char *blf_display_name(FontBLF *font) @@ -1176,13 +1206,31 @@ int blf_font_init(void) memset(&g_batch, 0, sizeof(g_batch)); BLI_spin_init(&ft_lib_mutex); BLI_spin_init(&blf_glyph_cache_mutex); - return FT_Init_FreeType(&ft_lib); + int err = FT_Init_FreeType(&ft_lib); + if (err == FT_Err_Ok) { + err = FTC_Manager_New(ft_lib, + BLF_CACHE_MAX_FACES, + BLF_CACHE_MAX_SIZES, + BLF_CACHE_BYTES, + blf_cache_face_requester, + NULL, + &ftc_manager); + if (err == FT_Err_Ok) { + err = FTC_CMapCache_New(ftc_manager, &ftc_charmap_cache); + } + } + return err; } void blf_font_exit(void) { - FT_Done_FreeType(ft_lib); BLI_spin_end(&ft_lib_mutex); + if (ftc_manager) { + FTC_Manager_Done(ftc_manager); + } + if (ft_lib) { + FT_Done_FreeType(ft_lib); + } BLI_spin_end(&blf_glyph_cache_mutex); blf_batch_draw_exit(); } @@ -1261,12 +1309,7 @@ bool blf_ensure_face(FontBLF *font) FT_Error err; - if (font->filepath) { - err = FT_New_Face(ft_lib, font->filepath, 0, &font->face); - } - if (font->mem) { - err = FT_New_Memory_Face(ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face); - } + err = FTC_Manager_LookupFace(ftc_manager, font, &font->face); if (err) { if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) { @@ -1306,7 +1349,9 @@ bool blf_ensure_face(FontBLF *font) } } - if (FT_HAS_MULTIPLE_MASTERS(font->face)) { + font->face_flags = font->face->face_flags; + + if (FT_HAS_MULTIPLE_MASTERS(font)) { FT_Get_MM_Var(font->face, &(font->variations)); } @@ -1319,11 +1364,11 @@ bool blf_ensure_face(FontBLF *font) font->UnicodeRanges[3] = (uint)os2_table->ulUnicodeRange4; } - if (FT_IS_FIXED_WIDTH(font->face)) { + if (FT_IS_FIXED_WIDTH(font)) { font->flags |= BLF_MONOSPACED; } - if (FT_HAS_KERNING(font->face) && !font->kerning_cache) { + if (FT_HAS_KERNING(font) && !font->kerning_cache) { /* Create kerning cache table and fill with value indicating "unset". */ font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { @@ -1438,7 +1483,9 @@ void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, const siz open.flags = FT_OPEN_MEMORY; open.memory_base = (const FT_Byte *)mem; open.memory_size = (FT_Long)mem_size; - FT_Attach_Stream(font->face, &open); + if (blf_ensure_face(font)) { + FT_Attach_Stream(font->face, &open); + } } void blf_font_free(FontBLF *font) @@ -1454,7 +1501,8 @@ void blf_font_free(FontBLF *font) } if (font->face) { - FT_Done_Face(font->face); + FTC_Manager_RemoveFaceID(ftc_manager, font); + font->face = NULL; } if (font->filepath) { MEM_freeN(font->filepath); @@ -1482,17 +1530,23 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi) /* Adjust our new size to be on even 64ths. */ size = (float)ft_size / 64.0f; - if (font->size != size || font->dpi != dpi) { - if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == FT_Err_Ok) { - font->size = size; - font->dpi = dpi; - } - else { - printf("The current font does not support the size, %f and DPI, %u\n", size, dpi); - return false; - } + FTC_ScalerRec scaler = {0}; + scaler.face_id = font; + scaler.width = 0; + scaler.height = ft_size; + scaler.pixel = 0; + scaler.x_res = dpi; + scaler.y_res = dpi; + + if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) != FT_Err_Ok) { + printf("The current font don't support the size, %f and dpi, %u\n", size, dpi); + return false; } + font->size = size; + font->dpi = dpi; + font->ft_size->generic.data = (void *)font; + return true; } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 48ddbc9f920..780b75c6296 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -94,16 +94,16 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->bucket, 0, sizeof(gc->bucket)); /* Determine ideal fixed-width size for monospaced output. */ - FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); - if (gindex) { + FT_UInt gindex = blf_get_char_index(font, U'0'); + if (gindex && font->face) { FT_Fixed advance = 0; FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); /* Use CSS 'ch unit' width, advance of zero character. */ gc->fixed_width = (int)(advance >> 16); } else { - /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ - gc->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + /* Font does not have a face or does not contain "0" so use CSS fallback of 1/2 of em. */ + gc->fixed_width = (int)((font->ft_size->metrics.height / 2) >> 6); } if (gc->fixed_width < 1) { gc->fixed_width = 1; @@ -565,7 +565,7 @@ static bool blf_font_has_coverage_bit(FontBLF *font, int coverage_bit) */ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode) { - FT_UInt glyph_index = FT_Get_Char_Index((*font)->face, charcode); + FT_UInt glyph_index = blf_get_char_index(*font, charcode); if (glyph_index) { return glyph_index; } @@ -584,12 +584,10 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode continue; } if (coverage_bit < 0 || blf_font_has_coverage_bit(f, coverage_bit)) { - if (blf_ensure_face(f)) { - glyph_index = FT_Get_Char_Index(f->face, charcode); - if (glyph_index) { - *font = f; - return glyph_index; - } + glyph_index = blf_get_char_index(f, charcode); + if (glyph_index) { + *font = f; + return glyph_index; } } } @@ -599,8 +597,8 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode #endif /* Not found in the stack, return from Last Resort if there is one. */ - if (last_resort && blf_ensure_face(last_resort)) { - glyph_index = FT_Get_Char_Index(last_resort->face, charcode); + if (last_resort) { + glyph_index = blf_get_char_index(last_resort, charcode); if (glyph_index) { *font = last_resort; return glyph_index; @@ -789,8 +787,8 @@ static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool mo { if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* Fake bold if the font does not have this variable axis. */ - const FT_Pos average_width = FT_MulFix(glyph->face->units_per_EM, - glyph->face->size->metrics.x_scale); + const FontBLF *font = (FontBLF *)glyph->face->generic.data; + const FT_Pos average_width = font->ft_size->metrics.height; FT_Pos change = (FT_Pos)((float)average_width * factor * 0.1f); FT_Outline_EmboldenXY(&glyph->outline, change, change / 2); if (monospaced) { @@ -849,7 +847,8 @@ static bool blf_glyph_transform_width(FT_GlyphSlot glyph, float factor) static bool blf_glyph_transform_spacing(FT_GlyphSlot glyph, float factor) { if (glyph->advance.x > 0) { - const long int size = glyph->face->size->metrics.height; + const FontBLF *font = (FontBLF *)glyph->face->generic.data; + const long int size = font->ft_size->metrics.height; glyph->advance.x += (FT_Pos)(factor * (float)size / 6.0f); return true; } @@ -898,13 +897,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, int fixed_width) { if (glyph_font != settings_font) { - FT_Set_Char_Size(glyph_font->face, - 0, - ((FT_F26Dot6)(settings_font->size)) * 64, - settings_font->dpi, - settings_font->dpi); - glyph_font->size = settings_font->size; - glyph_font->dpi = settings_font->dpi; + blf_font_size(glyph_font, settings_font->size, settings_font->dpi); } /* We need to keep track if changes are still needed. */ @@ -961,7 +954,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, /* Fallback glyph transforms, but only if required and not yet done. */ if (weight != 0.0f && !weight_done) { - blf_glyph_transform_weight(glyph, weight, glyph->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); + blf_glyph_transform_weight(glyph, weight, FT_IS_FIXED_WIDTH(glyph_font)); } if (slant != 0.0f && !slant_done) { blf_glyph_transform_slant(glyph, slant); @@ -990,11 +983,9 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) FontBLF *font_with_glyph = font; FT_UInt glyph_index = blf_glyph_index_from_charcode(&font_with_glyph, charcode); - /* Glyphs are dynamically created as needed by font rendering. this means that - * to make font rendering thread safe we have to do locking here. note that this - * must be a lock for the whole library and not just per font, because the font - * renderer uses a shared buffer internally. */ - BLI_spin_lock(font_with_glyph->ft_lib_mutex); + if (!blf_ensure_face(font_with_glyph)) { + return NULL; + } FT_GlyphSlot glyph = blf_glyph_render( font, font_with_glyph, glyph_index, charcode, gc->fixed_width); @@ -1004,7 +995,6 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) g = blf_glyph_cache_add_glyph(font, gc, glyph, charcode, glyph_index); } - BLI_spin_unlock(font_with_glyph->ft_lib_mutex); return g; } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 6207edb0107..8ff00d05e02 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -14,9 +14,16 @@ struct ResultBLF; struct rctf; struct rcti; -/* Max number of fonts in memory. Take care that every font has a glyph cache per size/dpi, +/* Max number of FontBLFs in memory. Take care that every font has a glyph cache per size/dpi, * so we don't need load the same font with different size, just load one and call BLF_size. */ -#define BLF_MAX_FONT 32 +#define BLF_MAX_FONT 64 + +/* Maximum number of opened FT_Face objects managed by cache. 0 is default of 2. */ +#define BLF_CACHE_MAX_FACES 0 +/* Maximum number of opened FT_Size objects managed by cache. 0 is default of 4 */ +#define BLF_CACHE_MAX_SIZES 0 +/* Maximum number of bytes to use for cached data nodes. 0 is default of 200,000. */ +#define BLF_CACHE_BYTES 0 extern struct FontBLF *global_font[BLF_MAX_FONT]; @@ -39,6 +46,8 @@ void blf_font_exit(void); bool blf_font_id_is_valid(int fontid); +uint blf_get_char_index(struct FontBLF *font, uint charcode); + bool blf_ensure_face(struct FontBLF *font); void blf_draw_buffer__start(struct FontBLF *font); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 018cef4540f..007b717ab93 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -329,6 +329,12 @@ typedef struct FontBLF { /* freetype2 face. */ FT_Face face; + /* FreeType size is separated from face when using their caching subsystem. */ + FT_Size ft_size; + + /* Copy of the font->face->face_flags, in case we don't have a face loaded. */ + FT_Long face_flags; + /* data for buffer usage (drawing into a texture buffer) */ FontBufInfoBLF buf_info; -- cgit v1.2.3