src/shader.c

changeset 77
2187a732f4d7
parent 50
d8d2e4817db1
--- a/src/shader.c	Sat Mar 01 22:55:30 2025 +0100
+++ b/src/shader.c	Tue Mar 18 22:43:31 2025 +0100
@@ -26,73 +26,74 @@
  */
 
 #include "ascension/shader.h"
-#include "ascension/files.h"
 #include "ascension/error.h"
 
 #include <GL/glew.h>
 #include <string.h>
 
-AscShader asc_shader_compile(unsigned int type,
-                             char const *code,
-                             int length) {
+/**
+ * Compiles a shader from the given source code.
+ *
+ * The ID of the returned shader will be zero when something went wrong.
+ *
+ * @param type the shader type (use the GL enum)
+ * @param code the source code
+ * @return the compiled shader
+ */
+static unsigned asc_shader_compile(unsigned int type, char const *code) {
     GLuint id = glCreateShader(type);
     if (id == 0) {
         asc_error("glCreateShader failed");
-        return (AscShader) {0};
+        return 0;
     }
 
     GLint success;
+    int length = (int) strlen(code); // must be int because of OpenGL API
     glShaderSource(id, 1, &code, &length);
     glCompileShader(id);
     glGetShaderiv(id, GL_COMPILE_STATUS, &success);
     if (success) {
         asc_dprintf("Shader %u compiled", id);
-        return (AscShader) {id};
+        return id;
     } else {
         char *log = malloc(1024);
         glGetShaderInfoLog(id, 1024, NULL, log);
         glDeleteShader(id);
         asc_error(log);
         free(log);
-        return (AscShader) {0};
+        return 0;
     }
 }
 
-AscShader asc_shader_compilef(unsigned int type,
-                              char const *filename) {
-    asc_file code = asc_file_mmap_rdonly(filename);
-    if (code.ptr == NULL) {
-        asc_error("Mapping shader file into memory failed");
-        return (AscShader) {0};
-    }
-    AscShader ret = asc_shader_compile(type, code.ptr, code.length);
-    asc_file_unmap(code);
-    return ret;
-}
-
-AscShaderProgram asc_shader_link(AscShader vertex,
-                                 AscShader fragment) {
-    if (vertex.id == 0 || fragment.id == 0) {
-        asc_dprintf("Skip linking shader program - shaders have not been loaded correctly.");
-        return (AscShaderProgram) {0};
-    }
-
+/**
+ * This function links shaders into a program.
+ *
+ * The ID of the returned program will be zero when something went wrong.
+ *
+ * @param shader the shader IDs to link
+ * @param n the number of shaders
+ * @return a compiled program
+ */
+static AscShaderProgram asc_shader_link(unsigned shader[], unsigned n) {
     GLint success;
     GLint id = glCreateProgram();
     if (id <= 0) {
         asc_error("glCreateProgram failed");
         return (AscShaderProgram) {0};
     }
-    glAttachShader(id, vertex.id);
-    glAttachShader(id, fragment.id);
+    for (unsigned i = 0; i < n; i++) {
+        glAttachShader(id, shader[i]);
+    }
     glLinkProgram(id);
     glGetProgramiv(id, GL_LINK_STATUS, &success);
-    glDetachShader(id, vertex.id);
-    glDetachShader(id, fragment.id);
+    for (unsigned i = 0; i < n; i++) {
+        glDeleteShader(shader[i]);
+    }
     if (success) {
-        asc_dprintf("Shader Program %u linked (vtf: %u, frag: %u)", id, vertex.id, fragment.id);
+        asc_dprintf("Shader Program %u linked.", id);
         AscShaderProgram prog;
         prog.id = id;
+        // TODO: maybe not every program has the same uniforms
         prog.model = glGetUniformLocation(id, "model");
         prog.view = glGetUniformLocation(id, "view");
         prog.projection = glGetUniformLocation(id, "projection");
@@ -100,21 +101,13 @@
     } else {
         char *log = malloc(1024);
         glGetProgramInfoLog(id, 1024, NULL, log);
-        glDeleteShader(id);
+        glDeleteProgram(id);
         asc_error(log);
         free(log);
         return (AscShaderProgram) {0};
     }
 }
 
-void asc_shader_destroy(AscShader shader) {
-    if (shader.id > 0) {
-        asc_dprintf("Delete Shader %u", shader.id);
-        glDeleteShader(shader.id);
-    }
-    shader.id = 0;
-}
-
 void asc_shader_program_destroy(AscShaderProgram program) {
     if (program.id > 0) {
         asc_dprintf("Delete Shader Program %u", program.id);
@@ -123,12 +116,18 @@
     program.id = 0;
 }
 
-AscShaderProgram asc_shader_easy_compile_and_link(
-        char const *vtxName, char const *fragName) {
-    AscShader font_vtx = asc_shader_compilef(GL_VERTEX_SHADER, vtxName);
-    AscShader font_frag = asc_shader_compilef(GL_FRAGMENT_SHADER, fragName);
-    AscShaderProgram prog = asc_shader_link(font_vtx, font_frag);
-    asc_shader_destroy(font_vtx);
-    asc_shader_destroy(font_frag);
+AscShaderProgram asc_shader_program_create(AscShaderCodes codes) {
+    unsigned shader[4];
+    unsigned n = 0;
+    if (codes.vtx) {
+        shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx);
+    }
+    if (codes.frag) {
+        shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag);
+    }
+    const AscShaderProgram prog = asc_shader_link(shader, n);
+    for (unsigned i = 0; i < n; i++) {
+        glDeleteShader(shader[i]);
+    }
     return prog;
 }

mercurial