use refcounted objects for textures instead of pass-by-value int structs

Mon, 21 Apr 2025 17:27:33 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 21 Apr 2025 17:27:33 +0200
changeset 89
e1f682a8a145
parent 88
6234b7ea48f3
child 90
aa8e7a38905c

use refcounted objects for textures instead of pass-by-value int structs

src/Makefile file | annotate | diff | comparison | revisions
src/ascension/2d/sprite.h file | annotate | diff | comparison | revisions
src/ascension/texture.h file | annotate | diff | comparison | revisions
src/glcontext.c file | annotate | diff | comparison | revisions
src/mesh.c file | annotate | diff | comparison | revisions
src/sprite.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
src/texture.c file | annotate | diff | comparison | revisions
test/snake/Makefile file | annotate | diff | comparison | revisions
test/snake/snake.c file | annotate | diff | comparison | revisions
--- 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 $<
 
--- 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;
     /**
--- 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__))
--- 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(
--- 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 <GL/glew.h>
+#include <assert.h>
 
 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];
 
--- 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 <GL/glew.h>
 
 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
--- 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 <assert.h>
 #include <cx/printf.h>
 
 #include <GL/glew.h>
@@ -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;
--- 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 <assert.h>
+#include <cx/utils.h>
 #include <GL/glew.h>
 #include <SDL2/SDL_image.h>
 
@@ -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);
 }
--- 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
--- 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 <ascension/core.h>
 #include <ascension/ui.h>
 
 #include <cx/printf.h>
 
+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;
 }

mercurial