diff -r 2cb9a71df7a6 -r 4f32c7755138 src/shader.c --- 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 #include +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