improve "polymorphic" typing of shaders default tip

Thu, 26 Jun 2025 21:43:22 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 26 Jun 2025 21:43:22 +0200
changeset 169
6e6717d9c776
parent 168
f70569c49c24

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);

mercurial