replaces broken font cache with improved cache - fixes #387

Fri, 19 Apr 2024 22:28:29 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 19 Apr 2024 22:28:29 +0200
changeset 66
8297afa1c29c
parent 65
9c44c55d327a
child 67
0b96fe6d6b5e

replaces broken font cache with improved cache - fixes #387

src/Makefile file | annotate | diff | comparison | revisions
src/ascension/context.h file | annotate | diff | comparison | revisions
src/ascension/ui/font.h file | annotate | diff | comparison | revisions
src/context.c file | annotate | diff | comparison | revisions
src/font.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
--- a/src/Makefile	Thu Apr 18 22:53:55 2024 +0200
+++ b/src/Makefile	Fri Apr 19 22:28:29 2024 +0200
@@ -49,15 +49,16 @@
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
  ascension/primitives.h ascension/mesh.h ascension/shader.h \
  ascension/scene.h ascension/transform.h ascension/camera.h \
- ascension/ui/font.h ascension/error.h ascension/utils.h
+ ascension/input.h ascension/ui/font.h ascension/error.h \
+ ascension/utils.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \
  ascension/window.h ascension/glcontext.h ascension/primitives.h \
  ascension/mesh.h ascension/shader.h ascension/scene.h \
- ascension/transform.h ascension/camera.h ascension/ui/font.h \
- ascension/error.h ascension/utils.h
+ ascension/transform.h ascension/camera.h ascension/input.h \
+ ascension/ui/font.h ascension/error.h ascension/utils.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
@@ -69,7 +70,7 @@
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
  ascension/primitives.h ascension/mesh.h ascension/shader.h \
  ascension/scene.h ascension/transform.h ascension/camera.h \
- ascension/ui/font.h ascension/error.h
+ ascension/input.h ascension/ui/font.h ascension/error.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
@@ -83,14 +84,15 @@
  ascension/mesh.h ascension/error.h ascension/context.h \
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
  ascension/primitives.h ascension/shader.h ascension/scene.h \
- ascension/transform.h ascension/camera.h ascension/ui/font.h
+ ascension/transform.h ascension/camera.h ascension/input.h \
+ ascension/ui/font.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/datatypes.h \
  ascension/transform.h ascension/camera.h ascension/context.h \
  ascension/window.h ascension/glcontext.h ascension/primitives.h \
- ascension/mesh.h ascension/shader.h ascension/scene.h \
+ ascension/mesh.h ascension/shader.h ascension/scene.h ascension/input.h \
  ascension/ui/font.h ascension/utils.h ascension/shader.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
@@ -106,8 +108,8 @@
  ascension/ui/../texture.h ascension/ui/../utils.h ascension/context.h \
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
  ascension/primitives.h ascension/mesh.h ascension/shader.h \
- ascension/scene.h ascension/ui/font.h ascension/error.h \
- ascension/shader.h
+ ascension/scene.h ascension/input.h ascension/ui/font.h \
+ ascension/error.h ascension/shader.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
@@ -119,7 +121,8 @@
  ascension/glcontext.h ascension/primitives.h ascension/mesh.h \
  ascension/shader.h ascension/scene.h ascension/transform.h \
  ascension/camera.h ascension/context.h ascension/window.h \
- ascension/ui/font.h ascension/error.h ascension/utils.h
+ ascension/input.h ascension/ui/font.h ascension/error.h \
+ ascension/utils.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
--- a/src/ascension/context.h	Thu Apr 18 22:53:55 2024 +0200
+++ b/src/ascension/context.h	Fri Apr 19 22:28:29 2024 +0200
@@ -52,11 +52,9 @@
     CxBuffer error_buffer;
     AscInput input;
     AscWindow windows[ASC_MAX_WINDOWS];
-    // TODO: rework how fonts are cached
-    AscFont fonts[ASC_MAX_FONTS];
+    AscFont active_font;
     unsigned char fonts_loaded;
     unsigned char active_window;
-    unsigned char active_font;
     asc_col4i ink;
     uint64_t frame_nanos;
     uint64_t total_nanos;
@@ -69,24 +67,23 @@
  * The currently active font.
  * @see asc_font()
  */
-#define asc_active_font \
-    (&asc_context.fonts[asc_context.active_font])
+#define asc_active_font asc_context.active_font
 
+#ifdef NDEBUG
 /**
  * The currently active window in the context.
  * @see asc_window_activate()
  */
 #define asc_active_window \
     (&asc_context.windows[asc_context.active_window])
-
-static inline bool asc_assert_active_window() {
-    if (asc_context.active_window < ASC_MAX_WINDOWS) {
-        return true;
-    } else {
-        asc_error("A graphics operation was performed without active window");
-        return false;
-    }
-}
+#else
+/**
+ * The currently active window in the context.
+ * @see asc_window_activate()
+ */
+#define asc_active_window asc_active_window_assert()
+AscWindow *asc_active_window_assert(void);
+#endif // NDEBUG
 
 void asc_context_initialize(void);
 void asc_context_destroy(void);
--- a/src/ascension/ui/font.h	Thu Apr 18 22:53:55 2024 +0200
+++ b/src/ascension/ui/font.h	Fri Apr 19 22:28:29 2024 +0200
@@ -30,11 +30,6 @@
 
 #include <SDL2/SDL_ttf.h>
 
-#ifndef ASC_MAX_FONTS
-/** The maximum number of style/size combinations that can be loaded in parallel. */
-#define ASC_MAX_FONTS 64u
-#endif // ASC_MAX_FONTS
-
 enum AscFontStyle {
     ASC_FONT_REGULAR,
     ASC_FONT_BOLD,
@@ -51,11 +46,6 @@
      * Point size.
      */
     int size;
-    /**
-     * TODO: remove from public struct
-     * Pointer to the SDL TTF font structure.
-     */
-    TTF_Font *ptr;
 } AscFont;
 
 /**
@@ -73,4 +63,18 @@
  */
 void asc_font(enum AscFontStyle style, int size);
 
+/**
+ * Loads the specified font.
+ *
+ * Loaded fonts are kept within a font cache and returned faster
+ * on the next invocation.
+ *
+ * You will almost never need to use this function on your own.
+ * Ascension UI elements are loading the required fonts for you.
+ *
+ * @param font the font description
+ * @return a pointer to the SDL TTF font handle, or \c NULL on error
+ */
+TTF_Font *asc_font_load(AscFont font);
+
 #endif //ASCENSION_FONT_H
--- a/src/context.c	Thu Apr 18 22:53:55 2024 +0200
+++ b/src/context.c	Fri Apr 19 22:28:29 2024 +0200
@@ -42,14 +42,24 @@
     return 1000000000ull*(uint64_t)ts.tv_sec + (uint64_t)ts.tv_nsec;
 }
 
+// forward declare non-public font cache functions
+void asc_font_cache_init(void);
+void asc_font_cache_destroy(void);
+
 void asc_context_initialize(void) {
     if (asc_test_flag(asc_context.flags, ASC_FLAG_INITILIZED))
         return;
     memset(&asc_context, 0, sizeof(AscContext));
 
-    // nothing is active right after initialization
+    // initialize the font cache
+    asc_font_cache_init();
+
+    // set a default font, but do not load it
+    asc_context.active_font.style = ASC_FONT_REGULAR;
+    asc_context.active_font.size = 14;
+
+    // no window, yet
     asc_context.active_window = ASC_MAX_WINDOWS;
-    asc_context.active_font = ASC_MAX_FONTS;
 
     // initialize error buffer
     cxBufferInit(
@@ -75,10 +85,13 @@
 }
 
 void asc_context_destroy(void) {
+    // destroy windows
     for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
         asc_window_destroy(i);
     }
-    // TODO: fix that fonts are currently leaking by reworking the font cache
+
+    // destroy the font cache
+    asc_font_cache_destroy();
 
     // quit SDL
     if (TTF_WasInit())
@@ -95,6 +108,15 @@
     asc_set_flag(asc_context.flags, ASC_FLAG_QUIT);
 }
 
+AscWindow *asc_active_window_assert() {
+    if (asc_context.active_window < ASC_MAX_WINDOWS) {
+        return &asc_context.windows[asc_context.active_window];
+    } else {
+        asc_dprintf("Window access attempted without active window");
+        abort();
+    }
+}
+
 static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) {
     for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
         if (asc_context.windows[i].id == id) {
--- a/src/font.c	Thu Apr 18 22:53:55 2024 +0200
+++ b/src/font.c	Fri Apr 19 22:28:29 2024 +0200
@@ -29,6 +29,14 @@
 #include "ascension/context.h"
 #include "ascension/error.h"
 
+#include <assert.h>
+#include <cx/array_list.h>
+
+void asc_font(enum AscFontStyle style, int size) {
+    asc_context.active_font.style = style;
+    asc_context.active_font.size = size;
+}
+
 static char const *asc_font_filename(enum AscFontStyle style) {
     // TODO: do not assume we are running from the program dir
     switch (style) {
@@ -43,31 +51,49 @@
     }
 }
 
-void asc_font(enum AscFontStyle style, int size) {
-    for (unsigned int i = 0 ; i < asc_context.fonts_loaded ; i++) {
-        AscFont *font = &asc_context.fonts[i];
-        if (font->size == size && font->style == style) {
-            asc_context.active_font = i;
-            return;
+struct asc_font_cache_entry {
+    AscFont font;
+    TTF_Font *ttf;
+};
+
+static CxList *asc_font_cache;
+
+static void asc_font_unload(struct asc_font_cache_entry *entry) {
+    TTF_CloseFont(entry->ttf);
+    asc_dprintf("Closed font size %u, style %u", entry->font.size, entry->font.style);
+}
+
+void asc_font_cache_init(void) {
+    assert(asc_font_cache == NULL);
+    asc_font_cache = cxArrayListCreateSimple(
+            sizeof(struct asc_font_cache_entry), 16
+    );
+    asc_font_cache->simple_destructor = (cx_destructor_func) asc_font_unload;
+}
+
+void asc_font_cache_destroy(void) {
+    assert(asc_font_cache != NULL);
+    cxListDestroy(asc_font_cache);
+}
+
+
+TTF_Font *asc_font_load(AscFont font) {
+    CxIterator iter = cxListIterator(asc_font_cache);
+    cx_foreach(struct asc_font_cache_entry*, cache, iter) {
+        if (cache->font.style == font.style && cache->font.size == font.size) {
+            return cache->ttf;
         }
     }
 
-    if (asc_context.fonts_loaded == ASC_MAX_FONTS) {
-        asc_error("Too many fonts. Cannot load more until cache is repaired.");
-        asc_context.active_font = ASC_MAX_FONTS;
-        return;
-    }
-
-    unsigned int slot = asc_context.fonts_loaded++;
-    AscFont *font = &asc_context.fonts[slot];
-    font->size = size;
-    font->style = style;
-    font->ptr = TTF_OpenFont(asc_font_filename(style), size);
-    if (font->ptr == NULL) {
-        asc_context.fonts_loaded--;
+    struct asc_font_cache_entry entry;
+    entry.font = font;
+    entry.ttf = TTF_OpenFont(asc_font_filename(font.style), font.size);
+    if (entry.ttf == NULL) {
         asc_error(TTF_GetError());
+        return NULL;
     } else {
-        asc_dprintf("Loaded font size %u, style %u in slot %u", size, style, slot);
-        asc_context.active_font = slot;
+        cxListAdd(asc_font_cache, &entry);
+        asc_dprintf("Loaded font size %d, style %d from disk", font.size, font.style);
+        return entry.ttf;
     }
 }
--- a/src/text.c	Thu Apr 18 22:53:55 2024 +0200
+++ b/src/text.c	Fri Apr 19 22:28:29 2024 +0200
@@ -59,8 +59,7 @@
     }
 
     // Render text onto a surface
-    // TODO: obtain TTF_Font from font cache once it is repaired
-    TTF_Font *font = node->font.ptr;
+    TTF_Font *font = asc_font_load(node->font);
     static int alignments[] = {
             TTF_WRAPPED_ALIGN_LEFT,
             TTF_WRAPPED_ALIGN_CENTER,
@@ -102,7 +101,7 @@
     node->base.position.x = (float) args.x;
     node->base.position.y = (float) args.y;
     node->max_width = args.max_width;
-    node->font = *asc_active_font;
+    node->font = asc_active_font;
     node->color = asc_context.ink;
     if (args.text == NULL) {
         node->text = cx_mutstr(strdup(" "));

mercurial