Thu, 19 Jun 2025 19:22:07 +0200
improve the combination of shader preprocessor flags
src/2d.c | file | annotate | diff | comparison | revisions | |
src/ascension/shader.h | file | annotate | diff | comparison | revisions | |
src/shader.c | file | annotate | diff | comparison | revisions | |
test/snake/snake.c | file | annotate | diff | comparison | revisions |
--- a/src/2d.c Wed Jun 18 23:55:08 2025 +0200 +++ b/src/2d.c Thu Jun 19 19:22:07 2025 +0200 @@ -33,6 +33,7 @@ #include "ascension/shader.h" #include <GL/glew.h> +#include <assert.h> typedef struct asc_rectangle_shader_s { AscShaderProgram program; @@ -51,19 +52,15 @@ AscShaderCodes codes; // TODO: create a utility that rolls out those defines const char * const defines[] = { - NULL, - "#define FILL", - "#define BORDER\n#define ROUNDED_CORNERS", - "#define FILL\n#define ROUNDED_CORNERS", - "#define BORDER", - "#define BORDER\n#define FILL", - "#define BORDER\n#define ROUNDED_CORNERS", - "#define BORDER\n#define FILL\n#define ROUNDED_CORNERS", + "#define FILL\n", + "#define ROUNDED_CORNERS\n", + "#define BORDER\n" }; if (asc_shader_load_code_files((AscShaderCodeInfo){ .files.vtx = "sprite_vtx.glsl", .files.frag = "rectangle_frag.glsl", - .defines.frag = defines[flags], + .defines.frag_list = defines, + .defines.frag_list_select = flags }, &codes)) { asc_error("Loading sprite shader failed."); return NULL; @@ -71,6 +68,7 @@ AscRectangleShader *shader = asc_shader_create(codes, sizeof(*shader)); if (asc_has_error()) { asc_shader_free_codes(codes); + // TODO: must not return NULL or must add error handling on caller site return NULL; } shader->size = glGetUniformLocation(shader->program.gl_id, "size");
--- a/src/ascension/shader.h Wed Jun 18 23:55:08 2025 +0200 +++ b/src/ascension/shader.h Thu Jun 19 19:22:07 2025 +0200 @@ -50,6 +50,24 @@ * Preprocessor code that shall be prepended to the fragment shader. */ const char *frag; + /** + * A list of flag-based preprocessor code for the vertex shader. + * Each code must end with a new-line. + */ + const char * const *vtx_list; + /** + * A list of flag-based preprocessor code for the fragment shader. + * Each code must end with a new-line. + */ + const char * const *frag_list; + /** + * Selection flags for the list of codes for the vertex shader. + */ + unsigned short vtx_list_select; + /** + * Selection flags for the list of codes for the fragment shader. + */ + unsigned short frag_list_select; }; typedef struct asc_shader_code_info_s { @@ -68,6 +86,10 @@ * Optional preprocessor code for the fragment shader. */ const char *frag_pp; + const char * const *vtx_pp_list; + const char * const *frag_pp_list; + unsigned short vtx_pp_list_select; + unsigned short frag_pp_list_select; } AscShaderCodes; typedef struct asc_shader_program_s {
--- a/src/shader.c Wed Jun 18 23:55:08 2025 +0200 +++ b/src/shader.c Thu Jun 19 19:22:07 2025 +0200 @@ -44,32 +44,43 @@ * @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 * @return the compiled shader */ -static unsigned asc_shader_compile(unsigned int type, const char *code, const char *code_pp) { +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) { GLuint id = glCreateShader(type); if (id == 0) { asc_error("glCreateShader failed: %s", glGetError()); return 0; } - // some drivers don't like NULL strings, even when length is zero - if (code_pp == NULL) code_pp = ""; + GLint success; + const char *code_array[20]; + 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; + 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]; + } + select_index++; + test_flag <<= 1; + } + code_array[code_count++] = "\n#line 1\n"; + code_array[code_count++] = code; - GLint success; - const char *code_array[4] = { - "#version 400 core\n", - code_pp, - "\n#line 1\n", - code - }; - GLint length_array[4]; - for (unsigned int i = 0; i < cx_nmemb(length_array); i++) { + // compute the lengths + GLint length_array[20]; + for (int i = 0; i < code_count ; i++) { length_array[i] = (GLint) strlen(code_array[i]); } // compile - glShaderSource(id, cx_nmemb(length_array), code_array, length_array); + glShaderSource(id, code_count, code_array, length_array); glCompileShader(id); glGetShaderiv(id, GL_COMPILE_STATUS, &success); if (success) { @@ -145,10 +156,10 @@ unsigned shader[2]; unsigned n = 0; if (codes.vtx) { - shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx, codes.vtx_pp); + 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); + shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag, codes.frag_pp, codes.frag_pp_list, codes.frag_pp_list_select); } if (asc_shader_link(shader, n, prog)) { cxFreeDefault(prog); @@ -197,8 +208,12 @@ 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; }
--- a/test/snake/snake.c Wed Jun 18 23:55:08 2025 +0200 +++ b/test/snake/snake.c Thu Jun 19 19:22:07 2025 +0200 @@ -196,12 +196,40 @@ // create spaceship create_spaceship(); - // TODO: play around with the test rectangle + // TODO: play around with the test rectangles asc_ink_rgb(255, 0, 0); asc_scene_add_node(MAIN_SCENE, asc_rectangle(.x = 200, .y = 250, .width = 100, .height = 75, .thickness = 4, .radius = 15, .filled = true, .border_color = asc_col4i_new(0, 255, 0, 255)) ); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 50, .y = 150, .width = 100, .height = 75, + .thickness = 4, .radius = 15, .filled = false, .border_color = asc_col4i_new(0, 255, 0, 255)) + ); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 50, .y = 25, .width = 100, .height = 75, + .thickness = 4, .filled = false, .border_color = asc_col4i_new(0, 255, 0, 255)) + ); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 250, .y = 25, .width = 100, .height = 75, + .thickness = 4, .filled = true, .border_color = asc_col4i_new(0, 255, 0, 255)) + ); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 350, .y = 250, .width = 100, .height = 75, + .radius = 15, .filled = true) + ); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 350, .y = 50, .width = 100, .height = 75, + .filled = true) + ); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 50, .y = 350, .width = 100, .height = 75, + .radius = 15) + ); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 350, .y = 350, .width = 100, .height = 75) + ); + // Main Loop do {