summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2019-11-24 13:06:03 -0800
committerVito Caputo <vcaputo@pengaru.com>2019-11-24 13:06:03 -0800
commitcc1dea6ec94aad181c05c157ca679c5dc1c1004d (patch)
tree7eaead7e67a5fdfd97fa3efbb788d3865cfff82b /src/modules
parent13189997111deac782df2be3db4e4669d5635ca2 (diff)
montage: add montage module
This is somewhat unfinished as it uses the generic tiled fragmenter that's not interested in appearances but prioritizes total coverage and simplicity. Montage should have its own tiler that can produce non-square and even non-uniform tile dimensions, prioritizing filling the screen with mostly-uniform tiles. But that's a TODO item, this is good enough for now and exercises some fragment details previously irrelevant and often ignored/broken in modules. The pixbounce module in particular seems completely broken with small fragment sizes.
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/Makefile.am2
-rw-r--r--src/modules/montage/Makefile.am3
-rw-r--r--src/modules/montage/montage.c168
3 files changed, 172 insertions, 1 deletions
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
index 0ab81b7..1622814 100644
--- a/src/modules/Makefile.am
+++ b/src/modules/Makefile.am
@@ -1 +1 @@
-SUBDIRS = flui2d julia pixbounce plasma ray roto rtv snow sparkler stars submit swab
+SUBDIRS = flui2d julia montage pixbounce plasma ray roto rtv snow sparkler stars submit swab
diff --git a/src/modules/montage/Makefile.am b/src/modules/montage/Makefile.am
new file mode 100644
index 0000000..7465510
--- /dev/null
+++ b/src/modules/montage/Makefile.am
@@ -0,0 +1,3 @@
+noinst_LIBRARIES = libmontage.a
+libmontage_a_SOURCES = montage.c
+libmontage_a_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs
diff --git a/src/modules/montage/montage.c b/src/modules/montage/montage.c
new file mode 100644
index 0000000..068c6e0
--- /dev/null
+++ b/src/modules/montage/montage.c
@@ -0,0 +1,168 @@
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "fb.h"
+#include "rototiller.h"
+#include "settings.h"
+#include "util.h"
+
+/* Copyright (C) 2019 - Vito Caputo <vcaputo@pengaru.com> */
+
+typedef struct montage_context_t {
+ const rototiller_module_t **modules;
+ const rototiller_module_t *rtv_module;
+ void **contexts;
+ size_t n_modules;
+ unsigned n_cpus;
+} montage_context_t;
+
+static void setup_next_module(montage_context_t *ctxt);
+static void * montage_create_context(unsigned num_cpus);
+static void montage_destroy_context(void *context);
+static void montage_prepare_frame(void *context, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter);
+static void montage_render_fragment(void *context, unsigned cpu, fb_fragment_t *fragment);
+
+
+rototiller_module_t montage_module = {
+ .create_context = montage_create_context,
+ .destroy_context = montage_destroy_context,
+ .prepare_frame = montage_prepare_frame,
+ .render_fragment = montage_render_fragment,
+ .name = "montage",
+ .description = "Rototiller montage",
+ .author = "Vito Caputo <vcaputo@pengaru.com>",
+ .license = "GPLv2",
+};
+
+
+static int skip_module(montage_context_t *ctxt, const rototiller_module_t *module)
+{
+ /* prevent recursion */
+ if (module == &montage_module)
+ return 1;
+
+ /* also prevents recursion, as rtv could run montage... */
+ if (module == ctxt->rtv_module)
+ return 1;
+
+ return 0;
+}
+
+
+static void * montage_create_context(unsigned num_cpus)
+{
+ montage_context_t *ctxt = calloc(1, sizeof(montage_context_t));
+
+ ctxt->n_cpus = num_cpus;
+ rototiller_get_modules(&ctxt->modules, &ctxt->n_modules);
+
+ ctxt->contexts = calloc(ctxt->n_modules, sizeof(void *));
+ if (!ctxt->contexts) {
+ free(ctxt);
+
+ return NULL;
+ }
+
+ ctxt->rtv_module = rototiller_lookup_module("rtv");
+
+ for (int i = 0; i < ctxt->n_modules; i++) {
+ const rototiller_module_t *module = ctxt->modules[i];
+
+ if (skip_module(ctxt, module))
+ continue;
+
+ if (module->create_context) /* FIXME errors */
+ ctxt->contexts[i] = module->create_context(num_cpus);
+ }
+
+ return ctxt;
+}
+
+
+static void montage_destroy_context(void *context)
+{
+ montage_context_t *ctxt = context;
+
+ for (int i = 0; i < ctxt->n_modules; i++) {
+ const rototiller_module_t *module = ctxt->modules[i];
+
+ if (skip_module(ctxt, module))
+ continue;
+
+ if (!ctxt->contexts[i])
+ continue;
+
+ module->destroy_context(ctxt->contexts[i]);
+ }
+
+ free(context);
+}
+
+
+/* The fragmenter in montage is serving double-duty:
+ * 1. it divides the frame into subfragments for threaded rendering
+ * 2. it determines which modules will be rendered where via fragment->number
+ */
+static int montage_fragmenter(void *context, const fb_fragment_t *fragment, unsigned number, fb_fragment_t *res_fragment)
+{
+ montage_context_t *ctxt = context;
+ float root = sqrtf((float)ctxt->n_modules);
+ int ret;
+
+ ret = fb_fragment_tile_single(fragment, fragment->frame_height / root, number, res_fragment);
+ if (!ret)
+ return 0;
+
+ if (number >= ctxt->n_modules)
+ return 0;
+
+ /* as these tiles are frames of their own rather than subfragments, override these values */
+ res_fragment->x = res_fragment->y = 0;
+ res_fragment->frame_width = res_fragment->width;
+ res_fragment->frame_height = res_fragment->height;
+
+ return ret;
+}
+
+
+
+static void montage_prepare_frame(void *context, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter)
+{
+ montage_context_t *ctxt = context;
+
+ *res_fragmenter = montage_fragmenter;
+}
+
+
+static void montage_render_fragment(void *context, unsigned cpu, fb_fragment_t *fragment)
+{
+ montage_context_t *ctxt = context;
+ const rototiller_module_t *module = ctxt->modules[fragment->number];
+
+ if (skip_module(ctxt, module))
+ return;
+
+ /* since we're *already* in a threaded render of tiles, no further
+ * threading within the montage tiles is desirable, so the per-module
+ * render is done explicitly serially here in an open-coded ad-hoc
+ * fashion for now. FIXME TODO: move this into rototiller.c
+ */
+ if (module->prepare_frame) {
+ rototiller_fragmenter_t unused;
+
+ /* XXX FIXME: ignoring the fragmenter here is a violation of the module API,
+ * rototiller.c should have a module render interface with explicit non-threading
+ * that still does all the necessary fragmenting as needed.
+ *
+ * Today, I can get away with this, because montage is the only module that's
+ * sensitive to this aspect of the API and it skips itself.
+ */
+
+ module->prepare_frame(ctxt->contexts[fragment->number], 1, fragment, &unused);
+ }
+
+ if (module->render_fragment)
+ module->render_fragment(ctxt->contexts[fragment->number], 0, fragment);
+}
+
© All Rights Reserved