Thu, 26 Jun 2025 21:43:22 +0200
improve "polymorphic" typing of shaders
src/2d.c | file | annotate | diff | comparison | revisions | |
src/ascension/datatypes.h | file | annotate | diff | comparison | revisions | |
src/ascension/shader.h | file | annotate | diff | comparison | revisions | |
src/shader.c | file | annotate | diff | comparison | revisions | |
src/sprite.c | file | annotate | diff | comparison | revisions |
--- a/src/2d.c Wed Jun 25 21:58:44 2025 +0200 +++ b/src/2d.c Thu Jun 26 21:43:22 2025 +0200 @@ -63,35 +63,35 @@ asc_error("Loading rectangle shader failed."); return NULL; } - // TODO: find better way to deal with inheritance (&shader->program just looks bad) - AscRectangleShader *shader = asc_shader_create(codes, sizeof(*shader)); - if (asc_shader_invalid(&shader->program)) { + AscShaderProgram *shader = asc_shader_create(codes, sizeof(AscRectangleShader)); + if (asc_shader_invalid(shader)) { asc_shader_free_codes(codes); - return (AscShaderProgram*) shader; + return shader; } - shader->size = asc_shader_get_uniform_loc(&shader->program, "size"); + asc_ptr_cast(AscRectangleShader, rect_shader, shader); + rect_shader->size = asc_shader_get_uniform_loc(shader, "size"); if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_FILL)) { - shader->color = asc_shader_get_uniform_loc(&shader->program, "color"); + rect_shader->color = asc_shader_get_uniform_loc(shader, "color"); } else { - shader->color = -1; + rect_shader->color = -1; } if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_BORDER)) { - shader->thickness = asc_shader_get_uniform_loc(&shader->program, "thickness"); - shader->border_color = asc_shader_get_uniform_loc(&shader->program, "border_color"); + rect_shader->thickness = asc_shader_get_uniform_loc(shader, "thickness"); + rect_shader->border_color = asc_shader_get_uniform_loc(shader, "border_color"); } else { - shader->thickness = -1; - shader->border_color = -1; + rect_shader->thickness = -1; + rect_shader->border_color = -1; } if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_ROUND)) { - shader->radius = asc_shader_get_uniform_loc(&shader->program, "radius"); + rect_shader->radius = asc_shader_get_uniform_loc(shader, "radius"); } else { - shader->radius = -1; + rect_shader->radius = -1; } asc_shader_free_codes(codes); asc_error_catch_all_gl(); - return (AscShaderProgram*) shader; + return shader; } static void asc_rectangle_destroy(AscSceneNode *node) { @@ -105,7 +105,7 @@ } static void asc_rectangle_draw(const AscCamera *camera, const AscSceneNode *node) { - asc_ptr_cast(AscRectangle, rectangle, node); + asc_cptr_cast(AscRectangle, rectangle, node); const bool filled = rectangle->filled; const bool round = rectangle->radius > 0; const bool border = rectangle->thickness > 0; @@ -129,26 +129,25 @@ }; // Look up and activate shader - const AscRectangleShader *shader = asc_shader_lookup_or_create( + const AscShaderProgram *shader = asc_shader_lookup_or_create( shader_ids[shader_flags], asc_rectangle_shader_create, shader_flags); - // TODO: how to remove the following cast? - if (asc_shader_invalid((AscShaderProgram*)shader)) return; - asc_shader_use(&shader->program, camera); + if (asc_shader_use(shader, camera)) return; + asc_cptr_cast(AscRectangleShader, rect_shader, shader); // Upload uniforms - asc_shader_upload_model_matrix(&shader->program, node); + asc_shader_upload_model_matrix(shader, node); if (filled) { - asc_shader_upload_col4f(shader->color, rectangle->color); + asc_shader_upload_col4f(rect_shader->color, rectangle->color); } - asc_shader_upload_vec2f(shader->size, rectangle->size); + asc_shader_upload_vec2f(rect_shader->size, rectangle->size); if (border) { - asc_shader_upload_float(shader->thickness, rectangle->thickness); - asc_shader_upload_col4f(shader->border_color, rectangle->border_color); + asc_shader_upload_float(rect_shader->thickness, rectangle->thickness); + asc_shader_upload_col4f(rect_shader->border_color, rectangle->border_color); } if (round) { - asc_shader_upload_float(shader->radius, rectangle->radius); + asc_shader_upload_float(rect_shader->radius, rectangle->radius); } // Draw mesh
--- a/src/ascension/datatypes.h Wed Jun 25 21:58:44 2025 +0200 +++ b/src/ascension/datatypes.h Thu Jun 26 21:43:22 2025 +0200 @@ -61,6 +61,7 @@ #define asc_set_flag_masked(reg, mask, flag) (reg = (reg & ~(mask)) | flag) #define asc_ptr_cast(type, lvalue, rvalue) type *lvalue = (type *)(rvalue); +#define asc_cptr_cast(type, lvalue, rvalue) const type *lvalue = (const type *)(rvalue); // -------------------------------------------------------------------------- // Datatype Definitions
--- a/src/ascension/shader.h Wed Jun 25 21:58:44 2025 +0200 +++ b/src/ascension/shader.h Thu Jun 26 21:43:22 2025 +0200 @@ -152,14 +152,11 @@ /** * Creates a shader program. * - * @note This function intentionally has an unspecified pointer return type - * so that you can assign the return value to your shader struct pointer without casts. - * * @param codes the (zero-terminated) source codes * @param mem_size the memory size required for the structure * @return the compiled and linked shader program with @c AscShaderProgram as base struct */ -void *asc_shader_create(AscShaderCodes codes, size_t mem_size); +AscShaderProgram *asc_shader_create(AscShaderCodes codes, size_t mem_size); /** * Frees a shader program. @@ -171,51 +168,41 @@ /** * Activates a shader for use. * - * Does nothing when the shader is already active. + * Does nothing and immediately returns zero when the shader is already active. * * @param shader the shader program to use or @c NULL to deactivate any shader * @param camera the camera matrices (view/projection) to upload + * @retval zero when the shader is now active + * @retval non-zero when the shader that was supposed to be activated is invalid */ -void asc_shader_use(const AscShaderProgram *shader, const AscCamera *camera); +int asc_shader_use(const AscShaderProgram *shader, const AscCamera *camera); /** * Registers a shader under a certain ID. * - * @note This function intentionally has an unspecified pointer return type - * so that you can assign the return value to your shader struct pointer without casts. - * All shader structs must be based on @c AscShaderProgram. - * * @param id the custom ID of the shader * @param create_func the function that creates the shader * @param create_flags flags passed to create_func * @return the shader created by the @c create_func or @c NULL when shader compilation failed * @see asc_shader_lookup() */ -const void *asc_shader_register(unsigned int id, asc_shader_create_func create_func, int create_flags); +const AscShaderProgram *asc_shader_register(unsigned int id, asc_shader_create_func create_func, int create_flags); /** * Looks up a shader by ID. * - * @note This function intentionally has an unspecified pointer return type - * so that you can assign the return value to your shader struct pointer without casts. - * All shader structs must be based on @c AscShaderProgram. - * * @param id the ID of the shader to look up * @return the shader or @c NULL if no shader with such ID exists * @see asc_shader_register() */ -const void *asc_shader_lookup(unsigned int id); +const AscShaderProgram *asc_shader_lookup(unsigned int id); /** * Looks up a shader by ID or registers a new one if no shader exists with the specified ID. * * This function can be used for lazy initialization of shaders. * - * @note This function intentionally has an unspecified pointer return type - * so that you can assign the return value to your shader struct pointer without casts. - * All shader structs must be based on @c AscShaderProgram. - * * @param id the custom ID of the shader * @param create_func the function to create the shader * @param create_flags flags passed to create_func @@ -223,7 +210,7 @@ * @see asc_shader_lookup() * @see asc_shader_register() */ -const void *asc_shader_lookup_or_create(unsigned int id, asc_shader_create_func create_func, int create_flags); +const AscShaderProgram *asc_shader_lookup_or_create(unsigned int id, asc_shader_create_func create_func, int create_flags); /**
--- a/src/shader.c Wed Jun 25 21:58:44 2025 +0200 +++ b/src/shader.c Thu Jun 26 21:43:22 2025 +0200 @@ -158,7 +158,7 @@ return program == NULL || program->gl_id == 0; } -void *asc_shader_create(AscShaderCodes codes, size_t mem_size) { +AscShaderProgram *asc_shader_create(AscShaderCodes codes, size_t mem_size) { AscShaderProgram *prog = cxZallocDefault(mem_size); unsigned shader[2]; unsigned n = 0; @@ -173,17 +173,19 @@ return prog; } -void asc_shader_use(const AscShaderProgram *shader, const AscCamera *camera) { +int asc_shader_use(const AscShaderProgram *shader, const AscCamera *camera) { if (shader == NULL) { asc_active_glctx->active_program = 0; glUseProgram(0); - return; + return 0; } - if (asc_active_glctx->active_program == shader->gl_id) return; + if (asc_active_glctx->active_program == shader->gl_id) return 0; + if (asc_shader_invalid(shader)) return -1; asc_active_glctx->active_program = shader->gl_id; glUseProgram(shader->gl_id); glUniformMatrix4fv(shader->projection, 1, GL_FALSE, camera->projection); glUniformMatrix4fv(shader->view, 1, GL_FALSE, camera->view); + return asc_error_catch_all_gl(); } static int asc_shader_load_code_file(const char *filename, char **code) { @@ -223,7 +225,7 @@ cxFreeDefault(codes.frag); } -const void *asc_shader_register(unsigned int id, asc_shader_create_func create_func, int create_flags) { +const AscShaderProgram *asc_shader_register(unsigned int id, asc_shader_create_func create_func, int create_flags) { AscGLContext *glctx = asc_active_glctx; #ifndef NDEBUG { @@ -242,7 +244,7 @@ return prog; } -const void *asc_shader_lookup(unsigned int id) { +const AscShaderProgram *asc_shader_lookup(unsigned int id) { CxIterator iter = cxListIterator(asc_active_glctx->shaders); cx_foreach(const AscShaderProgram *, prog, iter) { if (prog->id == id) return prog; @@ -250,7 +252,7 @@ return NULL; } -const void *asc_shader_lookup_or_create(unsigned int id, asc_shader_create_func create_func, int create_flags) { +const AscShaderProgram *asc_shader_lookup_or_create(unsigned int id, asc_shader_create_func create_func, int create_flags) { const AscShaderProgram *prog = asc_shader_lookup(id); if (prog == NULL) { return asc_shader_register(id, create_func, create_flags);
--- a/src/sprite.c Wed Jun 25 21:58:44 2025 +0200 +++ b/src/sprite.c Thu Jun 26 21:43:22 2025 +0200 @@ -50,28 +50,28 @@ asc_error("Loading sprite shader failed."); return NULL; } - // TODO: find better way to deal with inheritance (&shader->program just looks bad) - AscSpriteShader *shader = asc_shader_create(codes, sizeof(*shader)); - if (asc_shader_invalid(&shader->program)) { + AscShaderProgram *shader = asc_shader_create(codes, sizeof(*shader)); + if (asc_shader_invalid(shader)) { asc_shader_free_codes(codes); - return (AscShaderProgram*) shader; + return shader; } - shader->tex = asc_shader_get_uniform_loc(&shader->program, "tex"); + asc_ptr_cast(AscSpriteShader, sprite_shader, shader); + sprite_shader->tex = asc_shader_get_uniform_loc(shader, "tex"); asc_shader_free_codes(codes); asc_error_catch_all_gl(); - return (AscShaderProgram*) shader; + return shader; } static void asc_sprite_destroy(AscSceneNode *node) { - AscSprite *sprite = (AscSprite *) node; + asc_ptr_cast(AscSprite, sprite, node); asc_mesh_destroy(&sprite->mesh); sprite->texture->refcount--; } static void asc_sprite_update(AscSceneNode *node) { - AscSprite *sprite = (AscSprite *) node; + asc_ptr_cast(AscSprite, sprite, node); // calculate texture parameters asc_vec2f uv_scale; @@ -90,23 +90,22 @@ } void asc_sprite_draw(const AscCamera *camera, const AscSceneNode *node) { - asc_ptr_cast(const AscSprite, sprite, node); + asc_cptr_cast(AscSprite, sprite, node); // Activate shader // TODO: scene should know which shader we are going to activate s.t. it can pre-sort nodes - const AscSpriteShader *shader = + const AscShaderProgram *shader = asc_texture_is_uv_normalized(sprite->texture) ? asc_shader_lookup_or_create(ASC_SHADER_SPRITE_UV, asc_sprite_shader_create, 0) : asc_shader_lookup_or_create(ASC_SHADER_SPRITE_RECT, asc_sprite_shader_create, 1); - // TODO: how to remove the following cast? - if (asc_shader_invalid((AscShaderProgram*)shader)) return; - asc_shader_use(&shader->program, camera); + if (asc_shader_use(shader, camera)) return; + asc_cptr_cast(AscSpriteShader, sprite_shader, shader); // Upload model matrix - asc_shader_upload_model_matrix(&shader->program, node); + asc_shader_upload_model_matrix(shader, node); // Bind texture - asc_texture_bind(sprite->texture, shader->tex, 0); + asc_texture_bind(sprite->texture, sprite_shader->tex, 0); // Draw mesh asc_mesh_draw_triangle_strip(&sprite->mesh);