replace per-tick allocations in push_text with an arena/bump allocator
This commit is contained in:
		| @@ -37,13 +37,8 @@ static void title_tick(State *state) { | ||||
|         }, | ||||
|         (Color) { 0, 0, 0, 255 } | ||||
|     ); | ||||
|     push_text( | ||||
|         text_str, | ||||
|         (Vec2){ 0, 0 }, | ||||
|         text_h, | ||||
|         (Color) { 255, 255, 255, 255 }, | ||||
|         font | ||||
|     ); | ||||
|     push_text(text_str, (Vec2){ 0, 0 }, text_h, (Color) { 255, 255, 255, 255 }, font); | ||||
|  | ||||
|     free(text_str); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -22,13 +22,7 @@ Matrix4 camera_look_at_matrix; | ||||
|  | ||||
|  | ||||
| void render_queue_clear(void) { | ||||
|     /* this doesn't even _deserve_ a TODO */ | ||||
|     /* if you're gonna remove it, this is also being done in main.c */ | ||||
|     for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) { | ||||
|         if (ctx.render_queue_2d[i].type == PRIMITIVE_2D_TEXT) { | ||||
|             SDL_free(ctx.render_queue_2d[i].text.text); | ||||
|         } | ||||
|     } | ||||
|     text_cache_reset_arena(&ctx.text_cache); | ||||
|      | ||||
|     /* since i don't intend to free the queues, */ | ||||
|     /* it's faster and simpler to just "start over" */ | ||||
|   | ||||
| @@ -154,6 +154,8 @@ void text_cache_init(TextCache *cache); | ||||
|  | ||||
| void text_cache_deinit(TextCache *cache); | ||||
|  | ||||
| void text_cache_reset_arena(TextCache *cache); | ||||
|  | ||||
| /* vertex buffer */ | ||||
|  | ||||
| VertexBuffer create_vertex_buffer(void); | ||||
|   | ||||
| @@ -11,6 +11,88 @@ | ||||
| #define ASCII_END 128 | ||||
| #define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1) | ||||
|  | ||||
| /* should be large enough that there's virtually never more than one block */ | ||||
| #define STRING_ARENA_BLOCK_SIZE 512000 | ||||
|  | ||||
|  | ||||
| typedef struct StringArenaBlock { | ||||
|     struct StringArenaBlock *next; | ||||
|     char *buffer; | ||||
|     size_t used; | ||||
| } StringArenaBlock; | ||||
|  | ||||
|  | ||||
| typedef struct StringArena { | ||||
|     StringArenaBlock *head; | ||||
|     StringArenaBlock *current_block; | ||||
|     char *ptr; | ||||
| } StringArena; | ||||
|  | ||||
| static StringArena string_arena; | ||||
|  | ||||
|  | ||||
| static void string_arena_init(StringArena *arena) { | ||||
|     arena->head = cmalloc(sizeof *arena->head); | ||||
|     arena->current_block = arena->head; | ||||
|     arena->ptr = arena->current_block->buffer; | ||||
|  | ||||
|     arena->current_block->next = NULL; | ||||
|     arena->current_block->buffer = cmalloc(STRING_ARENA_BLOCK_SIZE); | ||||
|     arena->current_block->used = 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void string_arena_deinit(StringArena *arena) { | ||||
|     StringArenaBlock *p = arena->head; | ||||
|     while (p != NULL) { | ||||
|         StringArenaBlock *block = p; | ||||
|         p = block->next; /* need to keep this before freeing */ | ||||
|  | ||||
|         SDL_free(block->buffer); | ||||
|         SDL_free(block); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| static void string_arena_reset(StringArena *arena) { | ||||
|     arena->current_block = arena->head; | ||||
|     arena->current_block->used = 0; | ||||
|     arena->ptr = arena->current_block->buffer; | ||||
| } | ||||
|  | ||||
|  | ||||
| static char *string_arena_alloc(StringArena *arena, size_t size) { | ||||
|     /* will never fit! */ | ||||
|     if (size > STRING_ARENA_BLOCK_SIZE) { | ||||
|         /* is there a more graceful way to handle this without risking some sort of horror? */ | ||||
|         CRY("String arena allocation failure", "Tried to allocate a size larger than the block size"); | ||||
|         die_abruptly(); | ||||
|     } | ||||
|  | ||||
|     /* won't fit, we need a new block first */ | ||||
|     if (arena->current_block->used + size > STRING_ARENA_BLOCK_SIZE) { | ||||
|         log_info("String arena block size exceeded, moving on to the next..."); | ||||
|  | ||||
|         /* only allocate if there wasn't one ready to go already */ | ||||
|         if (arena->current_block->next == NULL) { | ||||
|             log_info("Allocating string arena block..."); | ||||
|  | ||||
|             arena->current_block->next = cmalloc(sizeof *arena->head); | ||||
|             arena->current_block->next->next = NULL; | ||||
|             arena->current_block->next->buffer = cmalloc(STRING_ARENA_BLOCK_SIZE); | ||||
|         } | ||||
|  | ||||
|         arena->current_block = arena->current_block->next; | ||||
|         arena->current_block->used = 0; | ||||
|         arena->ptr = arena->current_block->buffer; | ||||
|     } | ||||
|  | ||||
|     char *chunk = arena->ptr; | ||||
|     arena->ptr += size; | ||||
|     arena->current_block->used += size * (sizeof *arena->ptr); | ||||
|     return chunk; | ||||
| } | ||||
|  | ||||
|  | ||||
| static FontData *text_load_font_data(const char *path, int height_px) { | ||||
|     FontData *font_data = ccalloc(1, sizeof *font_data); | ||||
| @@ -146,6 +228,7 @@ void render_text(const TextPrimitive *text) { | ||||
|  | ||||
| void text_cache_init(TextCache *cache) { | ||||
|     arrsetlen(cache->data, 0); | ||||
|     string_arena_init(&string_arena); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -155,6 +238,13 @@ void text_cache_deinit(TextCache *cache) { | ||||
|     } | ||||
|  | ||||
|     arrfree(cache->data); | ||||
|     string_arena_deinit(&string_arena); | ||||
| } | ||||
|  | ||||
|  | ||||
| void text_cache_reset_arena(TextCache *cache) { | ||||
|     (void)cache; | ||||
|     string_arena_reset(&string_arena); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -162,8 +252,9 @@ void push_text(char *string, Vec2 position, int height_px, Color color, const ch | ||||
|     ensure_font_cache(font_path, height_px); | ||||
|  | ||||
|     /* the original string might not be around by the time it's used, so copy it */ | ||||
|     /* TODO: arena */ | ||||
|     char *dup_string = SDL_strdup(string); | ||||
|     size_t str_length = SDL_strlen(string) + 1; | ||||
|     char *dup_string = string_arena_alloc(&string_arena, str_length); | ||||
|     SDL_strlcpy(dup_string, string, str_length); | ||||
|  | ||||
|     TextPrimitive text = { | ||||
|         .color = color, | ||||
|   | ||||
| @@ -371,19 +371,12 @@ static void clean_up(void) { | ||||
|     scripting_deinit(ctx); | ||||
|     */ | ||||
|  | ||||
|     input_state_deinit(&ctx.game.input); | ||||
|  | ||||
|     /* if you're gonna remove this, it's also being done in rendering.c */ | ||||
|     for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) { | ||||
|         if (ctx.render_queue_2d[i].type == PRIMITIVE_2D_TEXT) { | ||||
|             SDL_free(ctx.render_queue_2d[i].text.text); | ||||
|         } | ||||
|     } | ||||
|     arrfree(ctx.render_queue_2d); | ||||
|  | ||||
|     input_state_deinit(&ctx.game.input);     | ||||
|     text_cache_deinit(&ctx.text_cache); | ||||
|     textures_cache_deinit(&ctx.texture_cache); | ||||
|  | ||||
|     arrfree(ctx.render_queue_2d); | ||||
|  | ||||
|     PHYSFS_deinit(); | ||||
|     SDL_Quit(); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user