src/shader.c

changeset 139
5d655459db85
parent 137
f8e6e0ae61a8
--- a/src/shader.c	Sun Jun 08 14:57:54 2025 +0200
+++ b/src/shader.c	Sun Jun 08 14:58:19 2025 +0200
@@ -87,14 +87,16 @@
  *
  * @param shader the shader IDs to link
  * @param n the number of shaders
- * @return a compiled program
+ * @param prog the struct where to store the result
+ * @retval zero success
+ * @retval non-zero failure
  */
-static AscShaderProgram asc_shader_link(unsigned shader[], unsigned n) {
+static int asc_shader_link(unsigned shader[], unsigned n, AscShaderProgram *prog) {
     GLint success;
     GLint id = glCreateProgram();
     if (id <= 0) {
         asc_error("glCreateProgram failed: %s", glGetError());
-        return (AscShaderProgram) {0};
+        return -1;
     }
     for (unsigned i = 0; i < n; i++) {
         glAttachShader(id, shader[i]);
@@ -106,33 +108,36 @@
     }
     if (success) {
         asc_dprintf("Shader Program %u linked.", id);
-        AscShaderProgram prog;
-        prog.id = id;
+        prog->gl_id = id;
         // by convention every shader shall have MVP matrices
-        prog.model = glGetUniformLocation(id, "model");
-        prog.view = glGetUniformLocation(id, "view");
-        prog.projection = glGetUniformLocation(id, "projection");
-        return prog;
+        prog->model = glGetUniformLocation(id, "model");
+        prog->view = glGetUniformLocation(id, "view");
+        prog->projection = glGetUniformLocation(id, "projection");
+        return 0;
     } else {
         char *log = malloc(1024);
         glGetProgramInfoLog(id, 1024, NULL, log);
         glDeleteProgram(id);
         asc_error("Linking shader program %u failed.\n%s", id, log);
         free(log);
-        return (AscShaderProgram) {0};
+        return -1;
     }
 }
 
-void asc_shader_program_destroy(AscShaderProgram *program) {
-    if (program->id > 0) {
-        asc_dprintf("Delete Shader Program %u", program->id);
-        glDeleteProgram(program->id);
+void asc_shader_free(AscShaderProgram *program) {
+    if (program->gl_id > 0) {
+        asc_dprintf("Delete Shader Program %u", program->gl_id);
+        glDeleteProgram(program->gl_id);
     }
-    program->id = 0;
+    if (program->destr_func) {
+        program->destr_func(program);
+    }
+    cxFreeDefault(program);
 }
 
-AscShaderProgram asc_shader_program_create(AscShaderCodes codes) {
-    unsigned shader[4];
+void *asc_shader_create(AscShaderCodes codes, size_t mem_size) {
+    AscShaderProgram *prog = cxZallocDefault(mem_size);
+    unsigned shader[2];
     unsigned n = 0;
     if (codes.vtx) {
         shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx, codes.vtx_pp);
@@ -140,15 +145,19 @@
     if (codes.frag) {
         shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag, codes.frag_pp);
     }
-    const AscShaderProgram prog = asc_shader_link(shader, n);
+    if (asc_shader_link(shader, n, prog)) {
+        cxFreeDefault(prog);
+        prog = NULL;
+    }
     for (unsigned i = 0; i < n; i++) {
+        asc_dprintf("Delete shader: %u", shader[i]);
         glDeleteShader(shader[i]);
     }
     return prog;
 }
 
-void asc_shader_program_use(const AscShaderProgram *shader, const AscCamera *camera) {
-    glUseProgram(shader->id);
+void asc_shader_use(const AscShaderProgram *shader, const AscCamera *camera) {
+    glUseProgram(shader->gl_id);
     glUniformMatrix4fv(shader->projection, 1, GL_FALSE, camera->projection);
     glUniformMatrix4fv(shader->view, 1, GL_FALSE, camera->view);
 }
@@ -185,3 +194,36 @@
     cxFreeDefault(codes.vtx);
     cxFreeDefault(codes.frag);
 }
+
+AscShaderProgram *asc_shader_register(unsigned int id, asc_shader_create_func create_func) {
+    AscGLContext *glctx = asc_active_glctx;
+    AscShaderProgram *prog = NULL;
+#ifndef NDEBUG
+    prog = asc_shader_lookup(id);
+    if (prog != NULL) {
+        asc_error("Shader program %u already exists. This is a bug!", id);
+        // still return it, so that the caller does not die immediately
+        return prog;
+    }
+#endif
+    prog = create_func();
+    prog->id = id;
+    cxListAdd(glctx->shaders, prog);
+    return prog;
+}
+
+AscShaderProgram *asc_shader_lookup(unsigned int id) {
+    CxIterator iter = cxListIterator(asc_active_glctx->shaders);
+    cx_foreach(AscShaderProgram *, prog, iter) {
+        if (prog->id == id) return prog;
+    }
+    return NULL;
+}
+
+AscShaderProgram *asc_shader_lookup_or_create(unsigned int id, asc_shader_create_func create_func) {
+    AscShaderProgram *prog = asc_shader_lookup(id);
+    if (prog == NULL) {
+        return asc_shader_register(id, create_func);
+    }
+    return prog;
+}
\ No newline at end of file

mercurial