# HG changeset patch # User Mike Becker # Date 1745249253 -7200 # Node ID e1f682a8a14503c1c39d3881ad917adcbc9ea7ad # Parent 6234b7ea48f3a7c8437bcf48cc7973b0398437b0 use refcounted objects for textures instead of pass-by-value int structs diff -r 6234b7ea48f3 -r e1f682a8a145 src/Makefile --- a/src/Makefile Sun Apr 20 15:41:16 2025 +0200 +++ b/src/Makefile Mon Apr 21 17:27:33 2025 +0200 @@ -52,18 +52,18 @@ $(BUILD_DIR)/context.o: context.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/scene_node.h ascension/transform.h \ - ascension/camera.h ascension/input.h ascension/ui/font.h \ - ascension/error.h ascension/utils.h + ascension/texture.h ascension/scene.h ascension/scene_node.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 $< $(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/scene_node.h ascension/transform.h ascension/camera.h \ - ascension/input.h ascension/ui/font.h ascension/error.h \ - ascension/utils.h + ascension/mesh.h ascension/shader.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.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 $< @@ -73,16 +73,16 @@ $(BUILD_DIR)/font.o: font.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/scene_node.h ascension/transform.h ascension/camera.h \ - ascension/input.h ascension/ui/font.h ascension/error.h \ - ascension/filesystem.h ascension/ui/font.h + ascension/mesh.h ascension/shader.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/transform.h \ + ascension/camera.h ascension/input.h ascension/ui/font.h \ + ascension/error.h ascension/filesystem.h ascension/ui/font.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/glcontext.o: glcontext.c ascension/glcontext.h \ ascension/primitives.h ascension/mesh.h ascension/shader.h \ - ascension/error.h + ascension/texture.h ascension/error.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -93,9 +93,9 @@ $(BUILD_DIR)/primitives.o: primitives.c ascension/primitives.h \ 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/scene_node.h ascension/transform.h ascension/camera.h \ - ascension/input.h ascension/ui/font.h + ascension/primitives.h ascension/shader.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/transform.h \ + ascension/camera.h ascension/input.h ascension/ui/font.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -103,18 +103,19 @@ ascension/scene_node.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/input.h ascension/ui/font.h \ - ascension/utils.h ascension/2d.h ascension/2d/sprite.h \ - ascension/2d/../scene_node.h ascension/2d/../texture.h + ascension/texture.h ascension/scene.h ascension/input.h \ + ascension/ui/font.h ascension/utils.h ascension/2d.h \ + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../texture.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/shader.o: shader.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/scene_node.h ascension/transform.h ascension/camera.h \ - ascension/input.h ascension/ui/font.h ascension/shader.h \ - ascension/error.h ascension/filesystem.h + ascension/mesh.h ascension/shader.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/transform.h \ + ascension/camera.h ascension/input.h ascension/ui/font.h \ + ascension/shader.h ascension/error.h ascension/filesystem.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -123,33 +124,39 @@ ascension/2d/../transform.h ascension/2d/../texture.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/scene_node.h \ - ascension/camera.h ascension/input.h ascension/ui/font.h \ - ascension/glcontext.h + ascension/shader.h ascension/texture.h ascension/scene.h \ + ascension/scene_node.h ascension/camera.h ascension/input.h \ + ascension/ui/font.h ascension/glcontext.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/text.o: text.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/scene_node.h ascension/transform.h ascension/camera.h \ - ascension/input.h ascension/ui/font.h ascension/error.h \ - ascension/ui/text.h ascension/ui/font.h ascension/ui/../2d/sprite.h \ - ascension/ui/../2d/../scene_node.h ascension/ui/../2d/../texture.h \ - ascension/ui/../utils.h + ascension/mesh.h ascension/shader.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/transform.h \ + ascension/camera.h ascension/input.h ascension/ui/font.h \ + ascension/error.h ascension/ui/text.h ascension/ui/font.h \ + ascension/ui/../2d/sprite.h ascension/ui/../2d/../scene_node.h \ + ascension/ui/../2d/../texture.h ascension/ui/../utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< -$(BUILD_DIR)/texture.o: texture.c ascension/texture.h ascension/error.h +$(BUILD_DIR)/texture.o: texture.c ascension/context.h \ + ascension/datatypes.h ascension/window.h ascension/glcontext.h \ + ascension/primitives.h ascension/mesh.h ascension/shader.h \ + ascension/texture.h ascension/scene.h ascension/scene_node.h \ + ascension/transform.h ascension/camera.h ascension/input.h \ + ascension/ui/font.h ascension/texture.h ascension/error.h \ + ascension/filesystem.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/window.o: window.c ascension/window.h ascension/datatypes.h \ ascension/glcontext.h ascension/primitives.h ascension/mesh.h \ - ascension/shader.h ascension/scene.h ascension/scene_node.h \ - ascension/transform.h ascension/camera.h ascension/context.h \ - ascension/window.h ascension/input.h ascension/ui/font.h \ - ascension/error.h ascension/utils.h + ascension/shader.h ascension/texture.h ascension/scene.h \ + ascension/scene_node.h ascension/transform.h ascension/camera.h \ + ascension/context.h ascension/window.h ascension/input.h \ + ascension/ui/font.h ascension/error.h ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< diff -r 6234b7ea48f3 -r e1f682a8a145 src/ascension/2d/sprite.h --- a/src/ascension/2d/sprite.h Sun Apr 20 15:41:16 2025 +0200 +++ b/src/ascension/2d/sprite.h Mon Apr 21 17:27:33 2025 +0200 @@ -33,11 +33,11 @@ typedef struct AscSprite { AscSceneNode data; - AscTexture tex; + AscTexture *texture; } AscSprite; struct asc_sprite_create_args { - AscTexture texture; + AscTexture *texture; int x; int y; /** diff -r 6234b7ea48f3 -r e1f682a8a145 src/ascension/texture.h --- a/src/ascension/texture.h Sun Apr 20 15:41:16 2025 +0200 +++ b/src/ascension/texture.h Mon Apr 21 17:27:33 2025 +0200 @@ -37,10 +37,9 @@ unsigned tex_id; unsigned width; unsigned height; + unsigned refcount; }; -#define asc_texture_uninitialized(tex) ((tex)->tex_id == 0) - enum asc_texture_target { ASC_TEXTURE_RECTANGLE, ASC_TEXTURE_2D @@ -63,20 +62,21 @@ __attribute__((__nonnull__)) void asc_texture_init( AscTexture *tex, + unsigned count, enum asc_texture_target target, enum asc_texture_min_filter min_filter, enum asc_texture_mag_filter mag_filter ); __attribute__((__nonnull__)) -void asc_texture_destroy(AscTexture *tex); +void asc_texture_destroy(AscTexture *tex, unsigned count); -#define asc_texture_init_rectangle(tex) \ - asc_texture_init(tex, ASC_TEXTURE_RECTANGLE, \ +#define asc_texture_init_rectangle(tex, count) \ + asc_texture_init(tex, count, ASC_TEXTURE_RECTANGLE, \ ASC_TEXTURE_MIN_FILTER_NEAREST, ASC_TEXTURE_MAG_FILTER_NEAREST) -#define asc_texture_init_2d(tex) \ - asc_texture_init(tex, ASC_TEXTURE_2D, \ +#define asc_texture_init_2d(tex, count) \ + asc_texture_init(tex, count, ASC_TEXTURE_2D, \ ASC_TEXTURE_MIN_FILTER_LINEAR, ASC_TEXTURE_MAG_FILTER_LINEAR) __attribute__((__nonnull__)) diff -r 6234b7ea48f3 -r e1f682a8a145 src/glcontext.c --- a/src/glcontext.c Sun Apr 20 15:41:16 2025 +0200 +++ b/src/glcontext.c Mon Apr 21 17:27:33 2025 +0200 @@ -75,8 +75,8 @@ } static int asc_texture_initialize_predefined(AscGLContext *ctx) { - asc_texture_init_rectangle(&ctx->textures.empty_1x1_rect); - asc_texture_init_2d(&ctx->textures.empty_1x1_2d); + asc_texture_init_rectangle(&ctx->textures.empty_1x1_rect, 1); + asc_texture_init_2d(&ctx->textures.empty_1x1_2d, 1); // Create a 1x1 surface with 32-bit RGBA format SDL_Surface* white1x1 = SDL_CreateRGBSurface( @@ -98,8 +98,8 @@ } static void asc_texture_destroy_predefined(AscGLContext *ctx) { - asc_texture_destroy(&ctx->textures.empty_1x1_2d); - asc_texture_destroy(&ctx->textures.empty_1x1_rect); + asc_texture_destroy(&ctx->textures.empty_1x1_2d, 1); + asc_texture_destroy(&ctx->textures.empty_1x1_rect, 1); } bool asc_gl_context_initialize( diff -r 6234b7ea48f3 -r e1f682a8a145 src/mesh.c --- a/src/mesh.c Sun Apr 20 15:41:16 2025 +0200 +++ b/src/mesh.c Mon Apr 21 17:27:33 2025 +0200 @@ -29,6 +29,7 @@ #include "ascension/error.h" #include +#include void asc_mesh_allocate_buffers(AscMesh *mesh, unsigned count) { asc_dprintf("Allocate mesh buffers for %u meshes.", count); @@ -45,10 +46,6 @@ void asc_mesh_free_buffers(AscMesh *mesh, unsigned count) { asc_dprintf("Free mesh buffers for %u meshes.", count); - if (count > 32) { - asc_error("Trying to free more than 32 mesh buffers at once."); - count = 32; - } GLuint buffers[count]; GLuint arrays[count]; diff -r 6234b7ea48f3 -r e1f682a8a145 src/sprite.c --- a/src/sprite.c Sun Apr 20 15:41:16 2025 +0200 +++ b/src/sprite.c Mon Apr 21 17:27:33 2025 +0200 @@ -33,6 +33,8 @@ #include static void asc_sprite_free(AscSceneNode *node) { + AscSprite *sprite = (AscSprite *) node; + sprite->texture->refcount--; free(node); } @@ -40,7 +42,8 @@ AscSprite *sprite = calloc(1, sizeof(AscSprite)); // special sprite parameters - sprite->tex = args.texture; + args.texture->refcount++; + sprite->texture = args.texture; // basic node parameters AscSceneNode *node = (AscSceneNode *) sprite; @@ -52,8 +55,8 @@ node->position.x = (float) args.x; node->position.y = (float) args.y; - node->scale.x = (float) (args.width == 0 ? args.texture.width : args.width); - node->scale.y = (float) (args.height == 0 ? args.texture.height : args.height); + node->scale.x = (float) (args.width == 0 ? args.texture->width : args.width); + node->scale.y = (float) (args.height == 0 ? args.texture->height : args.height); return node; } @@ -67,12 +70,12 @@ GL_FALSE, node->data.world_transform); // Bind texture - if (node->tex.target == GL_TEXTURE_RECTANGLE) { - asc_texture_bind(&node->tex, shader->rect_tex, 0); + if (node->texture->target == GL_TEXTURE_RECTANGLE) { + asc_texture_bind(node->texture, shader->rect_tex, 0); asc_texture_bind(&ASC_DEFAULT_TEXTURES.empty_1x1_2d, shader->uv_tex, 1); } else { asc_texture_bind(&ASC_DEFAULT_TEXTURES.empty_1x1_rect, shader->rect_tex, 0); - asc_texture_bind(&node->tex, shader->uv_tex, 1); + asc_texture_bind(node->texture, shader->uv_tex, 1); } // Apply depth diff -r 6234b7ea48f3 -r e1f682a8a145 src/text.c --- a/src/text.c Sun Apr 20 15:41:16 2025 +0200 +++ b/src/text.c Mon Apr 21 17:27:33 2025 +0200 @@ -29,6 +29,7 @@ #include "ascension/error.h" #include "ascension/ui/text.h" +#include #include #include @@ -37,11 +38,6 @@ AscSprite *sprite = (AscSprite*) node; AscText *text = (AscText*) node; - // Generate new texture, if required - if (asc_texture_uninitialized(&sprite->tex)) { - asc_texture_init_rectangle(&sprite->tex); - } - // Render text onto a surface TTF_Font *font = asc_font_load(text->font); static int alignments[] = { @@ -67,7 +63,7 @@ } // Transfer Image Data - asc_texture_from_surface(&sprite->tex, surface); + asc_texture_from_surface(sprite->texture, surface); // Free the surface SDL_FreeSurface(surface); @@ -76,7 +72,9 @@ static void asc_text_free(AscSceneNode *node) { AscText *text = (AscText*) node; AscSprite *sprite = (AscSprite*) node; - asc_texture_destroy(&sprite->tex); + asc_texture_destroy(sprite->texture, 1); + assert(sprite->texture->refcount == 0); + free(sprite->texture); cx_strfree(&text->text); free(node); } @@ -102,6 +100,8 @@ } // initialize + text->base.texture = malloc(sizeof(AscTexture)); + asc_texture_init_rectangle(text->base.texture, 1); asc_text_update(node); return node; diff -r 6234b7ea48f3 -r e1f682a8a145 src/texture.c --- a/src/texture.c Sun Apr 20 15:41:16 2025 +0200 +++ b/src/texture.c Mon Apr 21 17:27:33 2025 +0200 @@ -31,6 +31,7 @@ #include "ascension/filesystem.h" #include +#include #include #include @@ -46,7 +47,7 @@ } void asc_texture_from_surface(AscTexture *tex, SDL_Surface const *surface) { - if (asc_texture_uninitialized(tex)) { + if (tex->tex_id == 0) { asc_error("Tried to use uninitialized texture."); asc_dprintf("Texture address: %"PRIxPTR, (uintptr_t) tex); return; @@ -109,6 +110,7 @@ void asc_texture_init( AscTexture *tex, + unsigned count, enum asc_texture_target target, enum asc_texture_min_filter min_filter, enum asc_texture_mag_filter mag_filter @@ -128,18 +130,37 @@ assert(target < sizeof(texture_targets) / sizeof(GLenum)); assert(min_filter < sizeof(texture_filters) / sizeof(GLint)); assert(mag_filter < 2); // mag filter only supports nearest/linear - tex->target = texture_targets[target]; - glGenTextures(1, &tex->tex_id); - glBindTexture(tex->target, tex->tex_id); - glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, - texture_filters[min_filter]); - glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, - texture_filters[mag_filter]); - asc_dprintf("Initialized texture: %u", tex->tex_id); + + GLuint textures[count]; + glGenTextures(count, textures); + + for (unsigned i = 0; i < count; ++i) { + memset(&tex[i], 0, sizeof(AscTexture)); + tex[i].tex_id = textures[i]; + tex[i].target = texture_targets[target]; + glBindTexture(tex[i].target, tex[i].tex_id); + glTexParameteri(tex[i].target, GL_TEXTURE_MIN_FILTER, + texture_filters[min_filter]); + glTexParameteri(tex[i].target, GL_TEXTURE_MAG_FILTER, + texture_filters[mag_filter]); + asc_dprintf("Initialized texture: %u", tex[i].tex_id); + } + + // TODO: proper error handling for each gl call asc_error_catch_all_gl(); } -void asc_texture_destroy(AscTexture *tex) { - asc_dprintf("Destroy texture: %u", tex->tex_id); - glDeleteTextures(1, &tex->tex_id); +void asc_texture_destroy(AscTexture *tex, unsigned count) { + GLuint textures[count]; + for (unsigned i = 0; i < count; ++i) { + if (tex[i].refcount > 0) { + // TODO: asc_wprintf() for warnings + asc_dprintf("Texture %u still in use by %u objects.", + tex[i].tex_id, tex[i].refcount); + } + asc_dprintf("Destroy texture: %u", tex[i].tex_id); + textures[i] = tex[i].tex_id; + memset(&tex[i], 0, sizeof(AscTexture)); + } + glDeleteTextures(count, textures); } diff -r 6234b7ea48f3 -r e1f682a8a145 test/snake/Makefile --- a/test/snake/Makefile Sun Apr 20 15:41:16 2025 +0200 +++ b/test/snake/Makefile Mon Apr 21 17:27:33 2025 +0200 @@ -49,11 +49,12 @@ ../../src/ascension/datatypes.h ../../src/ascension/window.h \ ../../src/ascension/glcontext.h ../../src/ascension/primitives.h \ ../../src/ascension/mesh.h ../../src/ascension/shader.h \ - ../../src/ascension/scene.h ../../src/ascension/scene_node.h \ - ../../src/ascension/transform.h ../../src/ascension/camera.h \ - ../../src/ascension/input.h ../../src/ascension/ui/font.h \ - ../../src/ascension/ui.h ../../src/ascension/ui/text.h \ - ../../src/ascension/ui/font.h ../../src/ascension/ui/../2d/sprite.h \ + ../../src/ascension/texture.h ../../src/ascension/scene.h \ + ../../src/ascension/scene_node.h ../../src/ascension/transform.h \ + ../../src/ascension/camera.h ../../src/ascension/input.h \ + ../../src/ascension/ui/font.h ../../src/ascension/ui.h \ + ../../src/ascension/ui/text.h ../../src/ascension/ui/font.h \ + ../../src/ascension/ui/../2d/sprite.h \ ../../src/ascension/ui/../2d/../scene_node.h \ ../../src/ascension/ui/../2d/../texture.h \ ../../src/ascension/ui/../utils.h diff -r 6234b7ea48f3 -r e1f682a8a145 test/snake/snake.c --- a/test/snake/snake.c Sun Apr 20 15:41:16 2025 +0200 +++ b/test/snake/snake.c Mon Apr 21 17:27:33 2025 +0200 @@ -25,11 +25,27 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ascension/core.h" +#include #include #include +enum Textures2d { + TEX_SHIP = 0, + TEX2D_COUNT +}; +static AscTexture tex2d[TEX2D_COUNT]; +#define TEXTURE_SHIP &tex2d[TEX_SHIP] + +static void init_textures(void) { + asc_texture_init_2d(tex2d, TEX2D_COUNT); + asc_texture_from_file(TEXTURE_SHIP, "ship.png"); +} + +static void destroy_textures(void) { + asc_texture_destroy(tex2d, TEX2D_COUNT); +} + static void update_fps_counter(AscSceneNode *node) { static uint64_t last_fps = 0; static uint64_t debounce = ASC_NANOS_SECOND - 1; @@ -75,18 +91,9 @@ asc_add_ui_node(node); } -static void free_spaceship(AscSceneNode *node) { - asc_texture_destroy(&((AscSprite*)node)->tex); - free(node); -} - static void create_spaceship(void) { - // TODO: textures should be passed by pointers (and probably ref-counted) - AscTexture texture; - asc_texture_init_2d(&texture); - asc_texture_from_file(&texture, "ship.png"); AscSceneNode *sprite = asc_sprite( - .texture = texture, + .texture = TEXTURE_SHIP, .x = 250, .y = 300, .width = 64, @@ -96,9 +103,6 @@ // TODO: add to 2D scene instead of UI scene, once we have one asc_add_ui_node(sprite); - // TODO: remove this hack by refactoring how textures work - sprite->free_func = free_spaceship; - // TODO: return something } @@ -106,7 +110,7 @@ asc_context_initialize(); if (asc_has_error()) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, - "Fatal Error",asc_get_error(),NULL); + "Fatal Error",asc_get_error(), NULL); return 1; } #ifdef TEST_BUILD @@ -123,6 +127,9 @@ settings.title = "Snake"; asc_window_initialize(0, &settings); + // load textures + init_textures(); + // create UI elements create_fps_counter(); create_score_counter(); @@ -147,6 +154,8 @@ } } while (asc_loop_next()); + // cleanup + destroy_textures(); asc_context_destroy(); return 0; }