summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2023-01-10 23:26:06 -0800
committerVito Caputo <vcaputo@pengaru.com>2023-01-11 22:31:26 -0800
commitfe4d2000013329ed9882fb900a19a6fe03876c52 (patch)
treefe9a2e5ebbdb24c87feed7d0f10d3b6237315a66
parentd0a302aff0b3da86aa32b3cfea177ec41cc6488d (diff)
til_stream: first stab at implementing til_stream_t
Until now there's just been a forward declared type for til_fb_fragment_t.stream's type, and it's been completely unused. The purpose of the stream is to provide a continous inter-frame object where information can be stored pertaining to the stream of frames. Right now, that information is limited to emergent "pipes" formed by using taps against a given stream. Taps at new paths in the stream become added as pipes for those paths, with the responsible tap hooked in as the driving tap. Taps at existing paths become diverted to the driving taps, enabling potential for external drivers for tapped variables. This commit only adds the implementation, nothing is actually using it yet.
-rw-r--r--src/Makefile.am2
-rw-r--r--src/til_stream.c339
-rw-r--r--src/til_stream.h27
3 files changed, 367 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index efb7cc0..458294c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,7 +1,7 @@
SUBDIRS = libs modules
noinst_LTLIBRARIES = libtil.la
-libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_jenkins.c til_jenkins.h til_knobs.h til.c til.h til_module_context.c til_module_context.h til_settings.h til_settings.c til_setup.c til_setup.h til_tap.h til_threads.c til_threads.h til_util.c til_util.h
+libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_jenkins.c til_jenkins.h til_knobs.h til.c til.h til_module_context.c til_module_context.h til_settings.h til_settings.c til_setup.c til_setup.h til_stream.c til_stream.h til_tap.h til_threads.c til_threads.h til_util.c til_util.h
libtil_la_CPPFLAGS = -I@top_srcdir@/src
libtil_la_LIBADD = modules/blinds/libblinds.la modules/checkers/libcheckers.la modules/compose/libcompose.la modules/drizzle/libdrizzle.la modules/flui2d/libflui2d.la modules/julia/libjulia.la modules/meta2d/libmeta2d.la modules/moire/libmoire.la modules/montage/libmontage.la modules/pixbounce/libpixbounce.la modules/plasma/libplasma.la modules/plato/libplato.la modules/ray/libray.la modules/roto/libroto.la modules/rtv/librtv.la modules/shapes/libshapes.la modules/snow/libsnow.la modules/sparkler/libsparkler.la modules/spiro/libspiro.la modules/stars/libstars.la modules/strobe/libstrobe.la modules/submit/libsubmit.la modules/swab/libswab.la modules/swarm/libswarm.la modules/voronoi/libvoronoi.la libs/grid/libgrid.la libs/puddle/libpuddle.la libs/ray/libray.la libs/sig/libsig.la libs/txt/libtxt.la libs/ascii/libascii.la libs/din/libdin.la
diff --git a/src/til_stream.c b/src/til_stream.c
new file mode 100644
index 0000000..0f13d1f
--- /dev/null
+++ b/src/til_stream.c
@@ -0,0 +1,339 @@
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "til_stream.h"
+#include "til_tap.h"
+
+/* A stream in libtil is basically a hash table for tracking dynamic
+ * information for modules to create/modify/access. The objects stored in the
+ * table are "stream pipes" and endpoints called taps, making this something
+ * like a miniature in-memory implementation of named pipes, conceptually
+ * anyways (there are no actual file descriptors).
+ */
+
+#if 0
+/* example usage: */
+typedef struct foo_t {
+ struct {
+ til_tap_t position;
+ } taps;
+ struct {
+ v2f_t position;
+ } vars;
+
+ v2f_t *position;
+} foo_t;
+
+foo_t * foo_create_context(void)
+{
+ foo_t *foo = malloc(sizeof(foo_t));
+
+ /* This creates an isolated (pipe-)tap binding our local position variable and pointer
+ * to a name for later "tapping" onto a stream.
+ */
+ foo->taps.position = til_tap_init_v2f(&foo->position, 1, &foo->vars.position, "position");
+}
+
+foo_render(foo_t *foo, til_fb_fragment_t *fragment)
+{
+ if (!til_stream_tap_context(fragment->stream, foo, &foo->taps.position)) {
+ /* got nothing, we're driving position */
+ foo->position->x = cosf(ticks);
+ foo->position->y = sinf(ticks);
+ } /* else { got something, just use foo->position as-is */
+}
+#endif
+
+
+#define TIL_STREAM_BUCKETS_COUNT 256
+
+typedef struct til_stream_pipe_t til_stream_pipe_t;
+
+struct til_stream_pipe_t {
+ til_stream_pipe_t *next;
+ const void *owner;
+ char *parent_path;
+ const til_tap_t *driving_tap;
+ uint32_t hash;
+};
+
+typedef struct til_stream_t {
+ pthread_mutex_t mutex;
+ til_stream_pipe_t *buckets[TIL_STREAM_BUCKETS_COUNT];
+} til_stream_t;
+
+
+til_stream_t * til_stream_new(void)
+{
+ til_stream_t *stream;
+
+ stream = calloc(1, sizeof(til_stream_t));
+ if (!stream)
+ return NULL;
+
+ pthread_mutex_init(&stream->mutex, NULL);
+
+ return stream;
+}
+
+
+til_stream_t * til_stream_free(til_stream_t *stream)
+{
+ if (!stream)
+ return NULL;
+
+ for (int i = 0; i < TIL_STREAM_BUCKETS_COUNT; i++) {
+ for (til_stream_pipe_t *p = stream->buckets[i], *p_next; p != NULL; p = p_next) {
+ p_next = p->next;
+ free(p->parent_path);
+ free(p);
+ }
+ }
+
+ pthread_mutex_destroy(&stream->mutex);
+ free(stream);
+
+ return NULL;
+}
+
+
+/* Taps the key-named type-typed pipe on the supplied stream.
+ * If this is the first use of the tap on this stream, new pipe will be created.
+ * If the tap exists on this stream, and the type matches, existing pipe will be used as-is.
+ * If the key exists on this stream, but the type mismatches, an error is returned.
+ *
+ * -errno is returned on error, 0 when tap is driving, 1 when tap is passenger.
+ *
+ * If stream is NULL it's treated as if the key doesn't exist without a pipe creation.
+ */
+int til_stream_tap(til_stream_t *stream, const void *owner, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap)
+{
+ uint32_t hash, bucket;
+ til_stream_pipe_t *pipe;
+
+ assert(tap);
+
+ if (!stream) {
+ *(tap->ptr) = tap->elems;
+
+ return 0;
+ }
+
+ pthread_mutex_lock(&stream->mutex);
+
+ hash = (tap->name_hash ^ parent_hash);
+ bucket = hash % TIL_STREAM_BUCKETS_COUNT;
+ for (pipe = stream->buckets[bucket]; pipe != NULL; pipe = pipe->next) {
+ if (pipe->hash == hash) {
+ if (pipe->driving_tap == tap) {
+ /* this is our pipe and we're driving */
+ *(tap->ptr) = pipe->driving_tap->elems;
+
+ pthread_mutex_unlock(&stream->mutex);
+ return 0;
+ }
+
+ if (pipe->driving_tap->elems == *(tap->ptr) ||
+ (!strcmp(pipe->driving_tap->name, tap->name) && !strcmp(pipe->parent_path, parent_path))) {
+ /* this looks to be our pipe, but we're not driving */
+ *(tap->ptr) = pipe->driving_tap->elems;
+
+ pthread_mutex_unlock(&stream->mutex);
+ return 1;
+ }
+ }
+ }
+
+ /* matching pipe not found, create new one with tap as driver */
+ pipe = calloc(1, sizeof(til_stream_pipe_t));
+ if (!pipe) {
+ pthread_mutex_unlock(&stream->mutex);
+ return -ENOMEM;
+ }
+
+ pipe->owner = owner;
+ pipe->driving_tap = tap;
+
+ pipe->parent_path = strdup(parent_path);
+ if (!pipe->parent_path) {
+ free(pipe);
+
+ pthread_mutex_unlock(&stream->mutex);
+ return -ENOMEM;
+ }
+
+ pipe->hash = hash;
+ pipe->next = stream->buckets[bucket];
+ stream->buckets[bucket] = pipe;
+
+ pthread_mutex_unlock(&stream->mutex);
+ return 0;
+}
+
+
+/* remove all pipes belonging to owner in stream */
+void til_stream_untap_owner(til_stream_t *stream, const void *owner)
+{
+ for (int i = 0; i < TIL_STREAM_BUCKETS_COUNT; i++) {
+ for (til_stream_pipe_t *p = stream->buckets[i], *p_next, *p_prev; p != NULL; p = p_next) {
+ p_next = p->next;
+
+ if (p->owner == owner) {
+ if (p == stream->buckets[i])
+ stream->buckets[i] = p_next;
+ else
+ p_prev->next = p_next;
+
+ free(p);
+ } else
+ p_prev = p;
+ }
+ }
+}
+
+
+/* We need the higher-order types defined in order to print their contents.
+ * libtil should probably just formally define these smoewhere for modules to
+ * make use of. Until now it's been very deliberate to try leave modules to be
+ * relatively self-contained, even if they often reinvent the wheel as a
+ * result... it's relatively harmless for small functionalities while keeping
+ * the listings easier to grok as a whole esp. for a newcomer who isn't
+ * necessarily comfortable jumping around a sprawling tree of files.
+ */
+typedef struct v2f_t { float x, y; } v2f_t;
+
+typedef struct v3f_t {
+ float x, y, z;
+} v3f_t;
+
+typedef struct v4f_t {
+ float x, y, z, w;
+} v4f_t;
+
+/* XXX: note that while yes, this does acquire stream->mutex to serialize access to the table/pipes,
+ * this mutex does not serialize access to the tapped variables. So if this print is performed
+ * during the threaded rendering phase of things, it will technically be racy. The only strictly
+ * correct place to perform this print race-free is in the rendering loop between submissions to
+ * the rendering threads. Arguably if careful about only printing while serial, there's no need
+ * to acquire the mutex - but it's also an uncontended lock if that's the case so just take the
+ * mutex anyways since we're accessing the underlying structure it protects.
+ */
+void til_stream_fprint(til_stream_t *stream, FILE *out)
+{
+ fprintf(out, "Pipes on stream %p:\n", stream);
+ pthread_mutex_lock(&stream->mutex);
+ for (int i = 0; i < TIL_STREAM_BUCKETS_COUNT; i++) {
+ for (til_stream_pipe_t *p = stream->buckets[i]; p != NULL; p = p->next) {
+ fprintf(out, "%s/%s: ", p->parent_path, p->driving_tap->name);
+
+ for (size_t j = 0; j < p->driving_tap->n_elems; j++) {
+ const char *sep = j ? ", " : "";
+
+ switch (p->driving_tap->type) {
+ case TIL_TAP_TYPE_I8:
+ fprintf(out, "%"PRIi8"%s",
+ *(*((int8_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_I16:
+ fprintf(out, "%"PRIi16"%s",
+ *(*((int16_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_I32:
+ fprintf(out, "%"PRIi32"%s",
+ *(*((int32_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_I64:
+ fprintf(out, "%"PRIi64"%s",
+ *(*((int64_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U8:
+ fprintf(out, "%"PRIu8"%s",
+ *(*((int8_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U16:
+ fprintf(out, "%"PRIu16"%s",
+ *(*((int16_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U32:
+ fprintf(out, "%"PRIu32"%s",
+ *(*((int32_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_U64:
+ fprintf(out, "%"PRIu64"%s",
+ *(*((int64_t **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_FLOAT:
+ fprintf(out, "%f%s",
+ *(*((float **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_DOUBLE:
+ fprintf(out, "%f%s",
+ *(*((double **)p->driving_tap->ptr)),
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_V2F:
+ fprintf(out, "{%f,%f}%s",
+ (*((v2f_t **)p->driving_tap->ptr))->x,
+ (*((v2f_t **)p->driving_tap->ptr))->y,
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_V3F:
+ fprintf(out, "{%f,%f,%f}%s",
+ (*((v3f_t **)p->driving_tap->ptr))->x,
+ (*((v3f_t **)p->driving_tap->ptr))->y,
+ (*((v3f_t **)p->driving_tap->ptr))->z,
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_V4F:
+ fprintf(out, "{%f,%f,%f,%f}%s",
+ (*((v4f_t **)p->driving_tap->ptr))->x,
+ (*((v4f_t **)p->driving_tap->ptr))->y,
+ (*((v4f_t **)p->driving_tap->ptr))->z,
+ (*((v4f_t **)p->driving_tap->ptr))->w,
+ sep);
+ break;
+
+ case TIL_TAP_TYPE_M4F:
+ fprintf(out, "M4F TODO%s", sep);
+ break;
+
+ case TIL_TAP_TYPE_VOIDP:
+ fprintf(out, "%p%s", *((void **)p->driving_tap->ptr), sep);
+ break;
+
+ default:
+ assert(0);
+ }
+ fprintf(out, "\n");
+ }
+ }
+ }
+ pthread_mutex_unlock(&stream->mutex);
+ fprintf(out, "\n");
+}
diff --git a/src/til_stream.h b/src/til_stream.h
new file mode 100644
index 0000000..91eaa55
--- /dev/null
+++ b/src/til_stream.h
@@ -0,0 +1,27 @@
+#ifndef _TIL_STREAM_H
+#define _TIL_STREAM_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "til_module_context.h"
+
+typedef struct til_stream_t til_stream_t;
+typedef struct til_tap_t til_tap_t;
+
+til_stream_t * til_stream_new(void);
+til_stream_t * til_stream_free(til_stream_t *stream);
+
+/* bare interface for non-module-context owned taps */
+int til_stream_tap(til_stream_t *stream, const void *tap_owner, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap);
+
+/* convenience helper for use within modules */
+static inline int til_stream_tap_context(til_stream_t *stream, const til_module_context_t *module_context, const til_tap_t *tap)
+{
+ return til_stream_tap(stream, module_context, module_context->path, module_context->path_hash, tap);
+}
+
+void til_stream_untap_owner(til_stream_t *stream, const void *owner);
+void til_stream_fprint(til_stream_t *stream, FILE *out);
+
+#endif
© All Rights Reserved