Fri, 19 Apr 2024 22:28:29 +0200
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(" "));