src/shader.c

changeset 223
4f32c7755138
parent 222
2cb9a71df7a6
child 224
f72b80448413
--- a/src/shader.c	Thu Jul 24 20:58:00 2025 +0200
+++ b/src/shader.c	Fri Jul 25 18:50:36 2025 +0200
@@ -37,62 +37,81 @@
 #include <cx/buffer.h>
 #include <cx/streams.h>
 
+static char *asc_shader_load_code_file(const char *filename) {
+    cxmutstr fpath = asc_filesystem_combine_paths(cx_strcast(asc_context.shader_path), cx_str(filename));
+    asc_dprintf("Load shader code from %" CX_PRIstr, CX_SFMT(fpath));
+    FILE *f = fopen(fpath.ptr, "r");
+    cx_strfree(&fpath);
+    CxBuffer buffer;
+    if (f == NULL || cxBufferInit(&buffer, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND)) {
+        return NULL;
+    }
+    cx_stream_copy(f, &buffer, (cx_read_func) fread, cxBufferWriteFunc);
+    cxBufferPut(&buffer, '\0');
+    cxBufferShrink(&buffer, 0);
+    return buffer.space;
+}
+
 /**
  * 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
- * @param code_pp the optional preprocessor code
- * @param code_pp_list list of optional preprocessor code
- * @param code_pp_list_select selection flags for the preprocessor code list
+ * @param code the source code information
  * @return the compiled shader
  */
-static unsigned asc_shader_compile(unsigned int type, const char *code, const char *code_pp,
-    const char * const *code_pp_list, unsigned short code_pp_list_select) {
+static unsigned asc_shader_compile(unsigned int type, struct asc_shader_code_s code) {
+    // try to load the source code, first
+    char *source_text = asc_shader_load_code_file(code.source_file);
+    if (source_text == NULL) {
+        asc_error("Failed to load shader source code from %s", code.source_file);
+        return 0;
+    }
+
+    // create the shader
     GLuint id = glCreateShader(type);
     if (id == 0) {
         asc_error("glCreateShader failed: %s", glGetError());
         return 0;
     }
 
-    GLint success;
-    const char *code_array[20];
+    const unsigned max_code_parts = 128+3; // version + preamble + line 1 + source text
+    const char *code_array[max_code_parts];
     GLsizei code_count = 0;
     code_array[code_count++] = "#version 400 core\n";
-    if (code_pp != NULL) code_array[code_count++] = code_pp;
-    unsigned test_flag = 1;
+    uint64_t test_flag = 1;
     unsigned select_index = 0;
-    while (test_flag <= code_pp_list_select) {
-        if (asc_test_flag(code_pp_list_select, test_flag)) {
-            code_array[code_count++] = code_pp_list[select_index];
+    while (test_flag <= code.preamble_code_flags) {
+        if (asc_test_flag(code.preamble_code_flags, test_flag)) {
+            code_array[code_count++] = code.preamble_code[select_index];
+            code_array[code_count++] = "\n";
         }
         select_index++;
         test_flag <<= 1;
     }
-    code_array[code_count++] = "\n#line 1\n";
-    code_array[code_count++] = code;
-
-    // compute the lengths
-    GLint length_array[20];
-    for (int i = 0; i < code_count ; i++) {
-        length_array[i] = (GLint) strlen(code_array[i]);
-    }
+    code_array[code_count++] = "#line 1\n";
+    code_array[code_count++] = source_text;
 
     // compile
-    glShaderSource(id, code_count, code_array, length_array);
+    glShaderSource(id, code_count, code_array, NULL);
     glCompileShader(id);
+
+    // free the source text
+    cxFreeDefault(source_text);
+
+    // check the compilation result
+    GLint success;
     glGetShaderiv(id, GL_COMPILE_STATUS, &success);
     if (success) {
         asc_dprintf("Shader %u compiled", id);
         return id;
     } else {
-        char *log = malloc(1024);
+        char *log = cxMallocDefault(1024);
         glGetShaderInfoLog(id, 1024, NULL, log);
         glDeleteShader(id);
         asc_error("Shader %u compilation failed.\n%s", id, log);
-        free(log);
+        cxFreeDefault(log);
         return 0;
     }
 }
@@ -158,26 +177,34 @@
     return program == NULL || program->gl_id == 0;
 }
 
-AscShaderProgram *asc_shader_create(AscShaderCodeInfo code_info, size_t mem_size, asc_shader_init_func init_func, int flags) {
-    AscShaderCodes codes;
-    if (asc_shader_load_code_files(code_info, &codes)) {
-        asc_error("Loading shader failed.");
-        return NULL;
+AscShaderProgram *asc_shader_create(AscShaderCodes code_info, size_t mem_size, asc_shader_init_func init_func, int flags) {
+    unsigned shader[2];
+    unsigned n = 0;
+    if (code_info.vtx.source_file != NULL) {
+        shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, code_info.vtx);
+    }
+    if (code_info.frag.source_file != NULL) {
+        shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, code_info.frag);
     }
+#ifndef NDEBUG
+    // in debug mode, exit early when a shader compilation failed
+    for (unsigned i = 0 ; i < n ; i++) {
+        if (shader[i] == 0) {
+            asc_error("Shader %u compilation failed.", i);
+            for (unsigned j = 0; j < n; j++) {
+                if (shader[j] > 0) {
+                    asc_dprintf("Delete shader: %u", shader[j]);
+                    glDeleteShader(shader[j]);
+                }
+            }
+            return NULL;
+        }
+    }
+#endif
     AscShaderProgram *shader_program = cxZallocDefault(mem_size);
-    {
-        unsigned shader[2];
-        unsigned n = 0;
-        // TODO: clean up this pp mess by introducing proper nested structs
-        if (codes.vtx) {
-            shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx, codes.vtx_pp, codes.vtx_pp_list, codes.vtx_pp_list_select);
-        }
-        if (codes.frag) {
-            shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag, codes.frag_pp, codes.frag_pp_list, codes.frag_pp_list_select);
-        }
+    if (shader_program != NULL) {
         asc_shader_link(shader, n, shader_program);
     }
-    asc_shader_free_codes(codes);
     if (asc_error_catch_gl("Compile and link shader") || asc_shader_invalid(shader_program)) {
         asc_shader_free(shader_program);
         return NULL;
@@ -209,43 +236,6 @@
     return asc_error_catch_gl("Activating shader");
 }
 
-static int asc_shader_load_code_file(const char *filename, char **code) {
-    if (filename == NULL) {
-        *code = NULL;
-        return 0;
-    }
-    cxmutstr fpath = asc_filesystem_combine_paths(cx_strcast(asc_context.shader_path), cx_str(filename));
-    asc_dprintf("Load shader code from %" CX_PRIstr, CX_SFMT(fpath));
-    FILE *f = fopen(fpath.ptr, "r");
-    cx_strfree(&fpath);
-    if (f == NULL) return -1;
-    CxBuffer buffer;
-    cxBufferInit(&buffer, NULL, 1024, NULL, CX_BUFFER_AUTO_EXTEND);
-    cx_stream_copy(f, &buffer, (cx_read_func) fread, cxBufferWriteFunc);
-    cxBufferPut(&buffer, '\0');
-    cxBufferShrink(&buffer, 0);
-    *code = buffer.space;
-    return *code == NULL ? -1 : 0;
-}
-
-int asc_shader_load_code_files(AscShaderCodeInfo info, AscShaderCodes *codes) {
-    int ret = 0;
-    ret |= asc_shader_load_code_file(info.files.vtx, &codes->vtx);
-    codes->vtx_pp = info.defines.vtx;
-    codes->vtx_pp_list = info.defines.vtx_list;
-    codes->vtx_pp_list_select = info.defines.vtx_list_select;
-    ret |= asc_shader_load_code_file(info.files.frag, &codes->frag);
-    codes->frag_pp = info.defines.frag;
-    codes->frag_pp_list = info.defines.frag_list;
-    codes->frag_pp_list_select = info.defines.frag_list_select;
-    return ret;
-}
-
-void asc_shader_free_codes(AscShaderCodes codes) {
-    cxFreeDefault(codes.vtx);
-    cxFreeDefault(codes.frag);
-}
-
 const AscShaderProgram *asc_shader_register(unsigned int id, asc_shader_create_func create_func, int create_flags) {
     AscGLContext *glctx = asc_active_glctx;
 #ifndef NDEBUG

mercurial