# HG changeset patch # User Mike Becker # Date 1713473635 -7200 # Node ID 9c44c55d327ab9c8776ca541b0b308be49ab25d6 # Parent f18dc427f86f8ecc7fa7ed18512c2ca7ec2cf213 consistently refer to windows by ID - fixes #381 This change discovered that the font cache is completely broken. We created issue #387 for this. diff -r f18dc427f86f -r 9c44c55d327a src/ascension/context.h --- a/src/ascension/context.h Thu Apr 18 21:53:53 2024 +0200 +++ b/src/ascension/context.h Thu Apr 18 22:53:55 2024 +0200 @@ -52,10 +52,11 @@ CxBuffer error_buffer; AscInput input; AscWindow windows[ASC_MAX_WINDOWS]; - AscWindow *active_window; + // TODO: rework how fonts are cached AscFont fonts[ASC_MAX_FONTS]; - unsigned int fonts_loaded; - AscFont const *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; @@ -64,6 +65,29 @@ /** Global ascension context. */ extern AscContext asc_context; +/** + * The currently active font. + * @see asc_font() + */ +#define asc_active_font \ + (&asc_context.fonts[asc_context.active_font]) + +/** + * 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; + } +} + void asc_context_initialize(void); void asc_context_destroy(void); @@ -86,10 +110,5 @@ #define asc_ink_rgba(r,g,b,a) asc_context.ink = (asc_col4i){(r),(g),(b),(a)} #define asc_ink_rgb(r,g,b) asc_context.ink = (asc_col4i){(r),(g),(b),255u} -/** - * Sets the active drawing font. - */ -#define asc_set_font(font) asc_context.active_font = (font) - #endif /* ASCENSION_CONTEXT_H */ diff -r f18dc427f86f -r 9c44c55d327a src/ascension/input.h --- a/src/ascension/input.h Thu Apr 18 21:53:53 2024 +0200 +++ b/src/ascension/input.h Thu Apr 18 22:53:55 2024 +0200 @@ -45,4 +45,12 @@ #define asc_key_pressed(scancode) \ (asc_context.input.keys[SDL_SCANCODE_##scancode]) +#define asc_mouse_x asc_context.input.mouse_x +#define asc_mouse_y asc_context.input.mouse_y +#define asc_mouse_pos (asc_vec2i) {asc_mouse_x, asc_mouse_y} + +#define asc_mouse_move_x asc_context.input.mouse_xrel +#define asc_mouse_move_y asc_context.input.mouse_yrel +#define asc_mouse_move (asc_vec2i) {asc_mouse_move_x, asc_mouse_move_y} + #endif //ASCENSION_INPUT_H diff -r f18dc427f86f -r 9c44c55d327a src/ascension/ui.h --- a/src/ascension/ui.h Thu Apr 18 21:53:53 2024 +0200 +++ b/src/ascension/ui.h Thu Apr 18 22:53:55 2024 +0200 @@ -31,7 +31,7 @@ #include "ui/text.h" #define asc_add_ui_node(node) \ - asc_scene_node_link(asc_window_active->ui, node) + asc_scene_node_link(asc_active_window->ui, node) #endif /* ASCENSION_UI_H */ diff -r f18dc427f86f -r 9c44c55d327a src/ascension/ui/font.h --- a/src/ascension/ui/font.h Thu Apr 18 21:53:53 2024 +0200 +++ b/src/ascension/ui/font.h Thu Apr 18 22:53:55 2024 +0200 @@ -52,43 +52,25 @@ */ int size; /** + * TODO: remove from public struct * Pointer to the SDL TTF font structure. */ TTF_Font *ptr; } AscFont; /** - * Loads a font with the given style and size. + * Activates a font with the given style and size. * - * The font is cached and returned faster the next time you call this + * The font is cached and activated faster the next time you call this * function with the same arguments. However, when you reach the maximum * number of fonts, the cache is completely cleared and rebuilt. * * That means in general, that you should not be using too many fonts of - * different sizes at the same time, and you should not keep the pointer - * returned by this function too long, because you would risking cache misses. + * different sizes at the same time. * * @param style the style * @param size the point size - * @return a pointer to the font structure (do not free) */ -AscFont const *asc_font(enum AscFontStyle style, int size); - -/** - * Unloads all cached fonts from the context. - */ -void asc_font_cache_clear(void); - -/** - * Checks, if the font is still loaded and reloads it, if required. - * - * There is no need to call this function manually. Every Ascension function - * that you pass a font will do that for you and use the returned pointer in - * case of a cache miss. - * - * @param font the font to validate - * @return \p font, or a pointer to the new location of the loaded font - */ -AscFont const *asc_font_cache_validate(AscFont const *font); +void asc_font(enum AscFontStyle style, int size); #endif //ASCENSION_FONT_H diff -r f18dc427f86f -r 9c44c55d327a src/ascension/ui/text.h --- a/src/ascension/ui/text.h Thu Apr 18 21:53:53 2024 +0200 +++ b/src/ascension/ui/text.h Thu Apr 18 22:53:55 2024 +0200 @@ -38,7 +38,7 @@ typedef struct AscText { extend_asc_scene_node; cxmutstr text; - AscFont const *font; + AscFont font; asc_col4i color; unsigned short max_width; unsigned short offx; diff -r f18dc427f86f -r 9c44c55d327a src/ascension/window.h --- a/src/ascension/window.h Thu Apr 18 21:53:53 2024 +0200 +++ b/src/ascension/window.h Thu Apr 18 22:53:55 2024 +0200 @@ -56,12 +56,6 @@ } AscWindow; /** - * The currently active window in the context. - * @see asc_window_activate() - */ -#define asc_window_active asc_context.active_window - -/** * Initializes the settings structure with default values. * * @param settings an uninitialized settings object @@ -78,31 +72,33 @@ * * @param index the index of the new window * @param settings the settings to be used for initialization - * @return a pointer to the window data or \c NULL if initialization failed */ -AscWindow *asc_window_initialize(unsigned int index, AscWindowSettings const* settings); +void asc_window_initialize(unsigned int index, AscWindowSettings const* settings); /** * Destroys the window and its OpenGL context. * - * When this window is currently active, there will be \em no active window afterwards. + * When this window is currently active, there + * will be \em no active window afterwards. * * Still alive windows will also be destroyed by asc_context_destroy() * automatically. * - * @param window the window + * @param index the index of the window */ -void asc_window_destroy(AscWindow* window); +void asc_window_destroy(unsigned int index); /** * Swaps buffers and adjusts the viewport to the current window size. * - * This function is automatically invoked for all initialized windows - * by asc_loop_next(). You usually do not need to call this function manually. + * This function is automatically invoked by asc_loop_next(). + * You usually should not call this function manually. * - * @param window the window + * If this function is invoked on a non-initialized window, nothing happens. + * + * @param index the index of the window */ -void asc_window_sync(AscWindow *window); +void asc_window_sync(unsigned int index); /** * Switches the active window. @@ -110,9 +106,9 @@ * In particular that makes the corresponding OpenGL context "current". * When you only want to draw into one window, you'll never need this. * - * @param the window to activate + * @param index the index of the window */ -void asc_window_activate(AscWindow *window); +void asc_window_activate(unsigned int index); #endif /* ASCENSION_WINDOW_H */ diff -r f18dc427f86f -r 9c44c55d327a src/context.c --- a/src/context.c Thu Apr 18 21:53:53 2024 +0200 +++ b/src/context.c Thu Apr 18 22:53:55 2024 +0200 @@ -47,6 +47,10 @@ return; memset(&asc_context, 0, sizeof(AscContext)); + // nothing is active right after initialization + asc_context.active_window = ASC_MAX_WINDOWS; + asc_context.active_font = ASC_MAX_FONTS; + // initialize error buffer cxBufferInit( &asc_context.error_buffer, @@ -72,9 +76,9 @@ void asc_context_destroy(void) { for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) { - asc_window_destroy(&asc_context.windows[i]); + asc_window_destroy(i); } - asc_font_cache_clear(); + // TODO: fix that fonts are currently leaking by reworking the font cache // quit SDL if (TTF_WasInit()) @@ -143,9 +147,7 @@ // sync the windows for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) { - if (asc_context.windows[i].id > 0) { - asc_window_sync(&asc_context.windows[i]); - } + asc_window_sync(i); } // compute frame time diff -r f18dc427f86f -r 9c44c55d327a src/font.c --- a/src/font.c Thu Apr 18 21:53:53 2024 +0200 +++ b/src/font.c Thu Apr 18 22:53:55 2024 +0200 @@ -43,17 +43,19 @@ } } -AscFont const *asc_font(enum AscFontStyle style, int size) { +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) { - return font; + asc_context.active_font = i; + return; } } if (asc_context.fonts_loaded == ASC_MAX_FONTS) { - asc_dprintf("WARNING: Maximum number of fonts reached, wiping cache!"); - asc_font_cache_clear(); + 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++; @@ -64,27 +66,8 @@ if (font->ptr == NULL) { asc_context.fonts_loaded--; asc_error(TTF_GetError()); - return NULL; - } - asc_dprintf("Loaded font size %u, style %u in slot %u", size, style, slot); - return font; -} - -void asc_font_cache_clear(void) { - asc_dprintf("Fonts in cache that are being unloaded: %u", asc_context.fonts_loaded); - while (asc_context.fonts_loaded > 0) { - unsigned int i = --asc_context.fonts_loaded; - AscFont *font = &asc_context.fonts[i]; - TTF_CloseFont(font->ptr); - font->ptr = NULL; + } else { + asc_dprintf("Loaded font size %u, style %u in slot %u", size, style, slot); + asc_context.active_font = slot; } } - -AscFont const *asc_font_cache_validate(AscFont const *font) { - if (font->ptr) { - return font; - } else { - asc_dprintf("Cache miss for font size %u, style %u", font->size, font->style); - return asc_font(font->style, font->size); - } -} diff -r f18dc427f86f -r 9c44c55d327a src/primitives.c --- a/src/primitives.c Thu Apr 18 21:53:53 2024 +0200 +++ b/src/primitives.c Thu Apr 18 22:53:55 2024 +0200 @@ -87,7 +87,7 @@ } void asc_primitives_draw_plane(void) { - AscMesh const *mesh = &(asc_window_active->glctx.primitives.plane); + AscMesh const *mesh = &(asc_active_window->glctx.primitives.plane); glBindVertexArray(mesh->vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, mesh->vertices); } \ No newline at end of file diff -r f18dc427f86f -r 9c44c55d327a src/scene.c --- a/src/scene.c Thu Apr 18 21:53:53 2024 +0200 +++ b/src/scene.c Thu Apr 18 22:53:55 2024 +0200 @@ -151,7 +151,7 @@ // Sprites // ------- // TODO: implement view matrix for 2D worlds - shader = &asc_window_active->glctx.shader.sprite.base; + shader = &asc_active_window->glctx.shader.sprite.base; glUseProgram(shader->id); glUniformMatrix4fv(shader->projection, 1, GL_FALSE, camera->projection); diff -r f18dc427f86f -r 9c44c55d327a src/text.c --- a/src/text.c Thu Apr 18 21:53:53 2024 +0200 +++ b/src/text.c Thu Apr 18 22:53:55 2024 +0200 @@ -36,7 +36,7 @@ static void asc_text_draw(AscText const *node) { // Obtain shader - AscShaderSprite *shader = &asc_window_active->glctx.shader.sprite; + AscShaderSprite *shader = &asc_active_window->glctx.shader.sprite; // Upload model matrix glUniformMatrix4fv(shader->base.model, 1, @@ -59,7 +59,8 @@ } // Render text onto a surface - TTF_Font *font = asc_font_cache_validate(node->font)->ptr; + // TODO: obtain TTF_Font from font cache once it is repaired + TTF_Font *font = node->font.ptr; static int alignments[] = { TTF_WRAPPED_ALIGN_LEFT, TTF_WRAPPED_ALIGN_CENTER, @@ -101,7 +102,7 @@ node->base.position.x = (float) args.x; node->base.position.y = (float) args.y; node->max_width = args.max_width; - node->font = asc_context.active_font; + node->font = *asc_active_font; node->color = asc_context.ink; if (args.text == NULL) { node->text = cx_mutstr(strdup(" ")); diff -r f18dc427f86f -r 9c44c55d327a src/window.c --- a/src/window.c Thu Apr 18 21:53:53 2024 +0200 +++ b/src/window.c Thu Apr 18 22:53:55 2024 +0200 @@ -43,16 +43,16 @@ settings->title = "Ascended Window"; } -AscWindow *asc_window_initialize(unsigned int index, AscWindowSettings const *settings) { +void asc_window_initialize(unsigned int index, AscWindowSettings const *settings) { if (index >= ASC_MAX_WINDOWS) { asc_error("Maximum number of windows exceeded."); - return NULL; + return; } AscWindow *window = &asc_context.windows[index]; if (window->id > 0) { asc_error("Cannot create window - slot already occupied."); asc_dprintf("Tried to create window with index %u twice", index); - return NULL; + return; } if (window->ui != NULL) { asc_dprintf("Window with index %u has a dangling UI pointer", index); @@ -72,7 +72,7 @@ ); if (window->window == NULL) { asc_error(SDL_GetError()); - return NULL; + return; } window->id = SDL_GetWindowID(window->window); @@ -85,27 +85,31 @@ if (asc_gl_context_initialize(&window->glctx, window->window, &settings->glsettings)) { window->ui = asc_scene_node_empty(); asc_dprintf("Window %u initialized", window->id); - asc_window_active = window; - return window; + asc_context.active_window = index; } else { asc_dprintf("Creating GL context failed for window %u", window->id); // cleanup on error SDL_DestroyWindow(window->window); window->window = NULL; window->id = 0; - return NULL; } } -void asc_window_destroy(AscWindow* window) { +void asc_window_destroy(unsigned int index) { // safeguard - if (window->id == 0) return; + if (asc_context.windows[index].id == 0) return; // this window cannot be active anymore - if (asc_window_active == window) { - asc_window_active = NULL; + if (asc_context.active_window == index) { + asc_context.active_window = ASC_MAX_WINDOWS; } + // pointer to the window + AscWindow *window = &asc_context.windows[index]; + + // for releasing OpenGL resources, we need to make the context current + asc_gl_context_activate(&window->glctx); + // destroy all scenes asc_scene_node_free(window->ui); window->ui = NULL; @@ -119,8 +123,8 @@ } // if another window was active, make the other context current again - if (asc_window_active != NULL) { - asc_gl_context_activate(&asc_window_active->glctx); + if (asc_context.active_window < ASC_MAX_WINDOWS) { + asc_gl_context_activate(&asc_active_window->glctx); } // clean the data @@ -128,15 +132,19 @@ memset(window, 0, sizeof(AscWindow)); } -void asc_window_sync(AscWindow* window) { - AscWindow *active_window = asc_window_active; - if (window != active_window) { - asc_window_activate(window); +void asc_window_sync(unsigned int index) { + // necessary safeguard + if (asc_context.windows[index].id == 0) return; + + // active the window that shall be synced temporarily + unsigned int active_index = asc_context.active_window; + if (index != active_index) { + asc_window_activate(index); } // Clear the color buffer for the window frame - int window_width = window->dimensions.width; - int window_height = window->dimensions.height; + int window_width = asc_active_window->dimensions.width; + int window_height = asc_active_window->dimensions.height; glViewport(0, 0, window_width, window_height); glClear(GL_COLOR_BUFFER_BIT); asc_recti viewport = {0, 0, window_width, window_height}; @@ -144,20 +152,20 @@ // Draw the UI AscCamera ui_camera; asc_camera_ortho(&ui_camera, viewport); - asc_scene_draw(window->ui, viewport, &ui_camera); + asc_scene_draw(asc_active_window->ui, viewport, &ui_camera); // Swap Buffers - SDL_GL_SwapWindow(window->window); + SDL_GL_SwapWindow(asc_active_window->window); // Clear Flags - window->resized = false; + asc_active_window->resized = false; - if (window != active_window) { - asc_window_activate(active_window); + if (index != active_index) { + asc_window_activate(active_index); } } -void asc_window_activate(AscWindow *window) { - asc_gl_context_activate(&window->glctx); - asc_window_active = (AscWindow *)window; +void asc_window_activate(unsigned int index) { + asc_context.active_window = index; + asc_gl_context_activate(&asc_active_window->glctx); } diff -r f18dc427f86f -r 9c44c55d327a test/snake.c --- a/test/snake.c Thu Apr 18 21:53:53 2024 +0200 +++ b/test/snake.c Thu Apr 18 22:53:55 2024 +0200 @@ -47,7 +47,7 @@ } static void create_fps_counter(void) { - asc_set_font(asc_font(ASC_FONT_REGULAR, 24)); + asc_font(ASC_FONT_REGULAR, 24); asc_ink_rgb(255, 0, 0); AscSceneNode* node = asc_text( .x = 10, .y = 10 ); asc_scene_add_behavior(node, update_fps_counter); @@ -56,8 +56,8 @@ static void update_score_counter(AscSceneNode *node) { // tie to bottom right of the screen - if (asc_window_active->resized) { - asc_vec2i bottom_right = asc_window_active->dimensions; + if (asc_active_window->resized) { + asc_vec2i bottom_right = asc_active_window->dimensions; asc_vec2i scale = asc_get_scale2d(node); asc_set_position2d( node, @@ -68,7 +68,7 @@ } static void create_score_counter(void) { - asc_set_font(asc_font(ASC_FONT_BOLD, 14)); + asc_font(ASC_FONT_BOLD, 14); asc_ink_rgb(0, 255, 0); AscSceneNode* node = asc_text(.text = "Score: 0" ); asc_scene_add_behavior(node, update_score_counter); @@ -87,7 +87,7 @@ AscWindowSettings settings; asc_window_settings_init_defaults(&settings); settings.title = "Snake"; - AscWindow *window = asc_window_initialize(0, &settings); + asc_window_initialize(0, &settings); // create UI elements create_fps_counter(); @@ -98,7 +98,8 @@ // quit application on any error if (asc_has_error()) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, - "Fatal Error", asc_get_error(), window->window); + "Fatal Error", asc_get_error(), + asc_active_window->window); asc_clear_error(); asc_context_quit(); }