summaryrefslogtreecommitdiff
path: root/src/modules/mixer
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2023-06-13 22:42:37 -0700
committerVito Caputo <vcaputo@pengaru.com>2023-06-13 23:27:25 -0700
commit6e79300f342f2f21936365f8d91e758aa1494200 (patch)
tree3fdbdfed6eb631b57e426bd629f41ccfeca4599d /src/modules/mixer
parenta6ac8cd03439931a8783a7f505c0a7879734b1ec (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.
Diffstat (limited to 'src/modules/mixer')
-rw-r--r--src/modules/mixer/mixer.c147
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,
+};
© All Rights Reserved