[gegl] New graph processor



commit 782641491200e0d3a2c0b583872e401da961fed6
Author: Daniel Sabo <DanielSabo gmail com>
Date:   Mon Jun 3 23:14:08 2013 -0700

    New graph processor
    
    Streamline the graph evaluation process to reduce overhead.
    
    Important changes / gotchas:
    * get_cached_region is no longer used. The cache bounds are
      always the full output bounds, but the request will not be
      expanded.
    * The cache intersect is calculated after all requests to a node
      are combined, so partial cache hits from multiple nodes may
      skip the cache.
    * Meta nodes (nodes who's pads do not reference them) will never
      be visited.
    * Prepare is called only once per node, and must not modify the
      connectivity of the graph.

 gegl/Makefile.am                            |    1 +
 gegl/gegl-apply.c                           |    3 -
 gegl/gegl.h                                 |   12 +
 gegl/graph/Makefile.am                      |    2 +-
 gegl/graph/gegl-node.c                      |  372 ++++------------------
 gegl/graph/gegl-node.h                      |   11 +-
 gegl/graph/gegl-visitor.c                   |  104 +------
 gegl/graph/gegl-visitor.h                   |    3 -
 gegl/operation/gegl-operation-context.c     |   14 +-
 gegl/operation/gegl-operation-context.h     |    4 +-
 gegl/operation/gegl-operation.c             |    2 +
 gegl/operation/gegl-operation.h             |    2 -
 gegl/operation/gegl-operations.c            |   91 ------
 gegl/process/Makefile.am                    |   21 +-
 gegl/process/gegl-debug-rect-visitor.c      |   79 -----
 gegl/process/gegl-debug-rect-visitor.h      |   53 ---
 gegl/process/gegl-eval-manager.c            |  225 ++++----------
 gegl/process/gegl-eval-manager.h            |   35 +--
 gegl/process/gegl-eval-visitor.c            |  220 -------------
 gegl/process/gegl-eval-visitor.h            |   55 ----
 gegl/process/gegl-finish-visitor.c          |   61 ----
 gegl/process/gegl-finish-visitor.h          |   53 ---
 gegl/process/gegl-graph-debug.h             |   40 +++
 gegl/process/gegl-graph-traversal-debug.c   |   95 ++++++
 gegl/process/gegl-graph-traversal-private.h |   31 ++
 gegl/process/gegl-graph-traversal.c         |  453 +++++++++++++++++++++++++++
 gegl/process/gegl-graph-traversal.h         |   17 +
 gegl/process/gegl-have-visitor.c            |   85 -----
 gegl/process/gegl-have-visitor.h            |   53 ---
 gegl/process/gegl-list-visitor.c            |  120 +++++++
 gegl/process/gegl-list-visitor.h            |   60 ++++
 gegl/process/gegl-need-visitor.c            |   82 -----
 gegl/process/gegl-need-visitor.h            |   53 ---
 gegl/process/gegl-prepare-visitor.c         |  104 ------
 gegl/process/gegl-prepare-visitor.h         |   53 ---
 gegl/process/gegl-processor.c               |   27 +-
 36 files changed, 1006 insertions(+), 1690 deletions(-)
---
diff --git a/gegl/Makefile.am b/gegl/Makefile.am
index b5120ea..1c5189c 100644
--- a/gegl/Makefile.am
+++ b/gegl/Makefile.am
@@ -51,6 +51,7 @@ GEGL_introspectable_headers = \
     buffer/gegl-tile-backend.h         \
     buffer/gegl-tile-handler.h         \
     buffer/gegl-tile-source.h          \
+    process/gegl-graph-debug.h         \
     property-types/gegl-paramspecs.h   \
     property-types/gegl-color.h                \
     property-types/gegl-path.h         \
diff --git a/gegl/gegl-apply.c b/gegl/gegl-apply.c
index 3cb7f2d..a5b1bb2 100644
--- a/gegl/gegl-apply.c
+++ b/gegl/gegl-apply.c
@@ -37,9 +37,6 @@
 #include "operation/gegl-operation-point-filter.h"
 
 #include "process/gegl-eval-manager.h"
-#include "process/gegl-have-visitor.h"
-#include "process/gegl-prepare-visitor.h"
-#include "process/gegl-finish-visitor.h"
 #include "process/gegl-processor.h"
 
 
diff --git a/gegl/gegl.h b/gegl/gegl.h
index ce5ddcb..7f4a2a9 100644
--- a/gegl/gegl.h
+++ b/gegl/gegl.h
@@ -421,6 +421,18 @@ void          gegl_node_blit             (GeglNode            *node,
                                           GeglBlitFlags        flags);
 
 /**
+ * gegl_node_blit_buffer:
+ * @node: a #GeglNode
+ * @buffer: (transfer none) (allow-none): the #GeglBuffer to render to.
+ * @roi: (allow-none): the rectangle to render.
+ *
+ * Render a rectangular region from a node to the given buffer.
+ */
+void          gegl_node_blit_buffer      (GeglNode            *node,
+                                          GeglBuffer          *buffer,
+                                          const GeglRectangle *roi);
+
+/**
  * gegl_node_process:
  * @sink_node: a #GeglNode without outputs.
  *
diff --git a/gegl/graph/Makefile.am b/gegl/graph/Makefile.am
index a66702b..b72e24c 100644
--- a/gegl/graph/Makefile.am
+++ b/gegl/graph/Makefile.am
@@ -13,7 +13,7 @@ AM_CFLAGS = $(DEP_CFLAGS) $(BABL_CFLAGS)
 
 noinst_LTLIBRARIES = libgraph.la
 
-#libgraph_publicdir = $(includedir)/gegl-$(GEGL_API_VERSION)/gegl/graph
+#libgraph_publicdir = $(includedir)/gegl-$(GEGL_API_VERSION)/graph
 #libgraph_public_HEADERS = #
 
 libgraph_la_SOURCES = \
diff --git a/gegl/graph/gegl-node.c b/gegl/graph/gegl-node.c
index 4285f2f..15e8712 100644
--- a/gegl/graph/gegl-node.c
+++ b/gegl/graph/gegl-node.c
@@ -15,6 +15,7 @@
  *
  * Copyright 2003 Calvin Williamson
  *           2006 Øyvind Kolås
+ *           2013 Daniel Sabo
  */
 
 #include "config.h"
@@ -35,14 +36,13 @@
 #include "gegl-visitable.h"
 #include "gegl-config.h"
 
+#include "graph/gegl-visitor.h"
+
 #include "operation/gegl-operation.h"
 #include "operation/gegl-operations.h"
 #include "operation/gegl-operation-meta.h"
 
 #include "process/gegl-eval-manager.h"
-#include "process/gegl-have-visitor.h"
-#include "process/gegl-prepare-visitor.h"
-#include "process/gegl-finish-visitor.h"
 #include "process/gegl-processor.h"
 
 enum
@@ -70,8 +70,7 @@ struct _GeglNodePrivate
   GeglNode        *parent;
   gchar           *name;
   GeglProcessor   *processor;
-  GHashTable      *contexts;
-  GeglEvalManager *eval_manager[GEGL_MAX_THREADS];
+  GeglEvalManager *eval_manager;
 };
 
 
@@ -191,8 +190,6 @@ gegl_node_init (GeglNode *self)
                                             GEGL_TYPE_NODE,
                                             GeglNodePrivate);
 
-  self->priv->contexts = g_hash_table_new (NULL, NULL);
-
   self->pads        = NULL;
   self->input_pads  = NULL;
   self->output_pads = NULL;
@@ -232,15 +229,11 @@ gegl_node_dispose (GObject *gobject)
       self->cache = NULL;
     }
 
-  {
-    gint i;
-    for (i=0; i<GEGL_MAX_THREADS; i++)
-      if (self->priv->eval_manager[i])
-        {
-          g_object_unref (self->priv->eval_manager[i]);
-          self->priv->eval_manager[i] = NULL;
-        }
-  }
+  if (self->priv->eval_manager)
+    {
+      g_object_unref (self->priv->eval_manager);
+      self->priv->eval_manager = NULL;
+    }
 
   if (self->priv->processor)
     {
@@ -278,7 +271,6 @@ gegl_node_finalize (GObject *gobject)
     {
       g_free (self->priv->name);
     }
-  g_hash_table_destroy (self->priv->contexts);
   g_mutex_clear (&self->mutex);
 
   G_OBJECT_CLASS (gegl_node_parent_class)->finalize (gobject);
@@ -864,92 +856,59 @@ gegl_node_link_many (GeglNode *source,
   va_end (var_args);
 }
 
-static void gegl_node_ensure_eval_manager (GeglNode    *self,
-                                       const gchar *pad,
-                                       gint         no)
+static GeglEvalManager *
+gegl_node_get_eval_manager (GeglNode *self)
 {
-  if (!self->priv->eval_manager[no])
-    self->priv->eval_manager[no] = gegl_eval_manager_new (self, pad);
+  if (!self->priv->eval_manager)
+    self->priv->eval_manager = gegl_eval_manager_new (self, "output");
+  return self->priv->eval_manager;
 }
 
-/* Will set the eval_manager's roi to the supplied roi if defined, otherwise
- * it will use the node's bounding box. Then the gegl_eval_manager_apply will
- * be called.
- */
 static GeglBuffer *
 gegl_node_apply_roi (GeglNode            *self,
-                     const gchar         *output_pad_name,
-                     const GeglRectangle *roi,
-                     gint                 tid)
-{
-  /* This is a potential spot to multiplex paralell processing,
-   * doing so, might cause a lot of tile overlap between
-   * processes if were not careful (wouldn't neccesarily be totally
-   * bad if that happens though.
-   */
-  GeglBuffer *buffer;
-
-  /*g_print ("%i %i %i %i %i\n", tid, roi->x, roi->y, roi->width, roi->height);*/
+                     const GeglRectangle *roi)
+{
+  GeglEvalManager *eval_manager = gegl_node_get_eval_manager (self);
 
   if (roi)
     {
-      self->priv->eval_manager[tid]->roi = *roi;
+      return gegl_eval_manager_apply (eval_manager, roi);
     }
   else
     {
-      self->priv->eval_manager[tid]->roi = gegl_node_get_bounding_box (self);
+      GeglRectangle node_bbox = gegl_node_get_bounding_box (self);
+      return gegl_eval_manager_apply (eval_manager, &node_bbox);
     }
-  buffer = gegl_eval_manager_apply (self->priv->eval_manager[tid]);
-  return buffer;
 }
 
-
-typedef struct ThreadData
+void
+gegl_node_blit_buffer (GeglNode            *self,
+                       GeglBuffer          *buffer,
+                       const GeglRectangle *roi)
 {
-  GeglNode      *node;
-  gint           tid;
-  GeglRectangle  roi;
-  const gchar   *pad;
-
-  const Babl          *format;
-  gpointer             destination_buf;
-  gint                 rowstride;
-  GeglBlitFlags        flags;
-} ThreadData;
-
-static GThreadPool *pool            = NULL;
-static GMutex       mutex           = { 0, };
-static GCond        cond            = { 0, };
-static gint         remaining_tasks = 0;
+  GeglEvalManager *eval_manager;
+  GeglBuffer      *result;
+  GeglRectangle    request;
 
-static void spawnrender (gpointer data,
-                         gpointer foo)
-{
-  ThreadData *td = data;
-  GeglBuffer * buffer;
-  buffer = gegl_node_apply_roi (td->node, td->pad, &td->roi, td->tid);
+  eval_manager = gegl_node_get_eval_manager (self);
 
-  if ((buffer ) && td->destination_buf)
-    {
-      gegl_buffer_get (buffer, &td->roi, 1.0, td->format, td->destination_buf, td->rowstride,
-                       GEGL_ABYSS_NONE);
-    }
+  if (roi)
+    request = *roi;
+  else if (buffer)
+    request = *gegl_buffer_get_extent (buffer);
+  else
+    request = gegl_node_get_bounding_box (self);
 
-  /* and unrefing to ultimately clean it off from the graph */
-  if (buffer)
-    g_object_unref (buffer);
+  result = gegl_eval_manager_apply (eval_manager, &request);
 
-  g_mutex_lock (&mutex);
-  remaining_tasks --;
-  if (remaining_tasks == 0)
+  if (result)
     {
-      /* we were the last task, we're not busy rendering any more */
-      g_cond_signal (&cond);
+      if (buffer)
+        gegl_buffer_copy (result, &request, buffer, NULL);
+      g_object_unref (result);
     }
-  g_mutex_unlock (&mutex);
 }
 
-
 void
 gegl_node_blit (GeglNode            *self,
                 gdouble              scale,
@@ -959,124 +918,30 @@ gegl_node_blit (GeglNode            *self,
                 gint                 rowstride,
                 GeglBlitFlags        flags)
 {
-  gint threads;
   g_return_if_fail (GEGL_IS_NODE (self));
   g_return_if_fail (roi != NULL);
 
-  threads = gegl_config ()->threads;
-  if (threads > GEGL_MAX_THREADS)
-    threads = 1;
-
-  if (pool == NULL)
-    {
-      pool = g_thread_pool_new (spawnrender, NULL, threads, TRUE, NULL);
-      g_mutex_init (&mutex);
-      g_cond_init (&cond);
-    }
-
   if (flags == GEGL_BLIT_DEFAULT)
-#if 1  /* multi threaded version */
-    {
-      ThreadData data[GEGL_MAX_THREADS];
-      gint i;
-
-      /* Subdivide along the largest of width/height, this should be further
-       * extended similar to the subdivizion done in GeglProcessor, to get as
-       * square as possible subregions.
-       */
-      gboolean horizontal = roi->width > roi->height;
-
-      gint rowskip = 0;
-
-      if (!format)
-        format = babl_format ("RGBA float"); /* XXX: This probably duplicates
-                                                another hardcoded format, they
-                                                should be turned into a
-                                                constant. */
-      if (horizontal)
-        rowskip = (roi->width/threads) * babl_format_get_bytes_per_pixel (format);
-
-      if (rowstride == GEGL_AUTO_ROWSTRIDE)
-        rowstride = roi->width * babl_format_get_bytes_per_pixel (format);
-
-      data[0].node = self;
-      data[0].pad = "output";
-      data[0].format = format;
-      data[0].destination_buf = destination_buf;
-      data[0].rowstride = rowstride;
-      data[0].flags = flags;
-
-      for (i=0;i<threads;i++)
-        {
-          data[i] = data[0];
-          data[i].roi = *roi;
-          gegl_node_ensure_eval_manager (self, "output", i);
-          if (horizontal)
-            {
-              data[i].roi.width = roi->width / threads;
-              data[i].roi.x = roi->x + roi->width/threads * i;
-            }
-          else
-            {
-              data[i].roi.height = roi->height / threads;
-              data[i].roi.y = roi->y + roi->height/threads * i;
-            }
-
-          data[i].tid = i;
-          if (horizontal)
-            data[i].destination_buf = ((gchar*)destination_buf + rowskip * i);
-          else
-            data[i].destination_buf = ((gchar*)destination_buf + rowstride * (roi->height/threads) * i);
-        }
-      if (horizontal)
-        data[threads-1].roi.width = roi->width - (roi->width / threads)*(threads-1);
-      else
-        data[threads-1].roi.height = roi->height - (roi->height / threads)*(threads-1);
-
-      remaining_tasks+=threads;
-
-      if (threads==1)
-        {
-          spawnrender (&data[0], NULL);
-        }
-      else
-        {
-          for (i=0; i<threads-1; i++)
-            g_thread_pool_push (pool, &data[i], NULL);
-          spawnrender (&data[threads-1], NULL);
-
-          g_mutex_lock (&mutex);
-          while (remaining_tasks!=0)
-            g_cond_wait (&cond, &mutex);
-          g_mutex_unlock (&mutex);
-        }
-    }
-#else /* thread free version, could be removed, left behind in case it
-         is needed for debugging
-       */
     {
       GeglBuffer *buffer;
 
-      gegl_node_ensure_eval_manager (self, "output", 0);
-      buffer = gegl_node_apply_roi (self, "output", roi, 0);
+      buffer = gegl_node_apply_roi (self, roi);
       if (buffer && destination_buf)
         {
           if (destination_buf)
             {
-              gegl_buffer_get (buffer, 1.0, roi, format, destination_buf, rowstride, GEGL_REPEAT_MODE_ZERO);
+              gegl_buffer_get (buffer, roi, 1.0, format, destination_buf, rowstride, GEGL_ABYSS_NONE);
             }
 
           if (scale != 1.0)
             {
-              g_warning ("Scale %f!=1.0 in blit without cache NYI", scale);
+              g_warning ("scale %f=1.0 in blit without cache", scale);
             }
         }
 
-      /* and unrefing to ultimately clean it off from the graph */
       if (buffer)
         g_object_unref (buffer);
     }
-#endif
   else
     if ((flags & GEGL_BLIT_CACHE))
     {
@@ -1633,20 +1498,6 @@ gegl_node_get_operation (const GeglNode *node)
   return GEGL_OPERATION_GET_CLASS (node->operation)->name;
 }
 
-void
-gegl_node_set_need_rect (GeglNode *node,
-                         gpointer  context_id,
-                         const GeglRectangle *rect)
-{
-  GeglOperationContext *context;
-
-  g_return_if_fail (GEGL_IS_NODE (node));
-  g_return_if_fail (context_id != NULL);
-
-  context = gegl_node_get_context (node, context_id);
-  gegl_operation_context_set_need_rect (context, rect);
-}
-
 const gchar *
 gegl_node_get_debug_name (GeglNode *node)
 {
@@ -1721,55 +1572,24 @@ gegl_node_get_producer (GeglNode *node,
 }
 
 GeglRectangle
-gegl_node_get_bounding_box (GeglNode *root)
+gegl_node_get_bounding_box (GeglNode *self)
 {
-  GeglRectangle dummy = { 0, 0, 0, 0 };
-  GeglVisitor  *prepare_visitor;
-  GeglVisitor  *have_visitor;
-  GeglVisitor  *finish_visitor;
-
-  guchar       *id;
-  gint          i;
-
-  GeglPad      *pad;
-
-  if (!root)
-    return dummy;
-
-  if (root->valid_have_rect)
-    return root->have_rect;
-
-  pad = gegl_node_get_pad (root, "output");
-  if (pad && pad->node != root)
+  if (!self->valid_have_rect)
     {
-      root = pad->node;
-    }
-  if (!pad || !root)
-    return dummy;
-  g_object_ref (root);
-
-  id = g_malloc (1);
+      /* We need to construct an evaluation here because we need
+       * to be sure we use the same logic as the eval manager to
+       * calculate our bounds. We set our rect explicitly because
+       * if we're a meta-op the eval manager may not actually
+       * visit us in prepare.
+       */
 
-  for (i = 0; i < 2; i++)
-    {
-      prepare_visitor = g_object_new (GEGL_TYPE_PREPARE_VISITOR, "id", id, NULL);
-      gegl_visitor_dfs_traverse (prepare_visitor, GEGL_VISITABLE (root));
-      g_object_unref (prepare_visitor);
+      GeglEvalManager *eval = gegl_eval_manager_new (self, "output");
+      self->have_rect = gegl_eval_manager_get_bounding_box (eval);
+      self->valid_have_rect = TRUE;
+      g_object_unref (eval);
     }
 
-  have_visitor = g_object_new (GEGL_TYPE_HAVE_VISITOR, "id", id, NULL);
-  gegl_visitor_dfs_traverse (have_visitor, GEGL_VISITABLE (root));
-  g_object_unref (have_visitor);
-
-  finish_visitor = g_object_new (GEGL_TYPE_FINISH_VISITOR, "id", id, NULL);
-  gegl_visitor_dfs_traverse (finish_visitor, GEGL_VISITABLE (root));
-  g_object_unref (finish_visitor);
-
-  g_object_unref (root);
-  g_free (id);
-
-  root->valid_have_rect = TRUE;
-  return root->have_rect;
+  return self->have_rect;
 }
 
 #if 1
@@ -1807,7 +1627,7 @@ gegl_node_process (GeglNode *self)
 
   input   = gegl_node_get_producer (self, "input", NULL);
   defined = gegl_node_get_bounding_box (input);
-  buffer  = gegl_node_apply_roi (input, "output", &defined, 3);
+  buffer  = gegl_node_apply_roi (input, &defined);
 
   g_assert (GEGL_IS_BUFFER (buffer));
   context = gegl_node_add_context (self, &defined);
@@ -1827,71 +1647,6 @@ gegl_node_process (GeglNode *self)
 }
 #endif
 
-GeglOperationContext *
-gegl_node_get_context (GeglNode *self,
-                       gpointer  context_id)
-{
-  GeglOperationContext *context = NULL;
-  //g_mutex_lock (&self->mutex);
-
-  context = g_hash_table_lookup (self->priv->contexts, context_id);
-  //g_mutex_unlock (&self->mutex);
-  return context;
-}
-
-void
-gegl_node_remove_context (GeglNode *self,
-                          gpointer  context_id)
-{
-  GeglOperationContext *context;
-
-  g_return_if_fail (GEGL_IS_NODE (self));
-  g_return_if_fail (context_id != NULL);
-
-  context = gegl_node_get_context (self, context_id);
-  g_mutex_lock (&self->mutex);
-  if (!context)
-    {
-      g_warning ("didn't find context %p for %s",
-                 context_id, gegl_node_get_debug_name (self));
-      g_mutex_unlock (&self->mutex);
-      return;
-    }
-  g_hash_table_remove (self->priv->contexts, context_id);
-  gegl_operation_context_destroy (context);
-  g_mutex_unlock (&self->mutex);
-}
-
-/* Creates, sets up and returns a new context for the node, or just returns it
- * if it is already set up. Also adds it to an internal hash table.
- */
-GeglOperationContext *
-gegl_node_add_context (GeglNode *self,
-                       gpointer  context_id)
-{
-  GeglOperationContext *context = NULL;
-
-  g_return_val_if_fail (GEGL_IS_NODE (self), NULL);
-  g_return_val_if_fail (context_id != NULL, NULL);
-
-  g_mutex_lock (&self->mutex);
-  context = g_hash_table_lookup (self->priv->contexts, context_id);
-
-  if (context)
-    {
-      /* silently ignore, since multiple traversals of prepare are done
-       * to saturate the graph */
-      g_mutex_unlock (&self->mutex);
-      return context;
-    }
-
-  context             = gegl_operation_context_new ();
-  context->operation  = self->operation;
-  g_hash_table_insert (self->priv->contexts, context_id, context);
-  g_mutex_unlock (&self->mutex);
-  return context;
-}
-
 GeglNode *
 gegl_node_detect (GeglNode *root,
                   gint      x,
@@ -2082,9 +1837,8 @@ gegl_node_get_cache (GeglNode *node)
                                   "format", format,
                                   NULL);
 
-      /* gegl_node_get_bounding_box must be called at least once to compute
-         an initial have_rect for the extent of the cache */
       gegl_node_get_bounding_box (node);
+      gegl_buffer_set_extent (GEGL_BUFFER (node->cache), &node->have_rect);
 
       g_signal_connect (G_OBJECT (node->cache), "computed",
                         (GCallback) gegl_node_computed_event,
@@ -2274,14 +2028,12 @@ graph_source_invalidated (GeglNode            *source,
 
 
 static GeglNode *
-gegl_node_get_pad_proxy (GeglNode    *graph,
+gegl_node_get_pad_proxy (GeglNode    *node,
                          const gchar *name,
                          gboolean     is_graph_input)
 {
-  GeglNode *node = graph;
-  GeglPad  *pad;
+  GeglPad *pad = gegl_node_get_pad (node, name);
 
-  pad = gegl_node_get_pad (node, name);
   if (!pad)
     {
       GeglNode *nop      = NULL;
@@ -2293,7 +2045,7 @@ gegl_node_get_pad_proxy (GeglNode    *graph,
       nop_pad  = gegl_node_get_pad (nop, is_graph_input ? "input" : "output");
       g_free (nop_name);
 
-      gegl_node_add_child (graph, nop);
+      gegl_node_add_child (node, nop);
       g_object_unref (nop); /* our reference is made by the
                                gegl_node_add_child call */
 
@@ -2305,12 +2057,12 @@ gegl_node_get_pad_proxy (GeglNode    *graph,
         gegl_node_add_pad (node, new_pad);
       }
 
-      g_object_set_data (G_OBJECT (nop), "graph", graph);
+      g_object_set_data (G_OBJECT (nop), "graph", node);
 
       if (!is_graph_input)
         {
           g_signal_connect (G_OBJECT (nop), "invalidated",
-                            G_CALLBACK (graph_source_invalidated), graph);
+                            G_CALLBACK (graph_source_invalidated), node);
         }
       return nop;
     }
diff --git a/gegl/graph/gegl-node.h b/gegl/graph/gegl-node.h
index 76c9f42..e69c6a1 100644
--- a/gegl/graph/gegl-node.h
+++ b/gegl/graph/gegl-node.h
@@ -96,6 +96,10 @@ void          gegl_node_blit                (GeglNode            *node,
                                              gint                 rowstride,
                                              GeglBlitFlags        flags);
 
+void          gegl_node_blit_buffer         (GeglNode            *self,
+                                             GeglBuffer          *buffer,
+                                             const GeglRectangle *roi);
+
 void          gegl_node_process             (GeglNode      *self);
 void          gegl_node_link                (GeglNode      *source,
                                              GeglNode      *sink);
@@ -132,13 +136,6 @@ GeglNode    * gegl_node_get_parent          (GeglNode      *self);
 
 GType         gegl_node_get_type            (void) G_GNUC_CONST;
 
-GeglOperationContext *gegl_node_get_context      (GeglNode      *self,
-                                             gpointer       context_id);
-void             gegl_node_remove_context   (GeglNode      *self,
-                                             gpointer       context_id);
-GeglOperationContext *gegl_node_add_context      (GeglNode      *self,
-                                             gpointer       context_id);
-
 void          gegl_node_add_pad             (GeglNode      *self,
                                              GeglPad       *pad);
 void          gegl_node_remove_pad          (GeglNode      *self,
diff --git a/gegl/graph/gegl-visitor.c b/gegl/graph/gegl-visitor.c
index 0af0500..2e02aa4 100644
--- a/gegl/graph/gegl-visitor.c
+++ b/gegl/graph/gegl-visitor.c
@@ -40,11 +40,9 @@ struct _GeglVisitInfo
 
 enum
 {
-  PROP_0,
-  PROP_ID
+  PROP_0
 };
 
-
 static void           gegl_visitor_class_init  (GeglVisitorClass *klass);
 static void           gegl_visitor_init        (GeglVisitor      *self);
 static void           finalize                 (GObject          *gobject);
@@ -78,15 +76,6 @@ static void           visit_pad                (GeglVisitor      *self,
                                                 GeglPad          *pad);
 static void           visit_node               (GeglVisitor      *self,
                                                 GeglNode         *node);
-static void           set_property             (GObject          *gobject,
-                                                guint             prop_id,
-                                                const GValue     *value,
-                                                GParamSpec       *pspec);
-static void           get_property             (GObject          *gobject,
-                                                guint             prop_id,
-                                                GValue           *value,
-                                                GParamSpec       *pspec);
-
 
 
 G_DEFINE_TYPE (GeglVisitor, gegl_visitor, G_TYPE_OBJECT)
@@ -99,66 +88,13 @@ gegl_visitor_class_init (GeglVisitorClass *klass)
 
   gobject_class->finalize = finalize;
 
-  klass->visit_pad            = visit_pad;
-  klass->visit_node           = visit_node;
-  gobject_class->set_property = set_property;
-  gobject_class->get_property = get_property;
-
-  g_object_class_install_property (gobject_class, PROP_ID,
-                                   g_param_spec_pointer ("id",
-                                                         "evaluation-id",
-                                                         "The identifier for the evaluation context",
-                                                         G_PARAM_CONSTRUCT |
-                                                         G_PARAM_READWRITE));
+  klass->visit_pad  = visit_pad;
+  klass->visit_node = visit_node;
 }
 
-
-static void
-set_property (GObject      *gobject,
-              guint         property_id,
-              const GValue *value,
-              GParamSpec   *pspec)
-{
-  GeglVisitor *self = GEGL_VISITOR (gobject);
-
-  switch (property_id)
-    {
-      case PROP_ID:
-        self->context_id = g_value_get_pointer (value);
-        break;
-
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
-        break;
-    }
-}
-
-static void
-get_property (GObject    *gobject,
-              guint       property_id,
-              GValue     *value,
-              GParamSpec *pspec)
-{
-  GeglVisitor *self = GEGL_VISITOR (gobject);
-
-  switch (property_id)
-    {
-      case PROP_ID:
-        g_value_set_pointer (value, self->context_id);
-        break;
-
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
-        break;
-    }
-}
-
-
-
 static void
 gegl_visitor_init (GeglVisitor *self)
 {
-  self->visits_list = NULL;
   self->hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                                       NULL,
                                       (GDestroyNotify) visit_info_destroy);
@@ -169,7 +105,6 @@ finalize (GObject *gobject)
 {
   GeglVisitor *self = GEGL_VISITOR (gobject);
 
-  g_slist_free (self->visits_list);
   g_hash_table_destroy (self->hash);
 
   G_OBJECT_CLASS (gegl_visitor_parent_class)->finalize (gobject);
@@ -185,11 +120,6 @@ lookup (GeglVisitor   *self,
 /* resets the object's data (list of visits and visitable statuses) */
 void gegl_visitor_reset (GeglVisitor   *self)
 {
-  if (self->visits_list)
-    {
-      g_slist_free (self->visits_list);
-      self->visits_list = NULL;
-    }
   g_hash_table_remove_all (self->hash);
 }
 
@@ -280,22 +210,6 @@ set_shared_count (GeglVisitor   *self,
   visit_info->shared_count = shared_count;
 }
 
-/**
- * gegl_visitor_get_visits_list
- * @self: a #GeglVisitor.
- *
- * Gets a list of the visitables the visitor has visited so far.
- *
- * Returns: A list of the visitables visited by this visitor.
- **/
-GSList *
-gegl_visitor_get_visits_list (GeglVisitor *self)
-{
-  g_return_val_if_fail (GEGL_IS_VISITOR (self), NULL);
-
-  return self->visits_list;
-}
-
 static void
 visit_info_destroy (GeglVisitInfo *visit_info)
 {
@@ -501,7 +415,6 @@ static void
 visit_pad (GeglVisitor *self,
            GeglPad     *pad)
 {
-  self->visits_list = g_slist_prepend (self->visits_list, pad);
 }
 
 /* should be called by extending classes when their visit_node function
@@ -524,15 +437,4 @@ static void
 visit_node (GeglVisitor *self,
             GeglNode    *node)
 {
-#if 0
-#if ENABLE_MT
-  g_mutex_lock (node->mutex);
-#endif
-#endif
-  self->visits_list = g_slist_prepend (self->visits_list, node);
-#if 0
-#if ENABLE_MT
-  g_mutex_unlock (node->mutex);
-#endif
-#endif
 }
diff --git a/gegl/graph/gegl-visitor.h b/gegl/graph/gegl-visitor.h
index 9a40585..db9c8e5 100644
--- a/gegl/graph/gegl-visitor.h
+++ b/gegl/graph/gegl-visitor.h
@@ -35,9 +35,7 @@ typedef struct _GeglVisitorClass GeglVisitorClass;
 struct _GeglVisitor
 {
   GObject     parent_instance;
-  gpointer    context_id;
 
-  GSList     *visits_list;
   GHashTable *hash;
 };
 
@@ -54,7 +52,6 @@ struct _GeglVisitorClass
 
 GType    gegl_visitor_get_type         (void) G_GNUC_CONST;
 
-GSList * gegl_visitor_get_visits_list (GeglVisitor   *self);
 void     gegl_visitor_reset           (GeglVisitor   *self);
 void     gegl_visitor_visit_visitable (GeglVisitor   *self,
                                        GeglVisitable *visitable);
diff --git a/gegl/operation/gegl-operation-context.c b/gegl/operation/gegl-operation-context.c
index bb2df99..eaa7fd7 100644
--- a/gegl/operation/gegl-operation-context.c
+++ b/gegl/operation/gegl-operation-context.c
@@ -195,21 +195,29 @@ gegl_operation_context_add_value (GeglOperationContext *self,
   return &property->value;
 }
 
-GeglOperationContext *gegl_operation_context_new (void)
+GeglOperationContext *
+gegl_operation_context_new (GeglOperation *operation)
 {
   GeglOperationContext *self = g_slice_new0 (GeglOperationContext);
+  self->operation = operation;
   return self;
 }
 
-void gegl_operation_context_destroy (GeglOperationContext *self)
+void
+gegl_operation_context_purge (GeglOperationContext *self)
 {
-
   while (self->property)
     {
       Property *property = self->property->data;
       self->property = g_slist_remove (self->property, property);
       property_destroy (property);
     }
+}
+
+void
+gegl_operation_context_destroy (GeglOperationContext *self)
+{
+  gegl_operation_context_purge (self);
   g_slice_free (GeglOperationContext, self);
 }
 
diff --git a/gegl/operation/gegl-operation-context.h b/gegl/operation/gegl-operation-context.h
index 4561c58..a6340fd 100644
--- a/gegl/operation/gegl-operation-context.h
+++ b/gegl/operation/gegl-operation-context.h
@@ -83,6 +83,8 @@ void            gegl_operation_context_get_property    (GeglOperationContext *se
                                                         const gchar          *name,
                                                         GValue               *value);
 
+void            gegl_operation_context_purge           (GeglOperationContext *self);
+
 /* the rest of these functions are for internal use only */
 
 void            gegl_operation_context_remove_property (GeglOperationContext *self,
@@ -96,7 +98,7 @@ void            gegl_operation_context_set_result_rect (GeglOperationContext *no
 
 gint            gegl_operation_context_get_level (GeglOperationContext *ctxt);
 
-GeglOperationContext *gegl_operation_context_new (void);
+GeglOperationContext *gegl_operation_context_new (GeglOperation *operation);
 void gegl_operation_context_destroy (GeglOperationContext *opcontext);
 
 G_END_DECLS
diff --git a/gegl/operation/gegl-operation.c b/gegl/operation/gegl-operation.c
index 5d22cf5..d04c2e8 100644
--- a/gegl/operation/gegl-operation.c
+++ b/gegl/operation/gegl-operation.c
@@ -162,6 +162,8 @@ gegl_operation_process (GeglOperation        *operation,
       return TRUE;
     }
 
+  g_return_val_if_fail (klass->process, FALSE);
+
   return klass->process (operation, context, output_pad, result, level);
 }
 
diff --git a/gegl/operation/gegl-operation.h b/gegl/operation/gegl-operation.h
index 51a60fa..b57f87e 100644
--- a/gegl/operation/gegl-operation.h
+++ b/gegl/operation/gegl-operation.h
@@ -263,8 +263,6 @@ gboolean gegl_operation_cl_set_kernel_args (GeglOperation *operation,
 
 /* internal utility functions used by gegl, these should not be used
  * externally */
-gboolean gegl_operation_calc_need_rects      (GeglOperation       *operation,
-                                              gpointer             context_id);
 gboolean gegl_can_do_inplace_processing      (GeglOperation       *operation,
                                               GeglBuffer          *input,
                                               const GeglRectangle *result);
diff --git a/gegl/operation/gegl-operations.c b/gegl/operation/gegl-operations.c
index 53da8ad..64a61b3 100644
--- a/gegl/operation/gegl-operations.c
+++ b/gegl/operation/gegl-operations.c
@@ -148,97 +148,6 @@ gegl_operation_gtype_cleanup (void)
   G_UNLOCK (gtype_hash);
 }
 
-/**
- * gegl_operation_set_need_rect:
- * @operation:
- * @context_id:
- * @input_pad_name:
- * @region:
- *
- * Calculates the need rect for the node feeding into @input_pad_name
- * for the given graph evaluation that is identified by
- * @context_id. The need rect of a node mathematically depends on the
- * ROI that is being evaluated and is the area that evaluation of the
- * ROI depends on for the node in question.
- **/
-static void
-gegl_operation_set_need_rect (GeglOperation       *operation,
-                              gpointer             context_id,
-                              const gchar         *input_pad_name,
-                              const GeglRectangle *region)
-{
-  GeglNode     *child;         /* the node which need rect we are affecting */
-  GeglRectangle child_need;    /* the need rect of the child */
-  GeglPad      *output_pad;
-
-  g_return_if_fail (GEGL_IS_OPERATION (operation));
-  g_return_if_fail (GEGL_IS_NODE (operation->node));
-  g_return_if_fail (input_pad_name != NULL);
-
-  {
-    GeglPad *input_pad = gegl_node_get_pad (operation->node, input_pad_name);
-    if (!input_pad)
-      return;
-    output_pad = gegl_pad_get_connected_to (input_pad);
-    if (!output_pad)
-      return;
-    child = gegl_pad_get_node (output_pad);
-    if (!child)
-      return;
-  }
-
-  {
-    GeglOperationContext *child_context = gegl_node_get_context (child, context_id);
-    gegl_rectangle_bounding_box (&child_need, &child_context->need_rect, region);
-    gegl_rectangle_intersect (&child_need, &child->have_rect, &child_need);
-
-      /* If we're cached */
-      if (child->cache)
-        {
-          GeglCache *cache = child->cache;
-          GeglRectangle valid_box;
-          gegl_region_get_clipbox (cache->valid_region, &valid_box);
-
-          if (child_need.width == 0  ||
-              child_need.height == 0 ||
-              gegl_region_rect_in (cache->valid_region, &child_need) == GEGL_OVERLAP_RECTANGLE_IN)
-            {
-              child_context->result_rect = child_need;
-              child_context->cached = TRUE;
-              child_need.width = 0;
-              child_need.height = 0;
-            }
-        }
-
-    gegl_node_set_need_rect (child, context_id, &child_need);
-  }
-}
-
-gboolean
-gegl_operation_calc_need_rects (GeglOperation *operation,
-                                gpointer       context_id)
-{
-  GSList          *input_pads;
-  GeglOperationContext *context;
-  GeglRectangle    request;
-
-  context = gegl_node_get_context (operation->node, context_id);
-  request = context->need_rect;
-
-  /* For each input, do get_required_for_output() then use
-   * gegl_operation_set_need_rect()
-   */
-  for (input_pads = operation->node->input_pads;input_pads;input_pads=input_pads->next)
-    {
-      const gchar *pad_name = gegl_pad_get_name (input_pads->data);
-      GeglRectangle rect;
-      rect = gegl_operation_get_required_for_output (operation, pad_name, &request);
-
-      gegl_operation_set_need_rect (operation, context_id, pad_name, &rect);
-    }
-  return TRUE;
-}
-
 gboolean gegl_can_do_inplace_processing (GeglOperation       *operation,
                                          GeglBuffer          *input,
                                          const GeglRectangle *result)
diff --git a/gegl/process/Makefile.am b/gegl/process/Makefile.am
index 8825366..e186dfd 100644
--- a/gegl/process/Makefile.am
+++ b/gegl/process/Makefile.am
@@ -13,26 +13,21 @@ AM_CFLAGS = $(DEP_CFLAGS) $(BABL_CFLAGS)
 
 noinst_LTLIBRARIES = libprocess.la
 
-#libprocess_publicdir = $(includedir)/gegl-$(GEGL_API_VERSION)/gegl/process
+#libprocess_publicdir = $(includedir)/gegl-$(GEGL_API_VERSION)/process
 #libprocess_public_HEADERS = #
 
 libprocess_la_SOURCES = \
-       gegl-need-visitor.c             \
-       gegl-debug-rect-visitor.c       \
        gegl-eval-manager.c             \
-       gegl-eval-visitor.c             \
-       gegl-finish-visitor.c           \
-       gegl-have-visitor.c             \
-       gegl-prepare-visitor.c          \
+       gegl-graph-traversal.c          \
+       gegl-graph-traversal-debug.c    \
+       gegl-list-visitor.c             \
        gegl-processor.c                \
        \
-       gegl-need-visitor.h             \
-       gegl-debug-rect-visitor.h       \
        gegl-eval-manager.h             \
-       gegl-eval-visitor.h             \
-       gegl-finish-visitor.h           \
-       gegl-have-visitor.h             \
-       gegl-prepare-visitor.h          \
+       gegl-graph-debug.h              \
+       gegl-graph-traversal.h          \
+       gegl-graph-traversal-private.h  \
+       gegl-list-visitor.h             \
        gegl-processor.h
 
 #libprocess_la_SOURCES = $(lib_process_sources) $(libprocess_public_HEADERS)
diff --git a/gegl/process/gegl-eval-manager.c b/gegl/process/gegl-eval-manager.c
index bf9d82a..8fa799c 100644
--- a/gegl/process/gegl-eval-manager.c
+++ b/gegl/process/gegl-eval-manager.c
@@ -15,6 +15,7 @@
  *
  * Copyright 2003 Calvin Williamson
  *           2006 Øyvind Kolås
+ *           2013 Daniel Sabo
  */
 
 #include "config.h"
@@ -24,19 +25,11 @@
 #include "gegl.h"
 #include "gegl-types-internal.h"
 #include "gegl-eval-manager.h"
-#include "gegl-eval-visitor.h"
-#include "gegl-debug-rect-visitor.h"
-#include "gegl-need-visitor.h"
-#include "gegl-have-visitor.h"
 #include "gegl-instrument.h"
+
 #include "graph/gegl-node.h"
-#include "gegl-prepare-visitor.h"
-#include "gegl-finish-visitor.h"
-#include "graph/gegl-pad.h"
-#include "graph/gegl-visitable.h"
-#include "operation/gegl-operation.h"
-#include <stdlib.h>
 
+#include "process/gegl-graph-traversal.h"
 
 static void gegl_eval_manager_class_init (GeglEvalManagerClass *klass);
 static void gegl_eval_manager_init (GeglEvalManager *self);
@@ -55,44 +48,29 @@ gegl_eval_manager_class_init (GeglEvalManagerClass *klass)
 static void
 gegl_eval_manager_init (GeglEvalManager *self)
 {
-  GeglRectangle roi = { 0, 0, -1, -1 };
-  gpointer     context_id = self;
-
-  self->roi = roi;
-  self->prepare_visitor = g_object_new (GEGL_TYPE_PREPARE_VISITOR, "id", context_id, NULL);
-  self->have_visitor = g_object_new (GEGL_TYPE_HAVE_VISITOR, "id", context_id, NULL);
-  self->eval_visitor = g_object_new (GEGL_TYPE_EVAL_VISITOR, "id", context_id, NULL);
-  self->need_visitor = g_object_new (GEGL_TYPE_NEED_VISITOR, "id", context_id, NULL);
-  self->finish_visitor = g_object_new (GEGL_TYPE_FINISH_VISITOR, "id", context_id, NULL);
-  self->state = UNINITIALIZED;
+  self->state     = INVALID;
+  self->pad_name  = NULL;
+  self->traversal = NULL;
 }
 
 static void
 gegl_eval_manager_finalize (GObject *self_object)
 {
   GeglEvalManager *self = GEGL_EVAL_MANAGER (self_object);
-#if 0
-  GeglNode    *root;
-  GeglPad     *pad;
-  root=self->node;
-  pad = gegl_node_get_pad (root, self->pad_name);
-  /* Use the redirect output NOP of a graph instead of a graph if a traversal
-   * is attempted directly on a graph */
-  if (pad && pad->node != self->node)
-    root = pad->node;
-  else
-    root = self->node;
 
-  gegl_visitor_reset (self->finish_visitor);
-  gegl_visitor_dfs_traverse (self->finish_visitor, GEGL_VISITABLE (root));
-#endif
+  if (self->pad_name)
+    {
+      g_free (self->pad_name);
+      self->pad_name = NULL;
+    }
+
+  if (self->traversal)
+    {
+      gegl_graph_free (self->traversal);
+      self->traversal = NULL;
+    }
 
-  g_object_unref (self->prepare_visitor);
-  g_object_unref (self->have_visitor);
-  g_object_unref (self->eval_visitor);
-  g_object_unref (self->need_visitor);
-  g_object_unref (self->finish_visitor);
-  g_free (self->pad_name);
+  g_signal_handlers_disconnect_by_data (self->node, self);
 
   G_OBJECT_CLASS (gegl_eval_manager_parent_class)->finalize (self_object);
 }
@@ -103,146 +81,54 @@ gegl_eval_manager_change_notification (GObject             *gobject,
                                        gpointer             user_data)
 {
   GeglEvalManager *manager = GEGL_EVAL_MANAGER (user_data);
-
-  if (manager->node != NULL)
-    {
-      gpointer              context_id = manager;
-      GeglOperationContext *context    = gegl_node_get_context (manager->node,
-                                                                context_id);
-      if (context != NULL)
-        {
-          /* If the node is changed we can't use the cache any longer */
-          context->cached = FALSE;
-        }
-    }
-
-  if (manager->state != UNINITIALIZED)
-    {
-      manager->state = NEED_REDO_PREPARE_AND_HAVE_RECT_TRAVERSAL;
-    }
+  manager->state = INVALID;
 
   return FALSE;
 }
 
-
-GeglBuffer *
-gegl_eval_manager_apply (GeglEvalManager *self)
+void
+gegl_eval_manager_prepare (GeglEvalManager *self)
 {
-  GeglNode    *root;
-  GeglBuffer  *object;
-  GeglPad     *pad;
-  glong        time       = gegl_ticks ();
-  gpointer     context_id = self;
-
-  g_assert (GEGL_IS_EVAL_MANAGER (self));
-
-  gegl_instrument ("gegl", "process", 0);
+  g_return_if_fail (GEGL_IS_EVAL_MANAGER (self));
+  g_return_if_fail (GEGL_IS_NODE (self->node));
 
-  root=self->node;
-  pad = gegl_node_get_pad (root, self->pad_name);
-  /* Use the redirect output NOP of a graph instead of a graph if a traversal
-   * is attempted directly on a graph */
-  if (pad && pad->node != self->node)
-    root = pad->node;
-  else
-    root = self->node;
-  g_assert (root);
-
-  g_object_ref (root);
-
-  /* do the necessary set-up work (all using depth first traversal) */
-  switch (self->state)
+  if (self->state != READY)
     {
-      case UNINITIALIZED:
-        /* Set up the node's context and "needed rectangle"*/
-        gegl_visitor_reset (self->prepare_visitor);
-        gegl_visitor_dfs_traverse (self->prepare_visitor, GEGL_VISITABLE (root));
-        /* No idea why there is a second call */
-        gegl_visitor_reset (self->prepare_visitor);
-        gegl_visitor_dfs_traverse (self->prepare_visitor, GEGL_VISITABLE (root));
-      case NEED_REDO_PREPARE_AND_HAVE_RECT_TRAVERSAL:
-        /* sets up the node's rect (bounding box) */
-        gegl_visitor_reset (self->have_visitor);
-        gegl_visitor_dfs_traverse (self->have_visitor, GEGL_VISITABLE (root));
-      case NEED_CONTEXT_SETUP_TRAVERSAL:
-
-        gegl_visitor_reset (self->prepare_visitor);
-        gegl_visitor_dfs_traverse (self->prepare_visitor, GEGL_VISITABLE (root));
-        self->state = NEED_CONTEXT_SETUP_TRAVERSAL;
-     }
-
-  /* set up the root node */
-  if (self->roi.width == -1 &&
-      self->roi.height == -1)
-    {
-      self->roi = root->have_rect;
-    }
+      if (!self->traversal)
+        self->traversal = gegl_graph_build (self->node);
+      else
+        gegl_graph_rebuild (self->traversal, self->node);
 
-  gegl_node_set_need_rect (root, context_id, &self->roi);
+      gegl_graph_prepare (self->traversal);
 
-  /* set up the context's rectangle (breadth first traversal) */
-  gegl_visitor_reset (self->need_visitor);
-
-  /* should the need rect be moved into the context, making this
-   * part of gegl re-entrable without locking?.. or does that
-   * hamper other useful API that depends on the need_rect to be
-   * in the nodes?
-   */
-  gegl_visitor_bfs_traverse (self->need_visitor, GEGL_VISITABLE (root));
+      self->state = READY;
+    }
+}
 
-#if 0
-  if (g_getenv ("GEGL_DEBUG_RECTS") != NULL)
-    {
-      GeglVisitor *debug_rect_visitor;
+GeglRectangle
+gegl_eval_manager_get_bounding_box (GeglEvalManager     *self)
+{
+  gegl_eval_manager_prepare (self);
+  return gegl_graph_get_bounding_box (self->traversal);
+}
 
-      g_warning ("---------------------");
-      debug_rect_visitor = g_object_new (GEGL_TYPE_DEBUG_RECT_VISITOR, "id", context_id, NULL);
-      gegl_visitor_dfs_traverse (debug_rect_visitor, GEGL_VISITABLE (root));
-      g_object_unref (debug_rect_visitor);
-    }
-#endif
+GeglBuffer *
+gegl_eval_manager_apply (GeglEvalManager     *self,
+                         const GeglRectangle *roi)
+{
+  GeglBuffer  *object;
 
-  /* now let's do the real work */
-  gegl_visitor_reset (self->eval_visitor);
-  if (pad)
-    {
-      gegl_visitor_dfs_traverse (self->eval_visitor, GEGL_VISITABLE (pad));
-    }
-  else
-    { /* pull on the input of our sink if no pad of the given pad-name
-         was available, we take this as an indication that we're in fact
-         doing processing on a sink (and the ROI inidcates the data to
-         be written, note that GEGL might subdivide this roi
-         in its processing.
-       */
-      GeglPad *pad = gegl_node_get_pad (root, "input");
-      gegl_visitor_dfs_traverse (self->eval_visitor, GEGL_VISITABLE (pad));
-    }
+  g_return_val_if_fail (GEGL_IS_EVAL_MANAGER (self), NULL);
+  g_return_val_if_fail (GEGL_IS_NODE (self->node), NULL);
 
-  if (pad)
-    {
-      /* extract returned object before running finish visitor */
-      GValue value = { 0, };
-      g_value_init (&value, G_TYPE_OBJECT);
-      gegl_operation_context_get_property (gegl_node_get_context (root, context_id),
-                                      "output", &value);
-      object = g_value_get_object (&value);
-      g_object_ref (object);/* salvage buffer from finalization */
-      g_value_unset (&value);
-    }
+  if (roi->width <= 0 || roi->height <= 0)
+    return NULL;
 
-  /* do the clean up */
-  gegl_visitor_reset (self->finish_visitor);
-  gegl_visitor_dfs_traverse (self->finish_visitor, GEGL_VISITABLE (root));
+  gegl_eval_manager_prepare (self);
 
-  g_object_unref (root);
-  time = gegl_ticks () - time;
-  gegl_instrument ("gegl", "process", time);
+  gegl_graph_prepare_request (self->traversal, roi);
+  object = gegl_graph_process (self->traversal);
 
-  if (!pad || !G_IS_OBJECT (object))
-    {
-      return NULL;
-    }
   return object;
 }
 
@@ -250,14 +136,19 @@ GeglEvalManager * gegl_eval_manager_new     (GeglNode    *node,
                                              const gchar *pad_name)
 {
   GeglEvalManager *self = g_object_new (GEGL_TYPE_EVAL_MANAGER, NULL);
+
   g_assert (GEGL_IS_NODE (node));
+
+  /* FIXME: This should be a weakref */
   self->node = node;
+
   if (pad_name)
     self->pad_name = g_strdup (pad_name);
   else
     self->pad_name = g_strdup ("output");
-  /*g_signal_connect (G_OBJECT (self->node->operation), "notify", G_CALLBACK 
(gegl_eval_manager_change_notification), self);*/
-  g_signal_connect (G_OBJECT (self->node), "invalidated", G_CALLBACK 
(gegl_eval_manager_change_notification), self);
-  g_signal_connect (G_OBJECT (self->node), "notify", G_CALLBACK (gegl_eval_manager_change_notification), 
self);
+
+  g_signal_connect (G_OBJECT (self->node), "invalidated",
+                    G_CALLBACK (gegl_eval_manager_change_notification),
+                    self);
   return self;
 }
diff --git a/gegl/process/gegl-eval-manager.h b/gegl/process/gegl-eval-manager.h
index bd7263c..41a2585 100644
--- a/gegl/process/gegl-eval-manager.h
+++ b/gegl/process/gegl-eval-manager.h
@@ -14,6 +14,7 @@
  * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
  *
  * Copyright 2003 Calvin Williamson
+ * Copyright 2013 Daniel Sabo
  */
 
 #ifndef __GEGL_EVAL_MANAGER_H__
@@ -21,23 +22,15 @@
 
 #include "gegl-types-internal.h"
 #include "buffer/gegl-buffer-types.h"
+#include "process/gegl-graph-traversal.h"
 
 G_BEGIN_DECLS
 
 
 typedef enum
 {
-  UNINITIALIZED,
-
-  /* means we need to redo an extra prepare and have_rect
-   * traversal
-   */
-  NEED_REDO_PREPARE_AND_HAVE_RECT_TRAVERSAL,
-
-  /* means we need a prepare traversal to set up the contexts on the
-   * nodes
-   */
-  NEED_CONTEXT_SETUP_TRAVERSAL
+  INVALID,
+  READY
 } GeglEvalManagerStates;
 
 
@@ -56,19 +49,9 @@ struct _GeglEvalManager
   GObject    parent_instance;
   GeglNode  *node;
   gchar     *pad_name;
-  GeglRectangle roi;
 
-  /* whether we can fire off rendering requests straight
-   * away or we have to re-prepare etc of the graph
-   */
-  GeglEvalManagerStates state;
-
-  /* we keep these objects around, they are too expensive to throw away */
-  GeglVisitor *prepare_visitor;
-  GeglVisitor *need_visitor;
-  GeglVisitor *eval_visitor;
-  GeglVisitor *have_visitor;
-  GeglVisitor *finish_visitor;
+  GeglGraphTraversal    *traversal;
+  GeglEvalManagerStates  state;
 
 };
 
@@ -80,7 +63,11 @@ struct _GeglEvalManagerClass
 
 GType             gegl_eval_manager_get_type (void) G_GNUC_CONST;
 
-GeglBuffer *      gegl_eval_manager_apply    (GeglEvalManager *self);
+void              gegl_eval_manager_prepare  (GeglEvalManager     *self);
+GeglRectangle     gegl_eval_manager_get_bounding_box (GeglEvalManager     *self);
+
+GeglBuffer *      gegl_eval_manager_apply    (GeglEvalManager     *self,
+                                              const GeglRectangle *roi);
 GeglEvalManager * gegl_eval_manager_new      (GeglNode        *node,
                                               const gchar     *pad_name);
 
diff --git a/gegl/process/gegl-graph-debug.h b/gegl/process/gegl-graph-debug.h
new file mode 100644
index 0000000..5e2ae73
--- /dev/null
+++ b/gegl/process/gegl-graph-debug.h
@@ -0,0 +1,40 @@
+/* This file is part of GEGL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Daniel Sabo
+ */
+
+#ifndef __GEGL_GRAPH_DEBUG_H__
+#define __GEGL_GRAPH_DEBUG_H__
+
+/**
+ * gegl_graph_dump_outputs:
+ * @node: The final node of the graph
+ *
+ * Dump the bounds and format of each node in the graph to stdout.
+ */
+void gegl_graph_dump_outputs (GeglNode *node);
+
+/**
+ * gegl_graph_dump_request:
+ * @node: The final node of the graph
+ * @roi: The request rectangle
+ *
+ * Dump the region that will be rendered for each node to fulfill
+ * the request.
+ */
+void gegl_graph_dump_request (GeglNode *node, const GeglRectangle *roi);
+
+#endif /* __GEGL_GRAPH_DEBUG_H__ */
diff --git a/gegl/process/gegl-graph-traversal-debug.c b/gegl/process/gegl-graph-traversal-debug.c
new file mode 100644
index 0000000..51094bf
--- /dev/null
+++ b/gegl/process/gegl-graph-traversal-debug.c
@@ -0,0 +1,95 @@
+/* This file is part of GEGL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Daniel Sabo
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gegl-types-internal.h"
+#include "gegl.h"
+#include "gegl-utils.h"
+
+#include "graph/gegl-node.h"
+#include "graph/gegl-pad.h"
+
+#include "process/gegl-graph-debug.h"
+#include "process/gegl-graph-traversal.h"
+#include "process/gegl-graph-traversal-private.h"
+
+#include "operation/gegl-operation.h"
+#include "operation/gegl-operation-context.h"
+
+#include <stdio.h>
+
+void
+gegl_graph_dump_outputs (GeglNode *node)
+{
+  GeglGraphTraversal *path      = gegl_graph_build (node);
+  GList              *list_iter = NULL;
+
+  gegl_graph_prepare (path);
+
+  for (list_iter = path->dfs_path; list_iter; list_iter = list_iter->next)
+  {
+    GeglNode *cur_node = GEGL_NODE (list_iter->data);
+    if (gegl_node_get_pad (cur_node, "output"))
+      {
+        const Babl *format = gegl_operation_get_format (cur_node->operation, "output");
+        printf ("%s: output=%s\n", gegl_node_get_debug_name (cur_node),
+                                   format ? babl_get_name (format) : "N/A");
+      }
+    else
+      {
+        printf ("%s: sink\n", gegl_node_get_debug_name (cur_node));
+      }
+
+    if (cur_node->valid_have_rect)
+      {
+        printf ("  bounds: ");
+        gegl_rectangle_dump (&cur_node->have_rect);
+      }
+  }
+
+  gegl_graph_free (path);
+}
+
+void
+gegl_graph_dump_request (GeglNode            *node,
+                         const GeglRectangle *roi)
+{
+  GeglGraphTraversal *path      = gegl_graph_build (node);
+  GList              *list_iter = NULL;
+
+  gegl_graph_prepare (path);
+  gegl_graph_prepare_request (path, roi);
+
+  for (list_iter = path->dfs_path; list_iter; list_iter = list_iter->next)
+  {
+    GeglNode *cur_node = GEGL_NODE (list_iter->data);
+    GeglOperationContext *context = g_hash_table_lookup (path->contexts, cur_node);
+    
+    if (!context->cached)
+      printf ("%s: result: ", gegl_node_get_debug_name (cur_node));
+    else
+      printf ("%s: result (cached): ", gegl_node_get_debug_name (cur_node));
+    gegl_rectangle_dump (gegl_operation_context_get_need_rect (context));
+  }
+
+  gegl_graph_free (path);
+}
+
diff --git a/gegl/process/gegl-graph-traversal-private.h b/gegl/process/gegl-graph-traversal-private.h
new file mode 100644
index 0000000..cad0856
--- /dev/null
+++ b/gegl/process/gegl-graph-traversal-private.h
@@ -0,0 +1,31 @@
+/* This file is part of GEGL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Daniel Sabo
+ */
+
+#ifndef __GEGL_GRAPH_TRAVERSAL_PRIVATE_H__
+#define __GEGL_GRAPH_TRAVERSAL_PRIVATE_H__
+
+struct _GeglGraphTraversal
+{
+  GHashTable *contexts;
+  GList *dfs_path;
+  GList *bfs_path;
+  gboolean rects_dirty;
+  GeglBuffer *shared_empty;
+};
+
+#endif /* __GEGL_GRAPH_TRAVERSAL_PRIVATE_H__ */
diff --git a/gegl/process/gegl-graph-traversal.c b/gegl/process/gegl-graph-traversal.c
new file mode 100644
index 0000000..0ce9da0
--- /dev/null
+++ b/gegl/process/gegl-graph-traversal.c
@@ -0,0 +1,453 @@
+/* This file is part of GEGL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Daniel Sabo
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gegl-types-internal.h"
+#include "gegl.h"
+#include "gegl-utils.h"
+#include "gegl-debug.h"
+
+#include "buffer/gegl-region.h"
+
+#include "graph/gegl-node.h"
+#include "graph/gegl-pad.h"
+#include "graph/gegl-visitable.h"
+#include "graph/gegl-connection.h"
+
+#include "process/gegl-graph-traversal.h"
+#include "process/gegl-graph-traversal-private.h"
+#include "process/gegl-list-visitor.h"
+
+#include "operation/gegl-operation.h"
+#include "operation/gegl-operation-context.h"
+
+typedef struct
+{
+  const gchar *name;
+  GeglOperationContext *context;
+} ContextConnection;
+
+static void   free_context_connection                  (gpointer concon);
+static GList *gegl_graph_get_connected_output_contexts (GeglGraphTraversal *path,
+                                                        GeglPad            *output_pad);
+static void   _gegl_graph_do_build                     (GeglGraphTraversal *path,
+                                                        GeglNode           *node);
+static GeglBuffer *gegl_graph_get_shared_empty         (GeglGraphTraversal *path);
+
+static void
+_gegl_graph_do_build (GeglGraphTraversal *path, GeglNode *node)
+{
+  GeglPad *pad = NULL;
+  GeglListVisitor *list_visitor = g_object_new (GEGL_TYPE_LIST_VISITOR, NULL);
+
+  /* We need to check the real node of the output/input pad in case this is a proxy node */
+  pad = gegl_node_get_pad (node, "output");
+  if (pad)
+    {
+      node = gegl_pad_get_node (pad);
+    }
+  else
+    {
+      pad = gegl_node_get_pad (node, "input");
+      if (pad)
+        node = gegl_pad_get_node (pad);
+    }
+
+  path->dfs_path = gegl_list_visitor_get_dfs_path (list_visitor, GEGL_VISITABLE (node));
+  path->bfs_path = gegl_list_visitor_get_bfs_path (list_visitor, GEGL_VISITABLE (node));
+  path->contexts = g_hash_table_new_full (NULL,
+                                          NULL,
+                                          NULL,
+                                          (GDestroyNotify)gegl_operation_context_destroy);
+  path->rects_dirty = FALSE;
+  g_object_unref (list_visitor);
+}
+
+/**
+ * gegl_graph_build:
+ * @node: The node to build a traversal for
+ * 
+ * Build a traversal for @node.
+ *
+ * Return value: A new #GeglGraphTraversal
+ */
+GeglGraphTraversal *
+gegl_graph_build (GeglNode *node)
+{
+  GeglGraphTraversal *result = g_new0 (GeglGraphTraversal, 1);
+
+  _gegl_graph_do_build (result, node);
+
+  return result;
+}
+
+/**
+ * gegl_graph_rebuild:
+ * @path: The traversal object to reuse
+ * @node: The node to build a traversal for
+ * 
+ * Build a traversal for @node, reusing the scratch objects
+ * from @path where possible.
+ */
+void
+gegl_graph_rebuild (GeglGraphTraversal *path, GeglNode *node)
+{
+  g_list_free (path->dfs_path);
+  g_list_free (path->bfs_path);
+  g_hash_table_unref (path->contexts);
+
+  /* Replaces everything but shared_empty */
+  _gegl_graph_do_build (path, node);
+}
+
+void
+gegl_graph_free (GeglGraphTraversal *path)
+{
+  g_list_free (path->dfs_path);
+  g_list_free (path->bfs_path);
+  g_hash_table_unref (path->contexts);
+  if (path->shared_empty)
+    g_object_unref (path->shared_empty);
+
+  g_free (path);
+}
+
+
+/**
+ * gegl_graph_get_bounding_box:
+ * @path: The traversal path
+ * 
+ * Get output bounding box for this graph, which is the
+ * have rect of the final node.
+ *
+ * Return value: Output rect of @path
+ */
+GeglRectangle
+gegl_graph_get_bounding_box (GeglGraphTraversal *path)
+{
+  GeglNode *node = GEGL_NODE (g_list_last (path->dfs_path)->data);
+  if (node->valid_have_rect)
+    {
+      return node->have_rect;
+    }
+  return *GEGL_RECTANGLE(0, 0, 0, 0);
+}
+
+/**
+ * gegl_graph_prepare:
+ * @path: The traversal path
+ * 
+ * Prepare all nodes, initializing their output formats and have rects.
+ */
+void
+gegl_graph_prepare (GeglGraphTraversal *path)
+{
+  GList *list_iter = NULL;
+  
+  for (list_iter = path->dfs_path; list_iter; list_iter = list_iter->next)
+  {
+    GeglNode *node = GEGL_NODE (list_iter->data);
+    GeglOperation *operation = node->operation;
+    
+    g_mutex_lock (&node->mutex);
+
+    gegl_operation_prepare (operation);
+    node->have_rect = gegl_operation_get_bounding_box (operation);
+    node->valid_have_rect = TRUE;
+
+    if (node->cache)
+      {
+        gegl_buffer_set_extent (GEGL_BUFFER (node->cache),
+                                &node->have_rect);
+      }
+
+    g_mutex_unlock (&node->mutex);
+    
+    if (!g_hash_table_contains (path->contexts, node))
+      {
+        GeglOperationContext *context = gegl_operation_context_new (node->operation);
+        
+        g_hash_table_insert (path->contexts,
+                             node,
+                             context);
+      }
+  }
+}
+
+/**
+ * gegl_graph_prepare_request:
+ * @path: The traversal path
+ * @request_roi: The request rect
+ * 
+ * Prepare the graph to render request_roi, this will calculate
+ * the area that needs to be rendered from each node in the
+ * graph to fulfill this request.
+ */
+void
+gegl_graph_prepare_request (GeglGraphTraversal  *path,
+                            const GeglRectangle *request_roi)
+{
+  GList *list_iter = NULL;
+  static const GeglRectangle empty_rect = {0, 0, 0, 0};
+
+  g_return_if_fail (path->bfs_path);
+
+  if (path->rects_dirty)
+    {
+      /* Zero all the needs rects so we can intersect with them below */
+      for (list_iter = path->bfs_path; list_iter; list_iter = list_iter->next)
+        {
+          GeglNode *node = GEGL_NODE (list_iter->data);
+          GeglOperationContext *context = g_hash_table_lookup (path->contexts, node);
+
+          /* We only need to reset the need rect, result will always get overwritten */
+          gegl_operation_context_set_need_rect (context, &empty_rect);
+
+          /* Reset cached status, because the rect we need may have changed */
+          context->cached = FALSE;
+        }
+    }
+
+  path->rects_dirty = TRUE;
+
+  {
+    /* Prep the first node */
+    GeglNode *node = GEGL_NODE (path->bfs_path->data);
+    GeglOperationContext *context = g_hash_table_lookup (path->contexts, node);
+    GeglRectangle new_need;
+
+    g_return_if_fail (context);
+
+    gegl_rectangle_intersect (&new_need, &node->have_rect, request_roi);
+
+    gegl_operation_context_set_need_rect (context, &new_need);
+    gegl_operation_context_set_result_rect (context, &new_need);
+  }
+  
+  /* Iterate over all the nodes and propagate the requested rectangle */
+  for (list_iter = path->bfs_path; list_iter; list_iter = list_iter->next)
+    {
+      GeglNode             *node      = GEGL_NODE (list_iter->data);
+      GeglOperation        *operation = node->operation;
+      GeglOperationContext *context;
+      GeglRectangle        *request;
+      GSList               *input_pads;
+      
+      context = g_hash_table_lookup (path->contexts, node);
+      g_return_if_fail (context);
+      
+      request = gegl_operation_context_get_need_rect (context);
+
+      if (request->width == 0 || request->height == 0)
+        {
+          gegl_operation_context_set_result_rect (context, &empty_rect);
+        }
+      else if (node->cache &&
+               gegl_region_rect_in (node->cache->valid_region, request) == GEGL_OVERLAP_RECTANGLE_IN)
+        {
+          /* This node is cached and the cache fulfills our need rect */
+          context->cached = TRUE;
+          gegl_operation_context_set_result_rect (context, &empty_rect);
+        }
+      else
+        {
+          /* FIXME: We could trim this down based on the cache, instead of being all or nothing */
+          gegl_operation_context_set_result_rect (context, request);
+
+          for (input_pads = node->input_pads; input_pads; input_pads = input_pads->next)
+            {
+              GeglPad *source_pad = gegl_pad_get_connected_to (input_pads->data);
+
+              if (source_pad)
+                {
+                  GeglNode             *source_node    = gegl_pad_get_node (source_pad);
+                  GeglOperationContext *source_context = g_hash_table_lookup (path->contexts, source_node);
+                  const gchar          *pad_name       = gegl_pad_get_name (input_pads->data);
+
+                  GeglRectangle rect, current_need, new_need;
+
+                  /* Combine this need rect with any existing request */
+                  rect = gegl_operation_get_required_for_output (operation, pad_name, request);
+                  current_need = *gegl_operation_context_get_need_rect (source_context);
+
+                  gegl_rectangle_bounding_box (&new_need, &rect, &current_need);
+
+                  /* Limit request to the nodes output */
+                  gegl_rectangle_intersect (&new_need, &source_node->have_rect, &new_need);
+
+                  gegl_operation_context_set_need_rect (source_context, &new_need);
+                }
+            }
+        }
+    }
+}
+
+void
+free_context_connection (gpointer concon)
+{
+  g_free (concon);
+}
+
+GList *
+gegl_graph_get_connected_output_contexts (GeglGraphTraversal *path,
+                                          GeglPad            *output_pad)
+{
+  GList *result = NULL;
+  GSList *targets = gegl_pad_get_connections (output_pad);
+  GSList *targets_iter;
+  for (targets_iter = targets; targets_iter; targets_iter = g_slist_next (targets_iter))
+    {
+      GeglNode *target_node = gegl_connection_get_sink_node (targets_iter->data);
+      GeglOperationContext *target_context = g_hash_table_lookup (path->contexts, target_node);
+      
+      /* Only include this target if it's part of the current path */
+      if (target_context)
+        {
+          const gchar *target_pad_name = gegl_pad_get_name (gegl_connection_get_sink_pad 
(targets_iter->data));
+          
+          ContextConnection *result_element = g_new0 (ContextConnection, 1);
+          result_element->name    = target_pad_name;
+          result_element->context = target_context;
+          
+          result = g_list_prepend (result, result_element);
+        }
+    }
+  return result;
+}
+
+GeglBuffer *
+gegl_graph_get_shared_empty (GeglGraphTraversal *path)
+{
+  if (!path->shared_empty)
+    {
+      path->shared_empty = gegl_buffer_new_ram (GEGL_RECTANGLE (0, 0, 0, 0),
+                                                babl_format ("RGBA float"));
+      gegl_object_set_has_forked (path->shared_empty);
+    }
+  return path->shared_empty;
+}
+
+
+/**
+ * gegl_graph_process:
+ * @path: The traversal path
+ * 
+ * Process the prepared request. This will return the
+ * resulting buffer from the final node, or NULL if
+ * that node is a sink.
+ *
+ * If gegl_graph_prepare_request has not been called
+ * the behavior of this function is undefined.
+ *
+ * Return value: (transfer full): The result of the graph, or NULL if
+ * there is no output pad.
+ */
+GeglBuffer *
+gegl_graph_process (GeglGraphTraversal *path)
+{
+  GList *list_iter = NULL;
+  GeglBuffer *result = NULL;
+  GeglOperationContext *context = NULL;
+  GeglOperationContext *last_context = NULL;
+
+  for (list_iter = path->dfs_path; list_iter; list_iter = list_iter->next)
+    {
+      GeglNode *node = GEGL_NODE (list_iter->data);
+      GeglOperation *operation = node->operation;
+      GeglBuffer *operation_result = NULL;
+      g_return_val_if_fail (node, NULL);
+      g_return_val_if_fail (operation, NULL);
+      
+      if (last_context)
+        gegl_operation_context_purge (last_context);
+      
+      context = g_hash_table_lookup (path->contexts, node);
+      g_return_val_if_fail (context, NULL);
+
+      GEGL_NOTE (GEGL_DEBUG_PROCESS,
+                 "Will process %s result_rect = %d, %d %d×%d",
+                 gegl_node_get_debug_name (node),
+                 context->result_rect.x, context->result_rect.y, context->result_rect.width, 
context->result_rect.height);
+      
+      if (context->need_rect.width > 0 && context->need_rect.height > 0)
+        {
+          if (context->cached)
+            {
+              GEGL_NOTE (GEGL_DEBUG_PROCESS,
+                         "Using cached result for %s",
+                         gegl_node_get_debug_name (node));
+              operation_result = GEGL_BUFFER (node->cache);
+            }
+          else
+            {
+              /* Guarantee input pad */
+              if (gegl_node_has_pad (node, "input") &&
+                  !gegl_operation_context_get_object (context, "input"))
+                {
+                  gegl_operation_context_set_object (context, "input", G_OBJECT 
(gegl_graph_get_shared_empty(path)));
+                }
+
+              gegl_operation_process (operation, context, "output", &context->need_rect, context->level);
+              operation_result = GEGL_BUFFER (gegl_operation_context_get_object (context, "output"));
+            }
+        }
+      else
+        {
+          operation_result = NULL;
+        }
+
+      if (operation_result)
+        {
+          GeglPad *output_pad = gegl_node_get_pad (node, "output");
+          GList   *targets = gegl_graph_get_connected_output_contexts (path, output_pad);
+          GList   *targets_iter;
+
+          GEGL_NOTE (GEGL_DEBUG_PROCESS,
+                     "Will deliver the results of %s:%s to %d targets",
+                     gegl_node_get_debug_name (node),
+                     "output",
+                     g_list_length (targets));
+          
+          if (g_list_length (targets) > 1)
+            gegl_object_set_has_forked (operation_result);
+
+          for (targets_iter = targets; targets_iter; targets_iter = g_list_next (targets_iter))
+            {
+              ContextConnection *target_con = targets_iter->data;
+              gegl_operation_context_set_object (target_con->context, target_con->name, G_OBJECT 
(operation_result));
+            }
+          
+          g_list_free_full (targets, free_context_connection);
+        }
+      
+      last_context = context;
+    }
+  
+  if (last_context)
+    {
+      GValue *value = gegl_operation_context_get_value (context, "output");
+      if (value)
+        result = g_value_dup_object (value);
+      gegl_operation_context_purge (last_context);
+    }
+
+  return result;
+}
diff --git a/gegl/process/gegl-graph-traversal.h b/gegl/process/gegl-graph-traversal.h
new file mode 100644
index 0000000..34b360b
--- /dev/null
+++ b/gegl/process/gegl-graph-traversal.h
@@ -0,0 +1,17 @@
+#ifndef __GEGL_GRAPH_TRAVERSAL_H__
+#define __GEGL_GRAPH_TRAVERSAL_H__
+
+typedef struct _GeglGraphTraversal GeglGraphTraversal;
+
+void gegl_graph_dfs_dump (GeglNode *);
+
+GeglGraphTraversal *gegl_graph_build (GeglNode *node);
+void gegl_graph_rebuild (GeglGraphTraversal *path, GeglNode *node);
+void gegl_graph_free (GeglGraphTraversal *path);
+
+void gegl_graph_prepare (GeglGraphTraversal *path);
+void gegl_graph_prepare_request (GeglGraphTraversal *path, const GeglRectangle *roi);
+GeglBuffer *gegl_graph_process (GeglGraphTraversal *path);
+GeglRectangle gegl_graph_get_bounding_box (GeglGraphTraversal *path);
+
+#endif /* __GEGL_GRAPH_TRAVERSAL_H__ */
diff --git a/gegl/process/gegl-list-visitor.c b/gegl/process/gegl-list-visitor.c
new file mode 100644
index 0000000..8d9a6c3
--- /dev/null
+++ b/gegl/process/gegl-list-visitor.c
@@ -0,0 +1,120 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Daniel Sabo
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <string.h>
+
+#include "gegl.h"
+#include "gegl-debug.h"
+#include "gegl-types-internal.h"
+#include "gegl-list-visitor.h"
+#include "graph/gegl-node.h"
+#include "graph/gegl-pad.h"
+#include "graph/gegl-visitable.h"
+#include "gegl-instrument.h"
+#include "operation/gegl-operation.h"
+
+static void gegl_list_visitor_class_init (GeglListVisitorClass *klass);
+static void gegl_list_visitor_visit_node (GeglVisitor          *self,
+                                          GeglNode             *node);
+static void gegl_list_visitor_finalize   (GObject              *visitor);
+
+G_DEFINE_TYPE (GeglListVisitor, gegl_list_visitor, GEGL_TYPE_VISITOR)
+
+
+static void
+gegl_list_visitor_class_init (GeglListVisitorClass *klass)
+{
+  GeglVisitorClass *visitor_class = GEGL_VISITOR_CLASS (klass);
+  GObjectClass     *gobject_class = G_OBJECT_CLASS (klass);
+
+  visitor_class->visit_node = gegl_list_visitor_visit_node;
+  gobject_class->finalize   = gegl_list_visitor_finalize;
+}
+
+static void
+gegl_list_visitor_init (GeglListVisitor *visitor)
+{
+  GeglListVisitor *list_visitor = GEGL_LIST_VISITOR(visitor);
+
+  list_visitor->visit_path = NULL;
+}
+
+static void
+gegl_list_visitor_finalize (GObject *object)
+{
+  GeglListVisitor *list_visitor = GEGL_LIST_VISITOR(object);
+
+  if (list_visitor->visit_path)
+    {
+      g_list_free (list_visitor->visit_path);
+      list_visitor->visit_path = NULL;
+    }
+
+  G_OBJECT_CLASS (gegl_list_visitor_parent_class)->finalize (object);
+}
+
+void
+gegl_list_visitor_reset (GeglListVisitor *self)
+{
+  gegl_visitor_reset (GEGL_VISITOR (self));
+  if (self->visit_path)
+    {
+      g_list_free (self->visit_path);
+      self->visit_path = NULL;
+    }
+}
+
+GList *
+gegl_list_visitor_get_dfs_path (GeglListVisitor *self,
+                                GeglVisitable   *visitable)
+{
+  GList *result;
+
+  gegl_list_visitor_reset (self);
+  gegl_visitor_dfs_traverse (GEGL_VISITOR (self), visitable);
+
+  result = self->visit_path;
+  self->visit_path = NULL;
+  return g_list_reverse (result);
+}
+
+GList *
+gegl_list_visitor_get_bfs_path (GeglListVisitor *self,
+                                GeglVisitable   *visitable)
+{
+  GList *result;
+
+  gegl_list_visitor_reset (self);
+  gegl_visitor_bfs_traverse (GEGL_VISITOR (self), visitable);
+
+  result = self->visit_path;
+  self->visit_path = NULL;
+  return g_list_reverse (result);
+}
+
+static void
+gegl_list_visitor_visit_node (GeglVisitor *visitor,
+                              GeglNode    *node)
+{
+  GeglListVisitor *list_visitor = GEGL_LIST_VISITOR(visitor);
+
+  list_visitor->visit_path = g_list_prepend (list_visitor->visit_path, node);
+}
diff --git a/gegl/process/gegl-list-visitor.h b/gegl/process/gegl-list-visitor.h
new file mode 100644
index 0000000..89c100a
--- /dev/null
+++ b/gegl/process/gegl-list-visitor.h
@@ -0,0 +1,60 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Daniel Sabo
+ */
+
+#ifndef __GEGL_LIST_VISITOR_H__
+#define __GEGL_LIST_VISITOR_H__
+
+#include "graph/gegl-visitor.h"
+
+G_BEGIN_DECLS
+
+
+#define GEGL_TYPE_LIST_VISITOR            (gegl_list_visitor_get_type ())
+#define GEGL_LIST_VISITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_LIST_VISITOR, 
GeglListVisitor))
+#define GEGL_LIST_VISITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GEGL_TYPE_LIST_VISITOR, 
GeglListVisitorClass))
+#define GEGL_IS_LIST_VISITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_LIST_VISITOR))
+#define GEGL_IS_LIST_VISITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GEGL_TYPE_LIST_VISITOR))
+#define GEGL_LIST_VISITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GEGL_TYPE_LIST_VISITOR, 
GeglListVisitorClass))
+
+typedef struct _GeglListVisitorClass GeglListVisitorClass;
+typedef struct _GeglListVisitor GeglListVisitor;
+
+struct _GeglListVisitor
+{
+  GeglVisitor  parent_instance;
+  
+  GList *visit_path;
+};
+
+struct _GeglListVisitorClass
+{
+  GeglVisitorClass  parent_class;
+};
+
+
+GType   gegl_list_visitor_get_type (void) G_GNUC_CONST;
+
+void    gegl_list_visitor_reset        (GeglListVisitor *self);
+GList  *gegl_list_visitor_get_dfs_path (GeglListVisitor *self,
+                                        GeglVisitable   *visitable);
+GList  *gegl_list_visitor_get_bfs_path (GeglListVisitor *self,
+                                        GeglVisitable   *visitable);
+
+G_END_DECLS
+
+#endif /* __GEGL_LIST_VISITOR_H__ */
diff --git a/gegl/process/gegl-processor.c b/gegl/process/gegl-processor.c
index e9cb616..16c9925 100644
--- a/gegl/process/gegl-processor.c
+++ b/gegl/process/gegl-processor.c
@@ -34,6 +34,7 @@
 
 #include "graph/gegl-visitor.h"
 #include "graph/gegl-visitable.h"
+#include "process/gegl-list-visitor.h"
 
 #include "opencl/gegl-cl.h"
 
@@ -164,8 +165,7 @@ gegl_processor_finalize (GObject *self_object)
 
   if (processor->context)
     {
-      GeglCache *cache = gegl_node_get_cache (processor->input);
-      gegl_node_remove_context (processor->node, cache);
+      gegl_operation_context_destroy (processor->context);
     }
 
   if (processor->node)
@@ -365,8 +365,10 @@ gegl_processor_set_rectangle (GeglProcessor       *processor,
 
       cache = gegl_node_get_cache (processor->input);
 
-      if (!gegl_node_get_context (processor->node, cache))
-        processor->context = gegl_node_add_context (processor->node, cache);
+      if (!processor->context)
+        {
+          processor->context = gegl_operation_context_new (processor->node->operation);
+        }
 
       g_value_init (&value, GEGL_TYPE_BUFFER);
       g_value_set_object (&value, cache);
@@ -494,10 +496,11 @@ render_rectangle (GeglProcessor *processor)
               /* create a buffer and initialise it */
               guchar *buf;
 
-              gegl_region_union_with_rect (cache->valid_region, dr);
               buf = g_malloc (dr->width * dr->height * pxsize);
               g_assert (buf);
 
+              /* FIXME: Check if the node caches naturaly, if so the buffer_set call isn't needed */
+
               /* do the image calculations using the buffer */
               gegl_node_blit (processor->input, 1.0, dr, cache->format, buf,
                               GEGL_AUTO_ROWSTRIDE, GEGL_BLIT_DEFAULT);
@@ -747,19 +750,16 @@ gegl_processor_work (GeglProcessor *processor,
                      gdouble       *progress)
 {
   gboolean   more_work = FALSE;
-  GeglCache *cache     = gegl_node_get_cache (processor->input);
 
   if (gegl_config()->use_opencl)
     {
       if (gegl_cl_is_accelerated ()
           && processor->chunk_size != GEGL_CL_CHUNK_SIZE)
         {
-          GeglVisitor *visitor = g_object_new (GEGL_TYPE_VISITOR, NULL);
-          GSList *iterator = NULL;
-          GSList *visits_list = NULL;
-          gegl_visitor_reset (visitor);
-          gegl_visitor_dfs_traverse (visitor, GEGL_VISITABLE (processor->node));
-          visits_list = gegl_visitor_get_visits_list (visitor);
+          GeglListVisitor *visitor = g_object_new (GEGL_TYPE_LIST_VISITOR, NULL);
+          GList *iterator = NULL;
+          GList *visits_list = NULL;
+          visits_list = gegl_list_visitor_get_dfs_path (visitor, GEGL_VISITABLE (processor->node));
 
           for (iterator = visits_list; iterator; iterator = iterator->next)
             {
@@ -772,6 +772,7 @@ gegl_processor_work (GeglProcessor *processor,
                 }
             }
 
+          g_list_free (visits_list);
           g_object_unref (visitor);
         }
     }
@@ -794,7 +795,7 @@ gegl_processor_work (GeglProcessor *processor,
                               processor->context,
                               "output"  /* ignored output_pad */,
                               &processor->context->result_rect, processor->context->level);
-      gegl_node_remove_context (processor->node, cache);
+      gegl_operation_context_destroy (processor->context);
       processor->context = NULL;
 
       return TRUE;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]