diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2023-06-13 22:42:37 -0700 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2023-06-13 23:27:25 -0700 |
commit | 6e79300f342f2f21936365f8d91e758aa1494200 (patch) | |
tree | 3fdbdfed6eb631b57e426bd629f41ccfeca4599d | |
parent | a6ac8cd03439931a8783a7f505c0a7879734b1ec (diff) |
modules/mixer: add a fade style, make default
This is not optimized at all and tends to hurt the FPS
significantly. This is one of those things that would hugely
benefit from SIMD, but even without SIMD it could be done better.
I just slapped together something obvious for now, as I'd like to
focus more on the rkt side but need a better fader for scene
transitions than style=flicker.
Also changed {a,b}_module= preferred values to blank,compose
so you see something happen if you just run --defaults.
Otherwise, the compose,compose would just fade between two
identical compositions invisibly.
-rw-r--r-- | src/modules/mixer/mixer.c | 147 |
1 files changed, 127 insertions, 20 deletions
diff --git a/src/modules/mixer/mixer.c b/src/modules/mixer/mixer.c index f0cb174..7de91e6 100644 --- a/src/modules/mixer/mixer.c +++ b/src/modules/mixer/mixer.c @@ -16,6 +16,7 @@ /* This implements a rudimentary mixing module for things like fades */ typedef enum mixer_style_t { + MIXER_STYLE_FADE, MIXER_STYLE_FLICKER, } mixer_style_t; @@ -38,6 +39,7 @@ typedef struct mixer_context_t { float *T; mixer_input_t inputs[2]; + til_fb_fragment_t *snapshots[2]; } mixer_context_t; typedef struct mixer_setup_input_t { @@ -52,23 +54,7 @@ typedef struct mixer_setup_t { mixer_setup_input_t inputs[2]; } mixer_setup_t; -#define MIXER_DEFAULT_STYLE MIXER_STYLE_FLICKER - -static til_module_context_t * mixer_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup); -static void mixer_destroy_context(til_module_context_t *context); -static void mixer_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr); -static int mixer_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup); - - -til_module_t mixer_module = { - .create_context = mixer_create_context, - .destroy_context = mixer_destroy_context, - .render_fragment = mixer_render_fragment, - .name = "mixer", - .description = "Module blender", - .setup = mixer_setup, - .flags = TIL_MODULE_EXPERIMENTAL, -}; +#define MIXER_DEFAULT_STYLE MIXER_STYLE_FADE static til_module_context_t * mixer_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup) @@ -113,12 +99,15 @@ static inline float randf(unsigned *seed) return 1.f / ((float)RAND_MAX) * rand_r(seed); } -static void mixer_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr) + +static void mixer_prepare_frame(til_module_context_t *context, til_stream_t *stream, unsigned ticks, til_fb_fragment_t **fragment_ptr, til_frame_plan_t *res_frame_plan) { mixer_context_t *ctxt = (mixer_context_t *)context; til_fb_fragment_t *fragment = *fragment_ptr; size_t i = 0; + *res_frame_plan = (til_frame_plan_t){ .fragmenter = til_fragmenter_slice_per_cpu }; + if (!til_stream_tap_context(stream, context, NULL, &ctxt->taps.T)) *ctxt->T = cosf(ticks * .001f) * .5f + .5f; @@ -128,18 +117,121 @@ static void mixer_render_fragment(til_module_context_t *context, til_stream_t *s i = 1; else i = 0; + + til_module_render(ctxt->inputs[i].module_ctxt, stream, ticks, &fragment); + break; + + case MIXER_STYLE_FADE: + if (*ctxt->T < 1.f) { + til_module_render(ctxt->inputs[0].module_ctxt, stream, ticks, &fragment); + + if (*ctxt->T > 0.f) + ctxt->snapshots[0] = til_fb_fragment_snapshot(&fragment, 0); + } + + if (*ctxt->T > 0.f) { + til_module_render(ctxt->inputs[1].module_ctxt, stream, ticks, &fragment); + if (*ctxt->T < 1.f) + ctxt->snapshots[1] = til_fb_fragment_snapshot(&fragment, 0); + } break; default: assert(0); } - til_module_render(ctxt->inputs[i].module_ctxt, stream, ticks, &fragment); + *fragment_ptr = fragment; +} + + +/* derived from modules/drizzle pixel_mult_scalar(), there's definitely room for optimizations */ +static inline uint32_t pixels_lerp(uint32_t a_pixel, uint32_t b_pixel, float t) +{ + uint32_t pixel; + float a, b; + + /* r */ + a = (a_pixel >> 16) & 0xff; + a *= 1.f - t; + b = (b_pixel >> 16) & 0xff; + b *= t; + + pixel = (((uint32_t)(a+b)) << 16); + + /* g */ + a = (a_pixel >> 8) & 0xff; + a *= 1.f - t; + b = (b_pixel >> 8) & 0xff; + b *= t; + + pixel |= (((uint32_t)(a+b)) << 8); + + /* b */ + a = a_pixel & 0xff; + a *= 1.f - t; + b = b_pixel & 0xff; + b *= t; + + pixel |= ((uint32_t)(a+b)); + + return pixel; +} + + +static void mixer_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr) +{ + mixer_context_t *ctxt = (mixer_context_t *)context; + til_fb_fragment_t *fragment = *fragment_ptr; + + switch (((mixer_setup_t *)context->setup)->style) { + case MIXER_STYLE_FLICKER: + /* handled in prepare_frame() */ + break; + + case MIXER_STYLE_FADE: { + float T = *ctxt->T; + + if (T <= 0.f || T >= 1.f) + break; + + /* for the tweens, we already have snapshots sitting in ctxt->snapshots[], + * which we now interpolate the pixels out of in parallel + */ + for (int y = fragment->y; y < fragment->y + fragment->height; y++) { + for (int x = fragment->x; x < fragment->x + fragment->width; x++) { + uint32_t a_pixel = til_fb_fragment_get_pixel_unchecked(ctxt->snapshots[0], x, y); + uint32_t b_pixel = til_fb_fragment_get_pixel_unchecked(ctxt->snapshots[1], x, y); + uint32_t pixel; + + pixel = pixels_lerp(a_pixel, b_pixel, T); + til_fb_fragment_put_pixel_unchecked(fragment, 0, x, y, pixel); + } + } + + break; + } + + default: + assert(0); + } *fragment_ptr = fragment; } +static void mixer_finish_frame(til_module_context_t *context, til_stream_t *stream, unsigned int ticks, til_fb_fragment_t **fragment_ptr) +{ + mixer_context_t *ctxt = (mixer_context_t *)context; + + for (int i = 0; i < 2; i++) { + if (!ctxt->snapshots[i]) + continue; + + ctxt->snapshots[i] = til_fb_fragment_reclaim(ctxt->snapshots[i]); + } +} + + static void mixer_setup_free(til_setup_t *setup) { mixer_setup_t *s = (mixer_setup_t *)setup; @@ -157,6 +249,7 @@ static void mixer_setup_free(til_setup_t *setup) static int mixer_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) { const char *style_values[] = { + "fade", "flicker", NULL }; @@ -185,6 +278,7 @@ static int mixer_setup(const til_settings_t *settings, til_setting_t **res_setti const char *input_names[2] = { "First module to mix", "Second module to mix" }; const char *input_keys[2] = { "a_module", "b_module" }; const char *input_module_name_names[2] = { "First module's name", "Second module's name" }; + const char *input_preferred[2] = { "blank", "compose" }; for (int i = 0; i < 2; i++) { const til_module_t *mod; @@ -193,7 +287,7 @@ static int mixer_setup(const til_settings_t *settings, til_setting_t **res_setti &(til_setting_spec_t){ .name = input_names[i], .key = input_keys[i], - .preferred = "compose", + .preferred = input_preferred[i], .annotations = NULL, .as_nested_settings = 1, }, @@ -282,3 +376,16 @@ static int mixer_setup(const til_settings_t *settings, til_setting_t **res_setti return 0; } + + +til_module_t mixer_module = { + .create_context = mixer_create_context, + .destroy_context = mixer_destroy_context, + .prepare_frame = mixer_prepare_frame, + .render_fragment = mixer_render_fragment, + .finish_frame = mixer_finish_frame, + .name = "mixer", + .description = "Module blender", + .setup = mixer_setup, + .flags = TIL_MODULE_EXPERIMENTAL, +}; |