[gegl/wip/pippin/pipeline: 81/95] gegl: add pipeline infrastructure



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]