create catch-all util for GL errors + refactors mesh creation

Fri, 18 Apr 2025 20:13:01 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 18 Apr 2025 20:13:01 +0200
changeset 81
84a546e282b7
parent 80
9f7bfc0a1dc3
child 82
4e1e698f4b0d

create catch-all util for GL errors + refactors mesh creation

src/Makefile file | annotate | diff | comparison | revisions
src/ascension/error.h file | annotate | diff | comparison | revisions
src/ascension/glcontext.h file | annotate | diff | comparison | revisions
src/ascension/mesh.h file | annotate | diff | comparison | revisions
src/ascension/primitives.h file | annotate | diff | comparison | revisions
src/context.c file | annotate | diff | comparison | revisions
src/error.c file | annotate | diff | comparison | revisions
src/glcontext.c file | annotate | diff | comparison | revisions
src/mesh.c file | annotate | diff | comparison | revisions
src/primitives.c file | annotate | diff | comparison | revisions
--- a/src/Makefile	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/Makefile	Fri Apr 18 20:13:01 2025 +0200
@@ -28,7 +28,7 @@
 BUILD_DIR=../build/lib
 
 SRC = context.c glcontext.c error.c window.c shader.c font.c text.c \
-      texture.c scene.c camera.c primitives.c
+      texture.c scene.c camera.c primitives.c mesh.c
 
 OBJ = $(SRC:%.c=$(BUILD_DIR)/%.o)
 
@@ -78,6 +78,10 @@
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
+$(BUILD_DIR)/mesh.o: mesh.c ascension/mesh.h ascension/error.h
+	@echo "Compiling $<"
+	$(CC) -o $@ $(CFLAGS) -c $<
+
 $(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 \
--- a/src/ascension/error.h	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/ascension/error.h	Fri Apr 18 20:13:01 2025 +0200
@@ -44,6 +44,8 @@
 
 void asc_error_gl(unsigned code, cxstring message);
 
+int asc_error_catch_all_gl(void);
+
 bool asc_has_error(void);
 char const* asc_get_error(void);
 void asc_clear_error(void);
--- a/src/ascension/glcontext.h	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/ascension/glcontext.h	Fri Apr 18 20:13:01 2025 +0200
@@ -43,7 +43,9 @@
 typedef struct AscGLContext {
     SDL_Window *window;
     SDL_GLContext glctx;
-    AscPrimitives primitives;
+    struct {
+        AscMesh plane;
+    } primitives;
     struct {
         AscShaderSprite sprite;
     } shader;
--- a/src/ascension/mesh.h	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/ascension/mesh.h	Fri Apr 18 20:13:01 2025 +0200
@@ -34,4 +34,20 @@
     unsigned vertices;
 } AscMesh;
 
+/**
+ * Allocates VBO and VAO for one or more meshes.
+ *
+ * @param mesh pointer to a mesh or an array of meshes
+ * @param count the number of meshes (maximum 32)
+ */
+void asc_mesh_allocate_buffers(AscMesh *mesh, unsigned count);
+
+/**
+ * Frees VBO and VAO for one or more meshes.
+ *
+ * @param mesh pointer to a mesh or an array of meshes
+ * @param count the number of meshes (maximum 32)
+ */
+void asc_mesh_free_buffers(AscMesh *mesh, unsigned count);
+
 #endif //ASCENSION_MESH_H
--- a/src/ascension/primitives.h	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/ascension/primitives.h	Fri Apr 18 20:13:01 2025 +0200
@@ -32,26 +32,8 @@
 
 #include "mesh.h"
 
-typedef struct AscPrimitives {
-    AscMesh plane;
-} AscPrimitives;
-
+void asc_primitives_init_plane(AscMesh *mesh);
 
 void asc_primitives_draw_plane(void);
 
-/**
- * Automatically called after initializing the OpenGL context.
- *
- * @param primitives the data structure to initialize
- * @return true on success, false otherwise
- */
-bool asc_primitives_init(AscPrimitives *primitives);
-
-/**
- * Automatically called when the OpenGL context is destroyed.
- *
- * @param primitives containing information about the OpenGL objects
- */
-void asc_primitives_destroy(AscPrimitives *primitives);
-
 #endif //ASCENSION_PRIMITIVES_H
--- a/src/context.c	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/context.c	Fri Apr 18 20:13:01 2025 +0200
@@ -54,7 +54,7 @@
     // initialize the font cache
     asc_font_cache_init();
 
-    // set a default font, but do not load it
+    // set a default font but do not load it
     asc_context.active_font.style = ASC_FONT_REGULAR;
     asc_context.active_font.size = 14;
 
--- a/src/error.c	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/error.c	Fri Apr 18 20:13:01 2025 +0200
@@ -103,4 +103,14 @@
     cxmutstr msg = cx_strcat(3, message, CX_STR(" GL Error: "), cx_str(glerr));
     asc_error_cxstr(cx_strcast(msg));
     cx_strfree(&msg);
-}
\ No newline at end of file
+}
+
+int asc_error_catch_all_gl(void) {
+    GLenum error;
+    int ret = 0;
+    while ((error = glGetError()) != GL_NO_ERROR) {
+        asc_error_gl(error, CX_STR("Uncaught"));
+        ret = 1;
+    }
+    return ret;
+}
--- a/src/glcontext.c	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/glcontext.c	Fri Apr 18 20:13:01 2025 +0200
@@ -48,9 +48,25 @@
     cx_strfree(&buf);
 }
 
+static int asc_primitives_init(AscGLContext *context) {
+    asc_dprintf("Create primitives for the GL context of active window.");
+    asc_mesh_allocate_buffers((AscMesh*)&context->primitives, sizeof(context->primitives) / sizeof(AscMesh));
+
+    asc_primitives_init_plane(&context->primitives.plane);
+    // TODO: more primitives
+
+    return asc_error_catch_all_gl();
+}
+
+static void asc_primitives_destroy(AscGLContext *context) {
+    asc_dprintf("Destroy primitives in GL context of active window.");
+    asc_mesh_free_buffers((AscMesh*)&context->primitives, sizeof(context->primitives) / sizeof(AscMesh));
+}
+
 static int asc_shader_initialize_predefined(AscGLContext *ctx) {
     int ret = 0;
     ret |= asc_shader_sprite_init(&ctx->shader.sprite);
+    ret |= asc_error_catch_all_gl();
     return ret;
 }
 
@@ -81,13 +97,14 @@
         glEnable(GL_DEBUG_OUTPUT);
         glDebugMessageCallback(asc_gl_debug_callback, NULL);
 
-        if (!asc_primitives_init(&ctx->primitives)) {
+        if (asc_primitives_init(ctx)) {
             asc_error("Creating primitive meshes failed");
             SDL_GL_DeleteContext(ctx->glctx);
             return false;
         }
 
         if (asc_shader_initialize_predefined(ctx)) {
+            asc_error("Initializing predefined shaders failed");
             SDL_GL_DeleteContext(ctx->glctx);
             return false;
         }
@@ -105,7 +122,7 @@
     SDL_GL_MakeCurrent(ctx->window, ctx->glctx);
 
     asc_shader_destroy_predefined(ctx);
-    asc_primitives_destroy(&ctx->primitives);
+    asc_primitives_destroy(ctx);
 
     // destroy the GL context and the window
     SDL_GL_DeleteContext(ctx->glctx);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mesh.c	Fri Apr 18 20:13:01 2025 +0200
@@ -0,0 +1,65 @@
+/*
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * Copyright 2025 Mike Becker. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ascension/mesh.h"
+#include "ascension/error.h"
+
+#include <GL/glew.h>
+
+void asc_mesh_allocate_buffers(AscMesh *mesh, unsigned count) {
+    asc_dprintf("Allocate mesh buffers for %u meshes.", count);
+    GLuint buffers[count];
+    GLuint arrays[count];
+    glGenBuffers(count, buffers);
+    glGenVertexArrays(count, arrays);
+    for (unsigned i = 0; i < count; i++) {
+        mesh[i].vbo = buffers[i];
+        mesh[i].vao = arrays[i];
+    }
+    asc_error_catch_all_gl();
+}
+
+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];
+
+    for (unsigned i = 0; i < count; i++) {
+        buffers[i] = mesh[i].vbo;
+        arrays[i] = mesh[i].vao;
+        mesh[i].vbo = 0;
+        mesh[i].vao = 0;
+    }
+
+    glDeleteBuffers(count, buffers);
+    glDeleteVertexArrays(count, arrays);
+    asc_error_catch_all_gl();
+}
\ No newline at end of file
--- a/src/primitives.c	Fri Apr 18 19:34:31 2025 +0200
+++ b/src/primitives.c	Fri Apr 18 20:13:01 2025 +0200
@@ -29,11 +29,16 @@
 #include "ascension/error.h"
 #include "ascension/context.h"
 
-#include <string.h>
 #include <GL/glew.h>
 
-static void asc_primitives_init_plane(AscMesh *mesh) {
-    asc_dprintf("Create primitive plane in VBO %u and VAO %u", mesh->vbo, mesh->vao);
+void asc_primitives_init_plane(AscMesh *mesh) {
+    if (mesh->vbo == 0) {
+        if (mesh->vao > 0) {
+            asc_dprintf("!!! Mesh with VAO %u has no VBO - this is most likely a programming error !!!", mesh->vao);
+        }
+        asc_mesh_allocate_buffers(mesh, 1);
+    }
+    asc_dprintf("Create plane in VBO %u and VAO %u", mesh->vbo, mesh->vao);
     mesh->vertices = 4;
     float data[8] = {
             0.0f, 0.0f, // bottom left
@@ -48,44 +53,6 @@
     glEnableVertexAttribArray(0);
 }
 
-bool asc_primitives_init(AscPrimitives *primitives) {
-    asc_dprintf("Create primitives for the GL context of active window.");
-    // TODO: more primitives
-
-    GLuint buffers[1];
-    GLuint arrays[1];
-    glGenBuffers(1, buffers);
-    glGenVertexArrays(1, arrays);
-
-    AscMesh *plane = &(primitives->plane);
-    plane->vbo = buffers[0];
-    plane->vao = arrays[0];
-    asc_primitives_init_plane(plane);
-
-    GLenum error = glGetError();
-    if (error == GL_NO_ERROR) {
-        return true;
-    } else {
-        asc_error_gl(error, CX_STR("Initialization of primitive meshes failed."));
-        return false;
-    }
-}
-
-void asc_primitives_destroy(AscPrimitives *primitives) {
-    asc_dprintf("Destroy primitives in GL context of active window.");
-
-    GLuint buffers[1];
-    GLuint arrays[1];
-
-    buffers[0] = primitives->plane.vbo;
-    arrays[0] = primitives->plane.vao;
-
-    glDeleteBuffers(1, buffers);
-    glDeleteVertexArrays(1, arrays);
-
-    memset(primitives, 0, sizeof(AscPrimitives));
-}
-
 void asc_primitives_draw_plane(void) {
     AscMesh const *mesh = &(asc_active_window->glctx.primitives.plane);
     glBindVertexArray(mesh->vao);

mercurial