diff --git a/azur/include/azur/gint/render.h b/azur/include/azur/gint/render.h index f21612c..e253b8c 100644 --- a/azur/include/azur/gint/render.h +++ b/azur/include/azur/gint/render.h @@ -44,6 +44,10 @@ AZUR_BEGIN_DECLS * [fragment] is a pointer to azrp_frag. */ typedef void azrp_shader_t(void *uniforms, void *command, void *fragment); +/* azrp_shader_configure_t: Type of shader configuration functions + This function is mainly called when fragment settings change. */ +typedef void azrp_shader_configure_t(void); + /* Video memory fragment used as rendering target (in XRAM). */ extern uint16_t *azrp_frag; @@ -207,15 +211,6 @@ void azrp_triangle(int x1, int y1, int x2, int y2, int x3, int y3, int color); /* See below for more detailed image functions. Dynamic effects are provided with the same naming convention as gint. */ -/* Functions to update uniforms for these shaders. You should call them when: - * AZRP_SHADER_CLEAR: Changing super-scaling settings. - * AZRP_SHADER_IMAGE_*: Changing super-scaling or or fragment offsets. */ -void azrp_shader_clear_configure(void); -void azrp_shader_image_rgb16_configure(void); -void azrp_shader_image_p8_configure(void); -void azrp_shader_image_p4_configure(void); -void azrp_shader_triangle_configure(void); - //--- // Performance indicators // @@ -250,13 +245,20 @@ void azrp_perf_clear(void); /* azrp_register_shader(): Register a new command type and its shader program - This function adds the specified shader program to the program array, and - returns the corresponding command type. Adding new shaders is useful for - specialized rendering options (eg. tiles with fixed size) or new graphical - effects. + This function registers a new shader program to the program array, along + with its configuration function. The configuration function is called + immediately upon registration. - If the maximum number shaders is exceeded, returns -1. */ -int azrp_register_shader(azrp_shader_t *program); + There is often a choice between creating a new shader and generalizing an + existing one. The impact is small; the difference only really matters if + there is a lot of commands, but in that case command management becomes a + stronger bottleneck. The choice should be made for optimal code structure + and reuse. + + Returns the shader ID to be set in commands, or -1 if the maximum number of + shaders has been exceeded. */ +int azrp_register_shader(azrp_shader_t *program, + azrp_shader_configure_t *configure); /* azrp_set_uniforms(): Set a shader's uniforms pointer diff --git a/azur/src/gint/render.c b/azur/src/gint/render.c index 4926f52..d99abca 100644 --- a/azur/src/gint/render.c +++ b/azur/src/gint/render.c @@ -32,9 +32,19 @@ static uint32_t commands_array[AZRP_MAX_COMMANDS]; static GALIGNED(4) uint8_t commands_data[16384]; -/* Array of shader programs and uniforms. */ -static azrp_shader_t *shaders[AZRP_MAX_SHADERS] = { NULL }; -static void *shader_uniforms[AZRP_MAX_SHADERS] = { NULL }; +/* Shader program information. */ +typedef struct { + /* Rendering function. */ + azrp_shader_t *shader; + /* Uniform parameter. */ + void *uniform; + /* Configuration function (in response to scale, base offset, etc). */ + azrp_shader_configure_t *configure; + +} shader_info_t; + +/* Array of shader programs. */ +static shader_info_t shaders[AZRP_MAX_SHADERS] = { 0 }; /* Next free index in the shader program array. */ static uint16_t shaders_next = 0; @@ -116,9 +126,12 @@ void azrp_render_fragments(void) while(cmd < next_frag_threshold && i < commands_count) { azrp_commands_total++; uint8_t *data = commands_data + (cmd & 0xffff); + shader_info_t const *info = &shaders[data[0]]; + prof_enter_norec(azrp_perf_shaders); - shaders[data[0]](shader_uniforms[data[0]], data, azrp_frag); + info->shader(info->uniform, data, azrp_frag); prof_leave_norec(azrp_perf_shaders); + cmd = commands_array[++i]; } @@ -152,6 +165,14 @@ void azrp_update(void) // Configuration calls //--- +static void reconfigure_all_shaders(void) +{ + for(int i = 0; i < shaders_next; i++) { + if(shaders[i].configure) + shaders[i].configure(); + } +} + // TODO: Use larger fragments in upscales x2 and x3 static void update_frag_count(void) @@ -184,6 +205,7 @@ void azrp_config_scale(int scale) azrp_scale = scale; update_size(); update_frag_count(); + reconfigure_all_shaders(); } void azrp_config_frag_offset(int offset) @@ -193,9 +215,13 @@ void azrp_config_frag_offset(int offset) azrp_frag_offset = offset; update_frag_count(); + reconfigure_all_shaders(); } -__attribute__((constructor)) +/* Make sure this constructor runs before every shader's registration + constructor so we don't configure registered shaders before the settings are + initialized. */ +__attribute__((constructor(101))) static void default_settings(void) { azrp_config_scale(1); @@ -219,14 +245,19 @@ void azrp_hook_set_prefrag(azrp_hook_prefrag_t *hook) // Custom shaders //--- -int azrp_register_shader(azrp_shader_t *program) +int azrp_register_shader(azrp_shader_t *program, + azrp_shader_configure_t *configure) { int id = shaders_next; - if(id >= AZRP_MAX_SHADERS) return -1; - shaders[shaders_next++] = program; + shader_info_t *info = &shaders[id]; + info->shader = program; + info->uniform = NULL; + info->configure = configure; + shaders_next++; + return id; } @@ -234,10 +265,7 @@ void azrp_set_uniforms(int shader_id, void *uniforms) { if((unsigned int)shader_id >= AZRP_MAX_SHADERS) return; - if(shaders[shader_id] == NULL) - return; - - shader_uniforms[shader_id] = uniforms; + shaders[shader_id].uniform = uniforms; } bool azrp_queue_command(void *command, size_t size, int fragment, int count) diff --git a/azur/src/gint/shaders/clear.c b/azur/src/gint/shaders/clear.c index aad1a20..8cb1970 100644 --- a/azur/src/gint/shaders/clear.c +++ b/azur/src/gint/shaders/clear.c @@ -2,17 +2,18 @@ uint8_t AZRP_SHADER_CLEAR = -1; +static void configure(void) +{ + int longs_in_fragment = (azrp_width * azrp_frag_height / 2); + azrp_set_uniforms(AZRP_SHADER_CLEAR, (void *)longs_in_fragment); +} + __attribute__((constructor)) static void register_shader(void) { extern azrp_shader_t azrp_shader_clear; - AZRP_SHADER_CLEAR = azrp_register_shader(azrp_shader_clear); -} - -void azrp_shader_clear_configure(void) -{ - int longs_in_fragment = (azrp_width * azrp_frag_height / 2); - azrp_set_uniforms(AZRP_SHADER_CLEAR, (void *)longs_in_fragment); + AZRP_SHADER_CLEAR = azrp_register_shader(azrp_shader_clear, configure); + configure(); } //--- diff --git a/azur/src/gint/shaders/image_p4.c b/azur/src/gint/shaders/image_p4.c index 61c0438..52bc8d4 100644 --- a/azur/src/gint/shaders/image_p4.c +++ b/azur/src/gint/shaders/image_p4.c @@ -12,15 +12,16 @@ static void shader_p4(void *uniforms, void *command, void *fragment) cmd->output = fragment + cmd->x * 2; } +static void configure(void) +{ + azrp_set_uniforms(AZRP_SHADER_IMAGE_P4, (void *)azrp_width); +} + __attribute__((constructor)) static void register_shader(void) { - AZRP_SHADER_IMAGE_P4 = azrp_register_shader(shader_p4); -} - -void azrp_shader_image_p4_configure(void) -{ - azrp_set_uniforms(AZRP_SHADER_IMAGE_P4, (void *)azrp_width); + AZRP_SHADER_IMAGE_P4 = azrp_register_shader(shader_p4, configure); + configure(); } void azrp_image_p4(int x, int y, image_t const *img, int eff) diff --git a/azur/src/gint/shaders/image_p8.c b/azur/src/gint/shaders/image_p8.c index 02e8500..9987a98 100644 --- a/azur/src/gint/shaders/image_p8.c +++ b/azur/src/gint/shaders/image_p8.c @@ -12,15 +12,16 @@ static void shader_p8(void *uniforms, void *command, void *fragment) cmd->output = fragment + cmd->x * 2; } +static void configure(void) +{ + azrp_set_uniforms(AZRP_SHADER_IMAGE_P8, (void *)azrp_width); +} + __attribute__((constructor)) static void register_shader(void) { - AZRP_SHADER_IMAGE_P8 = azrp_register_shader(shader_p8); -} - -void azrp_shader_image_p8_configure(void) -{ - azrp_set_uniforms(AZRP_SHADER_IMAGE_P8, (void *)azrp_width); + AZRP_SHADER_IMAGE_P8 = azrp_register_shader(shader_p8, configure); + configure(); } void azrp_image_p8(int x, int y, image_t const *img, int eff) diff --git a/azur/src/gint/shaders/image_rgb16.c b/azur/src/gint/shaders/image_rgb16.c index 956ecab..4b49d24 100644 --- a/azur/src/gint/shaders/image_rgb16.c +++ b/azur/src/gint/shaders/image_rgb16.c @@ -12,15 +12,16 @@ static void shader_rgb16(void *uniforms, void *command, void *fragment) cmd->output = fragment + cmd->x * 2; } +static void configure(void) +{ + azrp_set_uniforms(AZRP_SHADER_IMAGE_RGB16, (void *)azrp_width); +} + __attribute__((constructor)) static void register_shader(void) { - AZRP_SHADER_IMAGE_RGB16 = azrp_register_shader(shader_rgb16); -} - -void azrp_shader_image_rgb16_configure(void) -{ - azrp_set_uniforms(AZRP_SHADER_IMAGE_RGB16, (void *)azrp_width); + AZRP_SHADER_IMAGE_RGB16 = azrp_register_shader(shader_rgb16, configure); + configure(); } void azrp_image_rgb16(int x, int y, image_t const *img, int eff) diff --git a/azur/src/gint/shaders/triangle.c b/azur/src/gint/shaders/triangle.c index 852d44d..6d1c2cb 100644 --- a/azur/src/gint/shaders/triangle.c +++ b/azur/src/gint/shaders/triangle.c @@ -2,16 +2,18 @@ uint8_t AZRP_SHADER_TRIANGLE = -1; +static void configure(void) +{ + azrp_set_uniforms(AZRP_SHADER_TRIANGLE, (void *)azrp_width); +} + __attribute__((constructor)) static void register_shader(void) { extern azrp_shader_t azrp_shader_triangle; - AZRP_SHADER_TRIANGLE = azrp_register_shader(azrp_shader_triangle); -} - -void azrp_shader_triangle_configure(void) -{ - azrp_set_uniforms(AZRP_SHADER_TRIANGLE, (void *)azrp_width); + AZRP_SHADER_TRIANGLE = azrp_register_shader(azrp_shader_triangle, + configure); + configure(); } static int min(int x, int y)