[gegl/wip/pippin/pipeline: 81/95] gegl: add pipeline infrastructure
- From: Øyvind "pippin" Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl/wip/pippin/pipeline: 81/95] gegl: add pipeline infrastructure
- Date: Wed, 12 Sep 2018 11:55:11 +0000 (UTC)
commit b1c520aca5e276c59f74c8a0be3a867129b1487c
Author: Øyvind Kolås <pippin gimp org>
Date: Mon Jul 23 17:59:16 2018 +0200
gegl: add pipeline infrastructure
The pipeline is a way to execute a set of point operations, be they
filter, composer or composer3 variants, support for point-renderers
should also be added.
The end result is that instead of using separate iteration calls one
large multi-buffer iterator is set up that on a tile by tile invokes
the processing functions of the operations in order.
gegl/operation/Makefile.am | 4 +-
gegl/operation/gegl-operation-pipeline.c | 430 +++++++++++++++++++++++++++++++
gegl/operation/gegl-operation-pipeline.h | 35 +++
3 files changed, 468 insertions(+), 1 deletion(-)
---
diff --git a/gegl/operation/Makefile.am b/gegl/operation/Makefile.am
index c9aaaf543..91c3add36 100644
--- a/gegl/operation/Makefile.am
+++ b/gegl/operation/Makefile.am
@@ -59,10 +59,12 @@ liboperation_sources = \
gegl-operation-sink.c \
gegl-operation-source.c \
gegl-operation-temporal.c \
+ gegl-operation-pipeline.c \
+ gegl-operation-pipeline.h \
gegl-operation-context.c \
gegl-operation-context-private.h \
gegl-operations.c \
- gegl-operations.h
+ gegl-operations.h
noinst_LTLIBRARIES = liboperation.la
diff --git a/gegl/operation/gegl-operation-pipeline.c b/gegl/operation/gegl-operation-pipeline.c
new file mode 100644
index 000000000..63f1b4033
--- /dev/null
+++ b/gegl/operation/gegl-operation-pipeline.c
@@ -0,0 +1,430 @@
+#include "config.h"
+
+#include <glib-object.h>
+#define GEGL_ITERATOR2_API // opt in to new buffer iterator API
+
+#include "gegl.h"
+#include "gegl-debug.h"
+#include "gegl-operation-point-filter.h"
+#include "gegl-operation-point-composer.h"
+#include "gegl-operation-point-composer3.h"
+#include "gegl-operation-context.h"
+#include "gegl-operation-pipeline.h"
+#include "gegl-config.h"
+#include "gegl-types-internal.h"
+#include "gegl-buffer-private.h"
+#include "gegl-tile-storage.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#define PIPELINE_MAX 32
+
+typedef struct {
+ GeglOperation *operation;
+ int in_pads;
+ void (*process)(void *);
+ GeglBuffer *aux;
+ GeglBuffer *aux2;
+ int aux_handle;
+ int aux2_handle;
+ const Babl *input_fish;
+
+ const Babl *in_format;
+ const Babl *aux_format;
+ const Babl *aux2_format;
+ const Babl *out_format;
+} PipeEntry;
+
+
+/* first make it work for a filter op masking its own output as
+ * a single entry pipeline
+ *
+ * then generalize/expand
+ */
+
+
+struct _PipeLine {
+ int entries;
+ GeglBuffer *input;
+ int buffers_used;
+ PipeEntry entry[PIPELINE_MAX];
+};
+
+
+static gboolean is_pipelinable_op (GeglOperation *op)
+{
+ return GEGL_IS_OPERATION_POINT_FILTER (op) ||
+ GEGL_IS_OPERATION_POINT_COMPOSER (op) ||
+ GEGL_IS_OPERATION_POINT_COMPOSER3 (op);
+}
+
+#if 0
+static int in_pads_for_op (GeglOperation *op)
+{
+ if (GEGL_IS_OPERATION_POINT_FILTER (op)) return 1;
+ if (GEGL_IS_OPERATION_POINT_COMPOSER (op)) return 2;
+ if (GEGL_IS_OPERATION_POINT_COMPOSER3 (op)) return 3;
+ return 0;
+}
+#endif
+
+static GeglNode *gegl_node_get_non_nop_producer (GeglNode *n)
+{
+ GeglNode *node = gegl_node_get_producer (n, "input", NULL);
+ while (node &&
+ (!strcmp (gegl_node_get_operation (node), "gegl:nop")))
+ {
+ node = gegl_node_get_producer (node, "input", NULL);
+ }
+ return node;
+}
+
+PipeLine *
+gegl_operation_pipeline_ensure (GeglOperation *operation,
+ GeglOperationContext *context,
+ GeglBuffer *input)
+{
+ PipeLine *pipeline;
+ GeglNode *source_node;
+ GeglOperationContext *source_context;
+ source_node = gegl_node_get_non_nop_producer (operation->node);
+ if (!source_node)
+ return FALSE;
+
+ source_context = gegl_operation_context_node_get_context (context, source_node);
+ pipeline = gegl_operation_context_get_pipeline (source_context);
+ gegl_operation_context_set_pipeline (source_context, NULL);
+
+ if (!pipeline)
+ {
+ pipeline = g_malloc0 (sizeof (PipeLine));
+ pipeline->buffers_used = 2; // input and output
+ }
+ gegl_operation_context_set_pipeline (context, pipeline);
+
+ if (gegl_operation_pipeline_get_entries (pipeline) == 0)
+ gegl_operation_pipeline_set_input (pipeline, input);
+
+ return pipeline;
+}
+
+
+/* we should not be intermediate if adding more bits to the pipeline
+ would not work
+ */
+gboolean
+gegl_operation_pipeline_is_intermediate_node (GeglOperation *op,
+ PipeLine *pipeline)
+{
+ gboolean is_intermediate = TRUE;
+ gint n_consumers;
+ GeglNode *it = op->node;
+ GeglNode **consumers = NULL;
+
+ n_consumers = gegl_node_get_consumers (it, "output", &consumers, NULL);
+ it = consumers[0];
+ while (n_consumers == 1 && (!strcmp (gegl_node_get_operation (consumers[0]), "gegl:nop")))
+ {
+ it = consumers[0];
+ g_free (consumers);
+ n_consumers = gegl_node_get_consumers (it, "output", &consumers, NULL);
+ }
+
+ if (n_consumers == 0)
+ {
+ is_intermediate = FALSE;
+ }
+ else if (n_consumers == 1)
+ {
+ GeglOperation *sink = gegl_node_get_gegl_operation (consumers[0]);
+
+ if (! is_pipelinable_op (sink))
+ {
+ is_intermediate = FALSE;
+ }
+
+ if (pipeline->entries + 1 >= PIPELINE_MAX)
+ {
+ is_intermediate = FALSE;
+ }
+
+#if 0 /* no longer needed with the new buffer iterator that has configurable
+ size
+*/
+ /* would blow our budget of buffer handles in buffer iterator API */
+ if (pipeline->buffers_used + (in_pads_for_op (sink)-1) >
+ GEGL_BUFFER_MAX_ITERATORS)
+ {
+ is_intermediate = FALSE;
+ }
+#endif
+
+ }
+ else
+ is_intermediate = FALSE;
+
+ g_free (consumers);
+
+ return is_intermediate;
+}
+
+gboolean
+gegl_operation_pipeline_is_composite_node (GeglOperation *op)
+{
+#if 0 // unused, perhaps remove?
+ GeglNode *source_node;
+ GeglOperation *source;
+ source_node = gegl_node_get_producer (op->node, "input", NULL);
+
+ if (!source_node)
+ return FALSE;
+
+ source = gegl_node_get_gegl_operation (source_node);
+
+ return (is_pipelinable_op (source) &&
+ gegl_operation_pipeline_is_intermediate_node (source));
+#endif
+ return FALSE;
+}
+
+
+gboolean
+gegl_operation_pipeline_process (PipeLine *pipeline,
+ GeglBuffer *output,
+ const GeglRectangle *result,
+ gint level)
+{
+ GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level,
pipeline->entry[pipeline->entries-1].out_format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE,
pipeline->buffers_used+1);
+ gint input_handle = 0;
+ void *temp[2]={NULL, NULL};
+ glong high_tide = 0;
+ void *cur_input = NULL;
+ void *cur_output = NULL;
+ int buf_mod = 0;
+ fprintf (stderr, "{%i}", pipeline->entries);
+
+ if (pipeline->input)
+ input_handle = gegl_buffer_iterator_add (i, pipeline->input, result, level,
+ pipeline->entry[0].in_format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+ {
+ gint e;
+ for (e = 0; e < pipeline->entries; e++)
+ {
+ switch (pipeline->entry[e].in_pads)
+ {
+ case 3:
+ if (pipeline->entry[e].aux2)
+ pipeline->entry[e].aux2_handle =
+ gegl_buffer_iterator_add (i, pipeline->entry[e].aux2, result, level,
+ pipeline->entry[e].aux2_format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ case 2:
+ if (pipeline->entry[e].aux)
+ pipeline->entry[e].aux_handle =
+ gegl_buffer_iterator_add (i, pipeline->entry[e].aux, result, level,
+ pipeline->entry[e].aux_format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ case 1:
+ default:
+ break;
+ }
+ }
+ }
+
+ while (gegl_buffer_iterator_next (i))
+ {
+ gint e;
+ if (i->length > high_tide)
+ {
+ if (temp[0])
+ g_free (temp[0]);
+ high_tide = i->length;
+ temp[0] = g_malloc (4 * 8 * high_tide * 2);
+ temp[1] = (char*)(temp[0]) + 4 * 8 * high_tide;
+ /* the two allocations are merged into one, to reduce overhead,
+ if sufficiently small for the architecture - this could use
+ stack allocations
+ */
+ }
+
+ for (e = 0; e < pipeline->entries; e++)
+ {
+ PipeEntry *entry = &pipeline->entry[e];
+ if (e == 0)
+ {
+ cur_input = i->items[input_handle].data;
+ }
+ else
+ {
+ if (!entry->input_fish)
+ {
+ cur_input = cur_output;
+ }
+ else
+ {
+ cur_input = temp[(buf_mod++)&1];
+ babl_process (entry->input_fish,
+ cur_output, cur_input, i->length);
+
+ }
+ }
+ if (pipeline->entries == e+1)
+ {
+ cur_output = i->items[0].data;
+ }
+ else
+ {
+ cur_output = temp[(buf_mod++)&1];
+ // XXX: through refactoring some in-place
+ // processing on same-format should be achivable
+ // for extra performance
+ }
+
+ switch (entry->in_pads)
+ {
+ case 1:
+ {
+ gboolean (*process)(GeglOperation *,
+ void *,
+ void *,
+ glong,
+ const GeglRectangle *,
+ gint) = (void*)entry->process;
+
+ process (entry->operation,
+ cur_input,
+ cur_output,
+ i->length,
+ &(i->items[0].roi),
+ level);
+ }break;
+
+ case 2:
+ {
+ gboolean (*process)(GeglOperation *,
+ void *,
+ void *,
+ void *,
+ glong,
+ const GeglRectangle *,
+ gint) = (void*)entry->process;
+
+ process (entry->operation,
+ cur_input,
+ entry->aux?
+ i->items[entry->aux_handle].data:
+ NULL,
+ cur_output,
+ i->length,
+ &(i->items[0].roi),
+ level);
+ }break;
+
+ case 3:
+ {
+ gboolean (*process)(GeglOperation *,
+ void *,
+ void *,
+ void *,
+ void *,
+ glong,
+ const GeglRectangle *,
+ gint) = (void*)entry->process;
+ process (entry->operation,
+ cur_input,
+ entry->aux?
+ i->items[entry->aux_handle].data:
+ NULL,
+ entry->aux2?
+ i->items[entry->aux2_handle].data:
+ NULL,
+ cur_output,
+ i->length,
+ &(i->items[0].roi),
+ level);
+ }break;
+ }
+ }
+ }
+ g_clear_object (&pipeline->input);
+
+ {
+ gint e;
+ for (e = 0; e < pipeline->entries; e++)
+ {
+ PipeEntry *entry = &pipeline->entry[e];
+
+ switch (entry->in_pads)
+ {
+ case 3:
+ if (entry->aux2)
+ g_clear_object (&entry->aux2);
+ case 2:
+ if (entry->aux)
+ g_clear_object (&entry->aux);
+ case 1:
+ default:
+ break;
+ }
+ }
+ }
+
+ if (temp[0])
+ g_free (temp[0]);
+
+ return TRUE;
+}
+
+void
+gegl_operation_pipeline_add (PipeLine *pipeline,
+ GeglOperation *operation,
+ gint in_pads,
+ const Babl *in_format,
+ const Babl *out_format,
+ const Babl *aux_format,
+ const Babl *aux2_format,
+ GeglBuffer *aux,
+ GeglBuffer *aux2,
+ void *process)
+{
+ PipeEntry *entry = &pipeline->entry[pipeline->entries];
+ PipeEntry *prev_entry = pipeline->entries?&pipeline->entry[pipeline->entries-1]:NULL;
+ g_assert (pipeline->entries + 1 <= PIPELINE_MAX);
+
+ entry->operation = operation;
+ entry->in_pads = in_pads;
+ entry->in_format = in_format;
+ entry->aux_format = aux_format;
+ entry->aux = aux;
+ entry->aux2_format= aux2_format;
+ entry->aux2 = aux2;
+ entry->out_format = out_format;
+ entry->process = process;
+
+ if (prev_entry && entry->in_format != prev_entry->out_format)
+ {
+ entry->input_fish = babl_fish (prev_entry->out_format, entry->in_format);
+ }
+
+ pipeline->entries++;
+
+ pipeline->buffers_used += ( (aux?1:0) + (aux2?1:0));
+}
+
+int gegl_operation_pipeline_get_entries (PipeLine *pipeline)
+{
+ return pipeline->entries;
+}
+
+
+
+void gegl_operation_pipeline_set_input (PipeLine *pipeline,
+ GeglBuffer *buffer)
+{
+ pipeline->input = buffer;
+}
diff --git a/gegl/operation/gegl-operation-pipeline.h b/gegl/operation/gegl-operation-pipeline.h
new file mode 100644
index 000000000..f343f7e69
--- /dev/null
+++ b/gegl/operation/gegl-operation-pipeline.h
@@ -0,0 +1,35 @@
+#ifndef _GEGL_OPERATION_PIPELINE_H
+#define _GEGL_OPERATION_PIPELINE_H
+
+int gegl_operation_pipeline_get_entries (PipeLine *pipeline);
+void gegl_operation_pipeline_set_input (PipeLine *pipeline,
+ GeglBuffer *buffer);
+
+gboolean
+gegl_operation_pipeline_is_intermediate_node (GeglOperation *op,
+ PipeLine *pipeline);
+gboolean gegl_operation_pipeline_is_composite_node (GeglOperation *op);
+
+gboolean gegl_operation_pipeline_process (PipeLine *pipeline,
+ GeglBuffer *output,
+ const GeglRectangle *result,
+ gint level);
+PipeLine *
+gegl_operation_pipeline_ensure (GeglOperation *operation,
+ GeglOperationContext *context,
+ GeglBuffer *input);
+
+
+void
+gegl_operation_pipeline_add (PipeLine *pipeline,
+ GeglOperation *operation,
+ gint in_pads,
+ const Babl *in_format,
+ const Babl *out_format,
+ const Babl *aux_format,
+ const Babl *aux2_format,
+ GeglBuffer *aux,
+ GeglBuffer *aux2,
+ void *process);
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]