Commit: 369f2e36d8cba0ca74369a1ec4497537f62d21b3
Parent: 911e62d70bd3674b0da791e5bb067ded5d5cfb1f
Author: Randy Palamar
Date: Mon, 2 Sep 2024 13:33:20 -0600
improve robustness of glyph searching/uploading
* When a Bold/Italic Glyph is not found for the font we should
just try the same font with normal style first before we try a
new font
* We should just use a variable to check if the glyph has been
uploaded to the GPU or not. This way we can ensure we don't end
up drawing an empty texture
* ' ' can have its width adjusted when rendered, that is already
the slow path.
Diffstat:
4 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/config.def.h b/config.def.h
@@ -1,15 +1,17 @@
/* See LICENSE for copyright details */
static FontDesc g_fonts[][FS_LAST] = {
{
- [FS_NORMAL] = {.path = "/usr/share/fonts/gofont/Go-Mono.ttf", .height = 24},
- [FS_BOLD] = {.path = "/usr/share/fonts/gofont/Go-Mono-Bold.ttf", .height = 24},
+ [FS_NORMAL] = {.path = "/usr/share/fonts/gofont/Go-Mono.ttf", .height = 24},
+ [FS_BOLD] = {.path = "/usr/share/fonts/gofont/Go-Mono-Bold.ttf", .height = 24},
+ [FS_ITALIC] = {.path = "/usr/share/fonts/gofont/Go-Mono-Italic.ttf", .height = 24},
+ [FS_BOLD_ITALIC] = {.path = "/usr/share/fonts/gofont/Go-Mono-Bold-Italic.ttf", .height = 24},
},
};
/* NOTE: terminal padding in pixels */
static v2 g_term_pad = {.w = 8, .h = 8};
/* NOTE: cell padding in pixels (glyphs will be centered) */
-static v2 g_cell_pad = {.w = 0, .h = 2};
+static v2 g_cell_pad = {.w = 0, .h = 0};
static u8 g_tabstop = 8;
diff --git a/font.c b/font.c
@@ -45,8 +45,6 @@ recycle_cache(GlyphCache *gc)
cg->next_with_same_hash = sentinel->next_with_same_hash;
sentinel->next_with_same_hash = last_index;
- cg->g.size = (uv2){0};
-
gc->stats.recycle_count++;
}
@@ -64,6 +62,7 @@ pop_free_glyph_entry(GlyphCache *gc)
CachedGlyph *cg = gc->glyphs + result;
sentinel->next_with_same_hash = cg->next_with_same_hash;
cg->next_with_same_hash = 0;
+ cg->uploaded_to_gpu = 0;
return result;
}
@@ -124,18 +123,17 @@ static u32 glyph_buf[MAX_FONT_SIZE * MAX_FONT_SIZE * sizeof(u32)];
static u8 temp_buf[MAX_FONT_SIZE * MAX_FONT_SIZE * sizeof(u8)];
static u32 *
-render_glyph(FontAtlas *fa, u32 cp, enum face_style style, Glyph *out_glyph, u32 *out_idx)
+render_glyph(FontAtlas *fa, u32 cp, enum face_style style, CachedGlyph **out_glyph, u32 *out_idx)
{
/* NOTE: first check if glyph is in the cache and valid */
+ /* NOTE: 8 MSB are not used for UTF8 so we can use that to store the style of the glyph */
u32 idx = get_glyph_entry_index(&fa->glyph_cache, cp|(1 << (30 - style)));
CachedGlyph *cg = fa->glyph_cache.glyphs + idx;
- *out_idx = idx;
- /* TODO: implement a way of ensuring the glyph is actually uploaded to the GPU */
- if (cg->g.size.w) {
- *out_glyph = cg->g;
+ *out_idx = idx;
+ *out_glyph = cg;
+ if (cg->uploaded_to_gpu)
return NULL;
- }
i32 glyph_idx = 0;
i32 font_idx = 0;
@@ -143,6 +141,8 @@ render_glyph(FontAtlas *fa, u32 cp, enum face_style style, Glyph *out_glyph, u32
if (!fa->fonts[i][style].buf)
style = FS_NORMAL;
glyph_idx = stbtt_FindGlyphIndex(&fa->fonts[i][style].font_info, cp);
+ if (!glyph_idx)
+ glyph_idx = stbtt_FindGlyphIndex(&fa->fonts[i][FS_NORMAL].font_info, cp);
if (glyph_idx) {
font_idx = i;
break;
@@ -175,7 +175,10 @@ render_glyph(FontAtlas *fa, u32 cp, enum face_style style, Glyph *out_glyph, u32
rgba_bitmap[i * cg->g.size.w + j] = pixel;
}
}
- *out_glyph = cg->g;
+
+ /* NOTE: ' ' has 0 size in freetype but we need it to have a width! */
+ if (cp == ' ') cg->g.size.w = fa->size.w;
+
return rgba_bitmap;
}
@@ -188,17 +191,10 @@ update_font_metrics(FontAtlas *fa)
fa->size.h = scale * (y1 - y0);
fa->size.w = scale * (x1 - x0);
fa->deltay = -scale * y0;
-
- /* NOTE: ' ' has 0 size in freetype but we need it to have a width! */
- /* TODO: technically this is a hack which assumes that ' ' will always be in the cache */
- Glyph g;
- u32 index;
- render_glyph(fa, ' ', FS_NORMAL, &g, &index);
- fa->glyph_cache.glyphs[index].g.size.w = fa->size.w;
}
static void
-initialize_glyph_cache(FontAtlas *fa)
+font_atlas_update(FontAtlas *fa)
{
GlyphCache *gc = &fa->glyph_cache;
mem_clear((u8 *)gc->glyphs, 0, sizeof(*gc->glyphs) * gc->cache_len);
@@ -206,6 +202,7 @@ initialize_glyph_cache(FontAtlas *fa)
for(u32 i = 0; i < gc->cache_len - 1; i++)
gc->glyphs[i].next_with_same_hash = i + 1;
get_and_clear_glyph_cache_stats(gc);
+ update_font_metrics(fa);
}
static i32
@@ -214,6 +211,8 @@ shift_font_sizes(FontAtlas *fa, i32 size_delta)
for (u32 i = 0; i < fa->nfonts; i++) {
for (u32 j = 0; j < FS_LAST; j++) {
Font *f = &fa->fonts[i][j];
+ if (!f->buf) continue;
+
i32 newsize = f->fontsize + size_delta;
CLAMP(newsize, 8, MAX_FONT_SIZE);
f->fontsize = newsize;
@@ -226,22 +225,23 @@ shift_font_sizes(FontAtlas *fa, i32 size_delta)
static void
init_fonts(Term *t, Arena *a)
{
- u32 nfontstrs = ARRAY_COUNT(g_fonts);
+ FontAtlas *fa = &t->fa;
+ u32 n_fonts = ARRAY_COUNT(g_fonts);
- for (u32 i = 0; i < nfontstrs; i++)
- t->fa.fonts[i] = alloc(a, Font, FS_LAST);
- for (t->fa.nfonts = 0; t->fa.nfonts < nfontstrs; t->fa.nfonts++) {
+ fa->fonts = alloc(a, typeof(*fa->fonts), n_fonts);
+ for (fa->nfonts = 0; fa->nfonts < n_fonts; fa->nfonts++) {
for (u32 i = 0; i < FS_LAST; i++) {
- if (!g_fonts[t->fa.nfonts][i].path)
+ if (!g_fonts[fa->nfonts][i].path)
continue;
- init_font(&t->fa, &t->fa.fonts[t->fa.nfonts][i], &g_fonts[t->fa.nfonts][i]);
+ init_font(fa, &fa->fonts[fa->nfonts][i], &g_fonts[fa->nfonts][i]);
}
}
static_assert(ISPOWEROFTWO(GLYPH_CACHE_LEN), "GLYPH_CACHE_LEN must be a power of two!");
- t->fa.glyph_cache.cache_len = GLYPH_CACHE_LEN;
- t->fa.glyph_cache.glyphs = alloc(a, CachedGlyph, t->fa.glyph_cache.cache_len);
- t->fa.glyph_cache.hash_table = alloc(a, u32, t->fa.glyph_cache.cache_len);
+ GlyphCache *gc = &fa->glyph_cache;
+ gc->cache_len = GLYPH_CACHE_LEN;
+ gc->glyphs = alloc(a, typeof(*gc->glyphs), gc->cache_len);
+ gc->hash_table = alloc(a, typeof(*gc->hash_table), gc->cache_len);
- initialize_glyph_cache(&t->fa);
+ font_atlas_update(fa);
}
diff --git a/util.h b/util.h
@@ -290,13 +290,15 @@ typedef struct {
} FontDesc;
typedef struct {
+ stbtt_fontinfo font_info;
u8 *buf;
i32 bufsize;
i32 fontsize;
f32 stbtt_scale;
- stbtt_fontinfo font_info;
} Font;
+typedef Font FontFamily[FS_LAST];
+
#define GLYPH_CACHE_LEN PUSH_BUFFER_CAP
typedef struct {
/* distance to shift glyph from bounding box origin */
@@ -310,6 +312,7 @@ typedef struct {
u32 cp;
u32 next_with_same_hash;
u16 prev, next;
+ b32 uploaded_to_gpu;
} CachedGlyph;
typedef struct {
@@ -326,7 +329,7 @@ typedef struct {
} GlyphCache;
typedef struct {
- Font *fonts[FS_LAST];
+ FontFamily *fonts;
u32 nfonts;
uv2 size;
i32 deltay;
diff --git a/vtgl.c b/vtgl.c
@@ -134,19 +134,7 @@ update_font_textures(GLCtx *gl, FontAtlas *fa)
MAX_FONT_SIZE, MAX_FONT_SIZE, TEXTURE_GLYPH_COUNT,
0, GL_RED, GL_UNSIGNED_BYTE, 0);
- initialize_glyph_cache(fa);
-
- /* TODO: remove this */
- u32 depth_idx;
- Glyph g;
- u32 *data = render_glyph(fa, ' ', FS_NORMAL, &g, &depth_idx);
- if (data) {
- ASSERT(depth_idx);
- glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, depth_idx,
- g.size.w, g.size.h, 1, GL_RGBA,
- GL_UNSIGNED_BYTE, data);
- }
- update_font_metrics(fa);
+ font_atlas_update(fa);
}
static void
@@ -183,13 +171,17 @@ static i32
get_gpu_glyph_index(FontAtlas *fa, u32 codepoint, enum face_style style, Glyph *out_glyph)
{
u32 depth_idx;
- u32 *data = render_glyph(fa, codepoint, style, out_glyph, &depth_idx);
+ CachedGlyph *cg;
+ u32 *data = render_glyph(fa, codepoint, style, &cg, &depth_idx);
+ *out_glyph = cg->g;
if (data) {
ASSERT(depth_idx);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, depth_idx,
out_glyph->size.w, out_glyph->size.h, 1, GL_RGBA,
GL_UNSIGNED_BYTE, data);
+ cg->uploaded_to_gpu = 1;
}
+ ASSERT(cg->uploaded_to_gpu);
return depth_idx;
}
@@ -590,6 +582,9 @@ fb_callback(GLFWwindow *win, i32 w, i32 h)
glBindTexture(GL_TEXTURE_2D, t->gl.fb_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ /* NOTE: reactive the glyph texture unit */
+ glActiveTexture(GL_TEXTURE0);
+
t->gl.flags |= NEEDS_RESIZE|NEEDS_BLIT;
}