replaces broken font cache with improved cache - fixes #387 default tip

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

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
     1.1 --- a/src/Makefile	Thu Apr 18 22:53:55 2024 +0200
     1.2 +++ b/src/Makefile	Fri Apr 19 22:28:29 2024 +0200
     1.3 @@ -49,15 +49,16 @@
     1.4   ascension/datatypes.h ascension/window.h ascension/glcontext.h \
     1.5   ascension/primitives.h ascension/mesh.h ascension/shader.h \
     1.6   ascension/scene.h ascension/transform.h ascension/camera.h \
     1.7 - ascension/ui/font.h ascension/error.h ascension/utils.h
     1.8 + ascension/input.h ascension/ui/font.h ascension/error.h \
     1.9 + ascension/utils.h
    1.10  	@echo "Compiling $<"
    1.11  	$(CC) -o $@ $(CFLAGS) -c $<
    1.12  
    1.13  $(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \
    1.14   ascension/window.h ascension/glcontext.h ascension/primitives.h \
    1.15   ascension/mesh.h ascension/shader.h ascension/scene.h \
    1.16 - ascension/transform.h ascension/camera.h ascension/ui/font.h \
    1.17 - ascension/error.h ascension/utils.h
    1.18 + ascension/transform.h ascension/camera.h ascension/input.h \
    1.19 + ascension/ui/font.h ascension/error.h ascension/utils.h
    1.20  	@echo "Compiling $<"
    1.21  	$(CC) -o $@ $(CFLAGS) -c $<
    1.22  
    1.23 @@ -69,7 +70,7 @@
    1.24   ascension/datatypes.h ascension/window.h ascension/glcontext.h \
    1.25   ascension/primitives.h ascension/mesh.h ascension/shader.h \
    1.26   ascension/scene.h ascension/transform.h ascension/camera.h \
    1.27 - ascension/ui/font.h ascension/error.h
    1.28 + ascension/input.h ascension/ui/font.h ascension/error.h
    1.29  	@echo "Compiling $<"
    1.30  	$(CC) -o $@ $(CFLAGS) -c $<
    1.31  
    1.32 @@ -83,14 +84,15 @@
    1.33   ascension/mesh.h ascension/error.h ascension/context.h \
    1.34   ascension/datatypes.h ascension/window.h ascension/glcontext.h \
    1.35   ascension/primitives.h ascension/shader.h ascension/scene.h \
    1.36 - ascension/transform.h ascension/camera.h ascension/ui/font.h
    1.37 + ascension/transform.h ascension/camera.h ascension/input.h \
    1.38 + ascension/ui/font.h
    1.39  	@echo "Compiling $<"
    1.40  	$(CC) -o $@ $(CFLAGS) -c $<
    1.41  
    1.42  $(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/datatypes.h \
    1.43   ascension/transform.h ascension/camera.h ascension/context.h \
    1.44   ascension/window.h ascension/glcontext.h ascension/primitives.h \
    1.45 - ascension/mesh.h ascension/shader.h ascension/scene.h \
    1.46 + ascension/mesh.h ascension/shader.h ascension/scene.h ascension/input.h \
    1.47   ascension/ui/font.h ascension/utils.h ascension/shader.h
    1.48  	@echo "Compiling $<"
    1.49  	$(CC) -o $@ $(CFLAGS) -c $<
    1.50 @@ -106,8 +108,8 @@
    1.51   ascension/ui/../texture.h ascension/ui/../utils.h ascension/context.h \
    1.52   ascension/datatypes.h ascension/window.h ascension/glcontext.h \
    1.53   ascension/primitives.h ascension/mesh.h ascension/shader.h \
    1.54 - ascension/scene.h ascension/ui/font.h ascension/error.h \
    1.55 - ascension/shader.h
    1.56 + ascension/scene.h ascension/input.h ascension/ui/font.h \
    1.57 + ascension/error.h ascension/shader.h
    1.58  	@echo "Compiling $<"
    1.59  	$(CC) -o $@ $(CFLAGS) -c $<
    1.60  
    1.61 @@ -119,7 +121,8 @@
    1.62   ascension/glcontext.h ascension/primitives.h ascension/mesh.h \
    1.63   ascension/shader.h ascension/scene.h ascension/transform.h \
    1.64   ascension/camera.h ascension/context.h ascension/window.h \
    1.65 - ascension/ui/font.h ascension/error.h ascension/utils.h
    1.66 + ascension/input.h ascension/ui/font.h ascension/error.h \
    1.67 + ascension/utils.h
    1.68  	@echo "Compiling $<"
    1.69  	$(CC) -o $@ $(CFLAGS) -c $<
    1.70  
     2.1 --- a/src/ascension/context.h	Thu Apr 18 22:53:55 2024 +0200
     2.2 +++ b/src/ascension/context.h	Fri Apr 19 22:28:29 2024 +0200
     2.3 @@ -52,11 +52,9 @@
     2.4      CxBuffer error_buffer;
     2.5      AscInput input;
     2.6      AscWindow windows[ASC_MAX_WINDOWS];
     2.7 -    // TODO: rework how fonts are cached
     2.8 -    AscFont fonts[ASC_MAX_FONTS];
     2.9 +    AscFont active_font;
    2.10      unsigned char fonts_loaded;
    2.11      unsigned char active_window;
    2.12 -    unsigned char active_font;
    2.13      asc_col4i ink;
    2.14      uint64_t frame_nanos;
    2.15      uint64_t total_nanos;
    2.16 @@ -69,24 +67,23 @@
    2.17   * The currently active font.
    2.18   * @see asc_font()
    2.19   */
    2.20 -#define asc_active_font \
    2.21 -    (&asc_context.fonts[asc_context.active_font])
    2.22 +#define asc_active_font asc_context.active_font
    2.23  
    2.24 +#ifdef NDEBUG
    2.25  /**
    2.26   * The currently active window in the context.
    2.27   * @see asc_window_activate()
    2.28   */
    2.29  #define asc_active_window \
    2.30      (&asc_context.windows[asc_context.active_window])
    2.31 -
    2.32 -static inline bool asc_assert_active_window() {
    2.33 -    if (asc_context.active_window < ASC_MAX_WINDOWS) {
    2.34 -        return true;
    2.35 -    } else {
    2.36 -        asc_error("A graphics operation was performed without active window");
    2.37 -        return false;
    2.38 -    }
    2.39 -}
    2.40 +#else
    2.41 +/**
    2.42 + * The currently active window in the context.
    2.43 + * @see asc_window_activate()
    2.44 + */
    2.45 +#define asc_active_window asc_active_window_assert()
    2.46 +AscWindow *asc_active_window_assert(void);
    2.47 +#endif // NDEBUG
    2.48  
    2.49  void asc_context_initialize(void);
    2.50  void asc_context_destroy(void);
     3.1 --- a/src/ascension/ui/font.h	Thu Apr 18 22:53:55 2024 +0200
     3.2 +++ b/src/ascension/ui/font.h	Fri Apr 19 22:28:29 2024 +0200
     3.3 @@ -30,11 +30,6 @@
     3.4  
     3.5  #include <SDL2/SDL_ttf.h>
     3.6  
     3.7 -#ifndef ASC_MAX_FONTS
     3.8 -/** The maximum number of style/size combinations that can be loaded in parallel. */
     3.9 -#define ASC_MAX_FONTS 64u
    3.10 -#endif // ASC_MAX_FONTS
    3.11 -
    3.12  enum AscFontStyle {
    3.13      ASC_FONT_REGULAR,
    3.14      ASC_FONT_BOLD,
    3.15 @@ -51,11 +46,6 @@
    3.16       * Point size.
    3.17       */
    3.18      int size;
    3.19 -    /**
    3.20 -     * TODO: remove from public struct
    3.21 -     * Pointer to the SDL TTF font structure.
    3.22 -     */
    3.23 -    TTF_Font *ptr;
    3.24  } AscFont;
    3.25  
    3.26  /**
    3.27 @@ -73,4 +63,18 @@
    3.28   */
    3.29  void asc_font(enum AscFontStyle style, int size);
    3.30  
    3.31 +/**
    3.32 + * Loads the specified font.
    3.33 + *
    3.34 + * Loaded fonts are kept within a font cache and returned faster
    3.35 + * on the next invocation.
    3.36 + *
    3.37 + * You will almost never need to use this function on your own.
    3.38 + * Ascension UI elements are loading the required fonts for you.
    3.39 + *
    3.40 + * @param font the font description
    3.41 + * @return a pointer to the SDL TTF font handle, or \c NULL on error
    3.42 + */
    3.43 +TTF_Font *asc_font_load(AscFont font);
    3.44 +
    3.45  #endif //ASCENSION_FONT_H
     4.1 --- a/src/context.c	Thu Apr 18 22:53:55 2024 +0200
     4.2 +++ b/src/context.c	Fri Apr 19 22:28:29 2024 +0200
     4.3 @@ -42,14 +42,24 @@
     4.4      return 1000000000ull*(uint64_t)ts.tv_sec + (uint64_t)ts.tv_nsec;
     4.5  }
     4.6  
     4.7 +// forward declare non-public font cache functions
     4.8 +void asc_font_cache_init(void);
     4.9 +void asc_font_cache_destroy(void);
    4.10 +
    4.11  void asc_context_initialize(void) {
    4.12      if (asc_test_flag(asc_context.flags, ASC_FLAG_INITILIZED))
    4.13          return;
    4.14      memset(&asc_context, 0, sizeof(AscContext));
    4.15  
    4.16 -    // nothing is active right after initialization
    4.17 +    // initialize the font cache
    4.18 +    asc_font_cache_init();
    4.19 +
    4.20 +    // set a default font, but do not load it
    4.21 +    asc_context.active_font.style = ASC_FONT_REGULAR;
    4.22 +    asc_context.active_font.size = 14;
    4.23 +
    4.24 +    // no window, yet
    4.25      asc_context.active_window = ASC_MAX_WINDOWS;
    4.26 -    asc_context.active_font = ASC_MAX_FONTS;
    4.27  
    4.28      // initialize error buffer
    4.29      cxBufferInit(
    4.30 @@ -75,10 +85,13 @@
    4.31  }
    4.32  
    4.33  void asc_context_destroy(void) {
    4.34 +    // destroy windows
    4.35      for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
    4.36          asc_window_destroy(i);
    4.37      }
    4.38 -    // TODO: fix that fonts are currently leaking by reworking the font cache
    4.39 +
    4.40 +    // destroy the font cache
    4.41 +    asc_font_cache_destroy();
    4.42  
    4.43      // quit SDL
    4.44      if (TTF_WasInit())
    4.45 @@ -95,6 +108,15 @@
    4.46      asc_set_flag(asc_context.flags, ASC_FLAG_QUIT);
    4.47  }
    4.48  
    4.49 +AscWindow *asc_active_window_assert() {
    4.50 +    if (asc_context.active_window < ASC_MAX_WINDOWS) {
    4.51 +        return &asc_context.windows[asc_context.active_window];
    4.52 +    } else {
    4.53 +        asc_dprintf("Window access attempted without active window");
    4.54 +        abort();
    4.55 +    }
    4.56 +}
    4.57 +
    4.58  static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) {
    4.59      for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) {
    4.60          if (asc_context.windows[i].id == id) {
     5.1 --- a/src/font.c	Thu Apr 18 22:53:55 2024 +0200
     5.2 +++ b/src/font.c	Fri Apr 19 22:28:29 2024 +0200
     5.3 @@ -29,6 +29,14 @@
     5.4  #include "ascension/context.h"
     5.5  #include "ascension/error.h"
     5.6  
     5.7 +#include <assert.h>
     5.8 +#include <cx/array_list.h>
     5.9 +
    5.10 +void asc_font(enum AscFontStyle style, int size) {
    5.11 +    asc_context.active_font.style = style;
    5.12 +    asc_context.active_font.size = size;
    5.13 +}
    5.14 +
    5.15  static char const *asc_font_filename(enum AscFontStyle style) {
    5.16      // TODO: do not assume we are running from the program dir
    5.17      switch (style) {
    5.18 @@ -43,31 +51,49 @@
    5.19      }
    5.20  }
    5.21  
    5.22 -void asc_font(enum AscFontStyle style, int size) {
    5.23 -    for (unsigned int i = 0 ; i < asc_context.fonts_loaded ; i++) {
    5.24 -        AscFont *font = &asc_context.fonts[i];
    5.25 -        if (font->size == size && font->style == style) {
    5.26 -            asc_context.active_font = i;
    5.27 -            return;
    5.28 +struct asc_font_cache_entry {
    5.29 +    AscFont font;
    5.30 +    TTF_Font *ttf;
    5.31 +};
    5.32 +
    5.33 +static CxList *asc_font_cache;
    5.34 +
    5.35 +static void asc_font_unload(struct asc_font_cache_entry *entry) {
    5.36 +    TTF_CloseFont(entry->ttf);
    5.37 +    asc_dprintf("Closed font size %u, style %u", entry->font.size, entry->font.style);
    5.38 +}
    5.39 +
    5.40 +void asc_font_cache_init(void) {
    5.41 +    assert(asc_font_cache == NULL);
    5.42 +    asc_font_cache = cxArrayListCreateSimple(
    5.43 +            sizeof(struct asc_font_cache_entry), 16
    5.44 +    );
    5.45 +    asc_font_cache->simple_destructor = (cx_destructor_func) asc_font_unload;
    5.46 +}
    5.47 +
    5.48 +void asc_font_cache_destroy(void) {
    5.49 +    assert(asc_font_cache != NULL);
    5.50 +    cxListDestroy(asc_font_cache);
    5.51 +}
    5.52 +
    5.53 +
    5.54 +TTF_Font *asc_font_load(AscFont font) {
    5.55 +    CxIterator iter = cxListIterator(asc_font_cache);
    5.56 +    cx_foreach(struct asc_font_cache_entry*, cache, iter) {
    5.57 +        if (cache->font.style == font.style && cache->font.size == font.size) {
    5.58 +            return cache->ttf;
    5.59          }
    5.60      }
    5.61  
    5.62 -    if (asc_context.fonts_loaded == ASC_MAX_FONTS) {
    5.63 -        asc_error("Too many fonts. Cannot load more until cache is repaired.");
    5.64 -        asc_context.active_font = ASC_MAX_FONTS;
    5.65 -        return;
    5.66 -    }
    5.67 -
    5.68 -    unsigned int slot = asc_context.fonts_loaded++;
    5.69 -    AscFont *font = &asc_context.fonts[slot];
    5.70 -    font->size = size;
    5.71 -    font->style = style;
    5.72 -    font->ptr = TTF_OpenFont(asc_font_filename(style), size);
    5.73 -    if (font->ptr == NULL) {
    5.74 -        asc_context.fonts_loaded--;
    5.75 +    struct asc_font_cache_entry entry;
    5.76 +    entry.font = font;
    5.77 +    entry.ttf = TTF_OpenFont(asc_font_filename(font.style), font.size);
    5.78 +    if (entry.ttf == NULL) {
    5.79          asc_error(TTF_GetError());
    5.80 +        return NULL;
    5.81      } else {
    5.82 -        asc_dprintf("Loaded font size %u, style %u in slot %u", size, style, slot);
    5.83 -        asc_context.active_font = slot;
    5.84 +        cxListAdd(asc_font_cache, &entry);
    5.85 +        asc_dprintf("Loaded font size %d, style %d from disk", font.size, font.style);
    5.86 +        return entry.ttf;
    5.87      }
    5.88  }
     6.1 --- a/src/text.c	Thu Apr 18 22:53:55 2024 +0200
     6.2 +++ b/src/text.c	Fri Apr 19 22:28:29 2024 +0200
     6.3 @@ -59,8 +59,7 @@
     6.4      }
     6.5  
     6.6      // Render text onto a surface
     6.7 -    // TODO: obtain TTF_Font from font cache once it is repaired
     6.8 -    TTF_Font *font = node->font.ptr;
     6.9 +    TTF_Font *font = asc_font_load(node->font);
    6.10      static int alignments[] = {
    6.11              TTF_WRAPPED_ALIGN_LEFT,
    6.12              TTF_WRAPPED_ALIGN_CENTER,
    6.13 @@ -102,7 +101,7 @@
    6.14      node->base.position.x = (float) args.x;
    6.15      node->base.position.y = (float) args.y;
    6.16      node->max_width = args.max_width;
    6.17 -    node->font = *asc_active_font;
    6.18 +    node->font = asc_active_font;
    6.19      node->color = asc_context.ink;
    6.20      if (args.text == NULL) {
    6.21          node->text = cx_mutstr(strdup(" "));

mercurial