[gegl] point-filter,-composer,-composer3: add threading logic



commit a206f032f77064cf9bff8590ac83ca5b086b53fd
Author: Øyvind Kolås <pippin gimp org>
Date:   Tue Jul 1 01:20:15 2014 +0200

    point-filter,-composer,-composer3: add threading logic
    
    When consuming chunks of gegl_buffer_iterate, moving the babl conversions from
    the main thread to the work-threads is neccesary to barely cancel out overhead
    on an i5.

 gegl/operation/gegl-operation-point-composer.c  |  387 +++++++++++++++--------
 gegl/operation/gegl-operation-point-composer3.c |  338 ++++++++++++++++++--
 gegl/operation/gegl-operation-point-filter.c    |  328 +++++++++++++------
 gegl/operation/gegl-operation-point-render.c    |    1 +
 4 files changed, 789 insertions(+), 265 deletions(-)
---
diff --git a/gegl/operation/gegl-operation-point-composer.c b/gegl/operation/gegl-operation-point-composer.c
index b3c11e8..d311507 100644
--- a/gegl/operation/gegl-operation-point-composer.c
+++ b/gegl/operation/gegl-operation-point-composer.c
@@ -22,13 +22,141 @@
 #include <glib-object.h>
 
 #include "gegl.h"
-#include "gegl/gegl-debug.h"
 #include "gegl-operation-point-composer.h"
 #include "gegl-operation-context.h"
+#include "gegl-config.h"
+#include <sys/types.h>
+#include <unistd.h>
 #include <string.h>
 
-#include "opencl/gegl-cl.h"
-#include "gegl-buffer-cl-iterator.h"
+typedef struct ThreadData
+{
+  GeglOperationPointComposerClass *klass;
+  GeglOperation                   *operation;
+  guchar                          *input;
+  guchar                          *aux;
+  guchar                          *output;
+  gint                            *pending;
+  gint                            *started;
+  gint                             level;
+  gboolean                         success;
+  GeglRectangle                    roi;
+
+  guchar                          *in_tmp;
+  guchar                          *aux_tmp;
+  guchar                          *output_tmp;
+  const Babl *input_fish;
+  const Babl *aux_fish;
+  const Babl *output_fish;
+} ThreadData;
+
+static void thread_process (gpointer thread_data, gpointer unused)
+{
+  ThreadData *data = thread_data;
+
+  guchar *input = data->input;
+  guchar *aux = data->aux;
+  guchar *output = data->output;
+  glong samples = data->roi.width * data->roi.height;
+
+  if (data->input_fish && input)
+    {
+      babl_process (data->input_fish, data->input, data->in_tmp, samples);
+      input = data->in_tmp;
+    }
+  if (data->aux_fish && aux)
+    {
+      babl_process (data->aux_fish, data->aux, data->aux_tmp, samples);
+      aux = data->aux_tmp;
+    }
+  if (data->output_fish)
+    output = data->output_tmp;
+
+  if (!data->klass->process (data->operation,
+                       input, aux,
+                       output, samples,
+                       &data->roi, data->level))
+    data->success = FALSE;
+  
+  if (data->output_fish)
+    babl_process (data->output_fish, data->output_tmp, data->output, samples);
+
+  g_atomic_int_add (data->pending, -1);
+}
+
+static GThreadPool *thread_pool (void)
+{
+  static GThreadPool *pool = NULL;
+  if (!pool)
+    {
+      pool =  g_thread_pool_new (thread_process, NULL, gegl_config()->threads,
+                                 FALSE, NULL);
+    }
+  return pool;
+}
+
+static gboolean
+gegl_operation_composer_process (GeglOperation        *operation,
+                                 GeglOperationContext *context,
+                                 const gchar          *output_prop,
+                                 const GeglRectangle  *result,
+                                 gint                  level)
+{
+  GeglOperationComposerClass *klass    = GEGL_OPERATION_COMPOSER_GET_CLASS (operation);
+  GeglOperationClass         *op_class = GEGL_OPERATION_CLASS (klass);
+  GeglBuffer                 *input;
+  GeglBuffer                 *aux;
+  GeglBuffer                 *output;
+  gboolean                    success = FALSE;
+
+  if (strcmp (output_prop, "output"))
+    {
+      g_warning ("requested processing of %s pad on a composer", output_prop);
+      return FALSE;
+    }
+
+  if (result->width == 0 || result->height == 0)
+  {
+    output = gegl_operation_context_get_target (context, "output");
+    return TRUE;
+  }
+
+  input = gegl_operation_context_get_source (context, "input");
+
+  if (op_class->want_in_place && 
+      gegl_can_do_inplace_processing (operation, input, result))
+    {
+      output = g_object_ref (input);
+      gegl_operation_context_take_object (context, "output", G_OBJECT (output));
+    }
+  else
+    {
+      output = gegl_operation_context_get_target (context, "output");
+    }
+
+  aux   = gegl_operation_context_get_source (context, "aux");
+
+  /* A composer with a NULL aux, can still be valid, the
+   * subclass has to handle it.
+   */
+  if (input != NULL ||
+      aux != NULL)
+    {
+      success = klass->process (operation, input, aux, output, result, level);
+
+      if (input)
+        g_object_unref (input);
+      if (aux)
+        g_object_unref (aux);
+    }
+  else
+    {
+      g_warning ("%s received NULL input, aux, and aux2",
+                 gegl_node_get_operation (operation->node));
+    }
+
+  return success;
+}
 
 static gboolean gegl_operation_point_composer_process
                               (GeglOperation       *operation,
@@ -51,17 +179,15 @@ static void prepare (GeglOperation *operation)
 static void
 gegl_operation_point_composer_class_init (GeglOperationPointComposerClass *klass)
 {
-  /*GObjectClass       *object_class    = G_OBJECT_CLASS (klass);*/
-  GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
-  GeglOperationComposerClass *composer_class = GEGL_OPERATION_COMPOSER_CLASS (klass);
+  GeglOperationClass          *operation_class = GEGL_OPERATION_CLASS (klass);
+  GeglOperationComposerClass *composer_class  = GEGL_OPERATION_COMPOSER_CLASS (klass);
 
   composer_class->process = gegl_operation_point_composer_process;
+  operation_class->process = gegl_operation_composer_process;
   operation_class->prepare = prepare;
-  operation_class->no_cache = FALSE;
+  operation_class->no_cache =TRUE;
   operation_class->want_in_place = TRUE;
-
-  klass->process = NULL;
-  klass->cl_process = NULL;
+  operation_class->threaded = TRUE;
 }
 
 static void
@@ -71,152 +197,159 @@ gegl_operation_point_composer_init (GeglOperationPointComposer *self)
 }
 
 static gboolean
-gegl_operation_point_composer_cl_process (GeglOperation       *operation,
-                                          GeglBuffer          *input,
-                                          GeglBuffer          *aux,
-                                          GeglBuffer          *output,
-                                          const GeglRectangle *result,
-                                          gint                 level)
+gegl_operation_point_composer_process (GeglOperation       *operation,
+                                       GeglBuffer          *input,
+                                       GeglBuffer          *aux,
+                                       GeglBuffer          *output,
+                                       const GeglRectangle *result,
+                                       gint                 level)
 {
-  const Babl *in_format  = gegl_operation_get_format (operation, "input");
-  const Babl *aux_format = gegl_operation_get_format (operation, "aux");
-  const Babl *out_format = gegl_operation_get_format (operation, "output");
-
-  GeglOperationClass *operation_class = GEGL_OPERATION_GET_CLASS (operation);
   GeglOperationPointComposerClass *point_composer_class = GEGL_OPERATION_POINT_COMPOSER_GET_CLASS 
(operation);
+  const Babl *in_format   = gegl_operation_get_format (operation, "input");
+  const Babl *aux_format  = gegl_operation_get_format (operation, "aux");
+  const Babl *out_format  = gegl_operation_get_format (operation, "output");
 
-  cl_int cl_err = 0;
-  gboolean err;
-
-  gint foo, read;
 
-  GeglBufferClIterator *i;
-
-  /* non-texturizable format! */
-  if (!gegl_cl_color_babl (in_format,  NULL) ||
-      !gegl_cl_color_babl (aux_format, NULL) ||
-      !gegl_cl_color_babl (out_format, NULL))
-    {
-      GEGL_NOTE (GEGL_DEBUG_OPENCL, "Non-texturizable format!");
-      return FALSE;
-    }
-
-  GEGL_NOTE (GEGL_DEBUG_OPENCL, "GEGL_OPERATION_POINT_COMPOSER: %s", operation_class->name);
-
-  i = gegl_buffer_cl_iterator_new (output,   result, out_format, GEGL_CL_BUFFER_WRITE);
-  read = gegl_buffer_cl_iterator_add (i, input, result, in_format,  GEGL_CL_BUFFER_READ, GEGL_ABYSS_NONE);
-
-  if (aux)
-    foo = gegl_buffer_cl_iterator_add (i, aux, result, aux_format,  GEGL_CL_BUFFER_READ, GEGL_ABYSS_NONE);
-
-  while (gegl_buffer_cl_iterator_next (i, &err))
+  if ((result->width > 0) && (result->height > 0))
     {
-      if (err) return FALSE;
+      const Babl *in_buf_format  = input?gegl_buffer_get_format(input):NULL;
+      const Babl *aux_buf_format = aux?gegl_buffer_get_format(aux):NULL;
+      const Babl *output_buf_format = output?gegl_buffer_get_format(output):NULL;
 
+      if (gegl_operation_use_threading (operation, result) && result->height > 1)
+      {
+        gint threads = gegl_config ()->threads;
+        GThreadPool *pool = thread_pool ();
+        ThreadData thread_data[GEGL_MAX_THREADS];
+        GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level, output_buf_format, 
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
+        gint foo = 0, read = 0;
+
+        gint in_bpp = input?babl_format_get_bytes_per_pixel (in_format):0;
+        gint aux_bpp = aux?babl_format_get_bytes_per_pixel (aux_format):0;
+        gint out_bpp = babl_format_get_bytes_per_pixel (out_format);
+
+        gint in_buf_bpp = input?babl_format_get_bytes_per_pixel (in_buf_format):0;
+        gint aux_buf_bpp = aux?babl_format_get_bytes_per_pixel (aux_buf_format):0;
+        gint out_buf_bpp = babl_format_get_bytes_per_pixel (output_buf_format);
+        gint temp_id = 0;
+
+        if (input)
+        {
+          read = gegl_buffer_iterator_add (i, input, result, level, in_buf_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+          for (gint j = 0; j < threads; j ++)
+          {
+            if (in_buf_format != in_format)
+            {
+              thread_data[j].input_fish = babl_fish (in_buf_format, in_format);
+              thread_data[j].in_tmp = gegl_temp_buffer (temp_id++, in_bpp * result->width * result->height);
+            }
+            else
+            {
+              thread_data[j].input_fish = NULL;
+            }
+          }
+        }
+        else
+          for (gint j = 0; j < threads; j ++)
+            thread_data[j].input_fish = NULL;
+        if (aux)
         {
-          if (point_composer_class->cl_process)
+          foo = gegl_buffer_iterator_add (i, aux, result, level, aux_buf_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+          for (gint j = 0; j < threads; j ++)
+          {
+            if (aux_buf_format != aux_format)
             {
-              err = point_composer_class->cl_process(operation, i->tex[read],
-                                                     (aux)? i->tex[foo] : NULL,
-                                                     i->tex[0], i->size[0], &i->roi[0], level);
-              if (err)
-                {
-                  gegl_buffer_cl_iterator_stop (i);
-                  GEGL_NOTE (GEGL_DEBUG_OPENCL, "Error: %s", operation_class->name);
-                  return FALSE;
-                }
+              thread_data[j].aux_fish = babl_fish (aux_buf_format, aux_format);
+              thread_data[j].aux_tmp = gegl_temp_buffer (temp_id++, aux_bpp * result->width * 
result->height);
             }
-          else if (operation_class->cl_data)
+            else
             {
-              gint p = 0;
-              GeglClRunData *cl_data = operation_class->cl_data;
+              thread_data[j].aux_fish = NULL;
+            }
+          }
+        }
+        else
+        {
+          for (gint j = 0; j < threads; j ++)
+            thread_data[j].aux_fish = NULL;
+        }
 
-              cl_err = gegl_clSetKernelArg(cl_data->kernel[0], p++, sizeof(cl_mem), (void*)&i->tex[read]);
-              CL_CHECK;
+        for (gint j = 0; j < threads; j ++)
+        {
+          if (output_buf_format != gegl_buffer_get_format (output))
+          {
+            thread_data[j].output_fish = babl_fish (out_format, output_buf_format);
+            thread_data[j].output_tmp = gegl_temp_buffer (temp_id++, out_bpp * result->width * 
result->height);
+          }
+          else
+          {
+            thread_data[j].output_fish = NULL;
+          }
+        }
 
-              if (aux)
-                cl_err = gegl_clSetKernelArg(cl_data->kernel[0], p++, sizeof(cl_mem), (void*)&i->tex[foo]);
-              else
-                cl_err = gegl_clSetKernelArg(cl_data->kernel[0], p++, sizeof(cl_mem), NULL);
-              CL_CHECK;
+        while (gegl_buffer_iterator_next (i))
+          {
+            gint threads = gegl_config()->threads;
+            gint pending;
+            gint bit;
 
-              cl_err = gegl_clSetKernelArg(cl_data->kernel[0], p++, sizeof(cl_mem), (void*)&i->tex[0]);
-              CL_CHECK;
+            if (i->roi[0].height < threads)
+            {
+              threads = i->roi[0].height;
+            }
 
-              gegl_operation_cl_set_kernel_args (operation, cl_data->kernel[0], &p, &cl_err);
-              CL_CHECK;
+            bit = i->roi[0].height / threads;
+            pending = threads;
 
-              cl_err = gegl_clEnqueueNDRangeKernel(gegl_cl_get_command_queue (),
-                                                   cl_data->kernel[0], 1,
-                                                   NULL, &i->size[0], NULL,
-                                                   0, NULL, NULL);
-              CL_CHECK;
+            for (gint j = 0; j < threads; j++)
+            {
+              thread_data[j].roi.x = (i->roi[0]).x;
+              thread_data[j].roi.width = (i->roi[0]).width;
+              thread_data[j].roi.y = (i->roi[0]).y + bit * j;
+              thread_data[j].roi.height = bit;
             }
-          else
+            thread_data[threads-1].roi.height = i->roi[0].height - (bit * (threads-1));
+            
+            for (gint j = 0; j < threads; j++)
             {
-              gegl_buffer_cl_iterator_stop (i);
-              g_warning ("OpenCL support enabled, but no way to execute");
-              return FALSE;
+              thread_data[j].klass = point_composer_class;
+              thread_data[j].operation = operation;
+              thread_data[j].input = input?((guchar*)i->data[read]) + (bit * j * i->roi[0].width * 
in_buf_bpp):NULL;
+              thread_data[j].aux = aux?((guchar*)i->data[foo]) + (bit * j * i->roi[0].width * 
aux_buf_bpp):NULL;
+              thread_data[j].output = ((guchar*)i->data[0]) + (bit * j * i->roi[0].width * out_buf_bpp);
+              thread_data[j].pending = &pending;
+              thread_data[j].level = level;
+              thread_data[j].success = TRUE;
             }
-        }
-    }
-
-  return TRUE;
+            pending = threads;
 
-error:
-  gegl_buffer_cl_iterator_stop (i);
-  GEGL_NOTE (GEGL_DEBUG_OPENCL, "Error: %s", gegl_cl_errstring(cl_err));
-  return FALSE;
-}
+            for (gint j = 1; j < threads; j++)
+              g_thread_pool_push (pool, &thread_data[j], NULL);
+            thread_process (&thread_data[0], NULL);
 
-static gboolean
-gegl_operation_point_composer_process (GeglOperation       *operation,
-                                       GeglBuffer          *input,
-                                       GeglBuffer          *aux,
-                                       GeglBuffer          *output,
-                                       const GeglRectangle *result,
-                                       gint                 level)
-{
-  GeglOperationClass *operation_class = GEGL_OPERATION_GET_CLASS (operation);
-  GeglOperationPointComposerClass *point_composer_class = GEGL_OPERATION_POINT_COMPOSER_GET_CLASS 
(operation);
-  const Babl *in_format  = gegl_operation_get_format (operation, "input");
-  const Babl *aux_format = gegl_operation_get_format (operation, "aux");
-  const Babl *out_format = gegl_operation_get_format (operation, "output");
-
-  if ((result->width > 0) && (result->height > 0))
-    {
-      if (gegl_operation_use_opencl (operation) &&
-          (operation_class->cl_data || point_composer_class->cl_process))
-        {
-          if (gegl_operation_point_composer_cl_process (operation, input, aux, output, result, level))
-            return TRUE;
-        }
+            while (g_atomic_int_get (&pending)) {};
+          }
 
+        return TRUE;
+      }
+      else
       {
         GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level, out_format, 
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
-        gint read = /*output == input ? 0 :*/ gegl_buffer_iterator_add (i, input,  result, level, in_format, 
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
-        /* using separate read and write iterators for in-place ideally a single
-         * readwrite indice would be sufficient
-         */
+        gint foo = 0, read = 0;
 
+        if (input)
+          read = gegl_buffer_iterator_add (i, input, result, level, in_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
         if (aux)
-          {
-            gint foo = gegl_buffer_iterator_add (i, aux, result, level, aux_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+          foo = gegl_buffer_iterator_add (i, aux, result, level, aux_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
 
-            while (gegl_buffer_iterator_next (i))
-              {
-                 point_composer_class->process (operation, i->data[read], i->data[foo], i->data[0], 
i->length, &(i->roi[0]), level);
-              }
-          }
-        else
+        while (gegl_buffer_iterator_next (i))
           {
-            while (gegl_buffer_iterator_next (i))
-              {
-                 point_composer_class->process (operation, i->data[read], NULL, i->data[0], i->length, 
&(i->roi[0]), level);
-              }
+            point_composer_class->process (operation, input?i->data[read]:NULL,
+                                                      aux?i->data[foo]:NULL,
+                                                      i->data[0], i->length, &(i->roi[0]), level);
           }
+        return TRUE;
       }
-      return TRUE;
     }
   return TRUE;
 }
diff --git a/gegl/operation/gegl-operation-point-composer3.c b/gegl/operation/gegl-operation-point-composer3.c
index 7161652..8d96c9b 100644
--- a/gegl/operation/gegl-operation-point-composer3.c
+++ b/gegl/operation/gegl-operation-point-composer3.c
@@ -24,8 +24,154 @@
 #include "gegl.h"
 #include "gegl-operation-point-composer3.h"
 #include "gegl-operation-context.h"
+#include "gegl-config.h"
+#include <sys/types.h>
+#include <unistd.h>
 #include <string.h>
 
+typedef struct ThreadData
+{
+  GeglOperationPointComposer3Class *klass;
+  GeglOperation                    *operation;
+  guchar                           *input;
+  guchar                           *aux;
+  guchar                           *aux2;
+  guchar                           *output;
+  gint                             *pending;
+  gint                             *started;
+  gint                              level;
+  gboolean                          success;
+  GeglRectangle                     roi;
+
+  guchar                           *in_tmp;
+  guchar                           *aux_tmp;
+  guchar                           *aux2_tmp;
+  guchar                           *output_tmp;
+  const Babl *input_fish;
+  const Babl *aux_fish;
+  const Babl *aux2_fish;
+  const Babl *output_fish;
+} ThreadData;
+
+static void thread_process (gpointer thread_data, gpointer unused)
+{
+  ThreadData *data = thread_data;
+
+  guchar *input = data->input;
+  guchar *aux = data->aux;
+  guchar *aux2 = data->aux2;
+  guchar *output = data->output;
+  glong samples = data->roi.width * data->roi.height;
+
+  if (data->input_fish && input)
+    {
+      babl_process (data->input_fish, data->input, data->in_tmp, samples);
+      input = data->in_tmp;
+    }
+  if (data->aux_fish && aux)
+    {
+      babl_process (data->aux_fish, data->aux, data->aux_tmp, samples);
+      aux = data->aux_tmp;
+    }
+  if (data->aux2_fish && aux2)
+    {
+      babl_process (data->aux2_fish, data->aux2, data->aux2_tmp, samples);
+      aux2 = data->aux2_tmp;
+    }
+  if (data->output_fish)
+    output = data->output_tmp;
+
+  if (!data->klass->process (data->operation,
+                       input, aux, aux2, 
+                       output, samples,
+                       &data->roi, data->level))
+    data->success = FALSE;
+  
+  if (data->output_fish)
+    babl_process (data->output_fish, data->output_tmp, data->output, samples);
+
+  g_atomic_int_add (data->pending, -1);
+}
+
+static GThreadPool *thread_pool (void)
+{
+  static GThreadPool *pool = NULL;
+  if (!pool)
+    {
+      pool =  g_thread_pool_new (thread_process, NULL, gegl_config()->threads,
+                                 FALSE, NULL);
+    }
+  return pool;
+}
+
+static gboolean
+gegl_operation_composer3_process (GeglOperation        *operation,
+                                  GeglOperationContext *context,
+                                  const gchar          *output_prop,
+                                  const GeglRectangle  *result,
+                                  gint                  level)
+{
+  GeglOperationComposer3Class *klass    = GEGL_OPERATION_COMPOSER3_GET_CLASS (operation);
+  GeglOperationClass          *op_class = GEGL_OPERATION_CLASS (klass);
+  GeglBuffer                  *input;
+  GeglBuffer                  *aux;
+  GeglBuffer                  *aux2;
+  GeglBuffer                  *output;
+  gboolean                     success = FALSE;
+
+  if (strcmp (output_prop, "output"))
+    {
+      g_warning ("requested processing of %s pad on a composer", output_prop);
+      return FALSE;
+    }
+
+  if (result->width == 0 || result->height == 0)
+  {
+    output = gegl_operation_context_get_target (context, "output");
+    return TRUE;
+  }
+
+  input = gegl_operation_context_get_source (context, "input");
+
+  if (op_class->want_in_place && 
+      gegl_can_do_inplace_processing (operation, input, result))
+    {
+      output = g_object_ref (input);
+      gegl_operation_context_take_object (context, "output", G_OBJECT (output));
+    }
+  else
+    {
+      output = gegl_operation_context_get_target (context, "output");
+    }
+
+  aux   = gegl_operation_context_get_source (context, "aux");
+  aux2  = gegl_operation_context_get_source (context, "aux2");
+
+  /* A composer with a NULL aux, can still be valid, the
+   * subclass has to handle it.
+   */
+  if (input != NULL ||
+      aux != NULL ||
+      aux2 != NULL)
+    {
+      success = klass->process (operation, input, aux, aux2, output, result, level);
+
+      if (input)
+        g_object_unref (input);
+      if (aux)
+        g_object_unref (aux);
+      if (aux2)
+        g_object_unref (aux2);
+    }
+  else
+    {
+      g_warning ("%s received NULL input, aux, and aux2",
+                 gegl_node_get_operation (operation->node));
+    }
+
+  return success;
+}
+
 static gboolean gegl_operation_point_composer3_process
                               (GeglOperation       *operation,
                                GeglBuffer          *input,
@@ -49,13 +195,13 @@ static void prepare (GeglOperation *operation)
 static void
 gegl_operation_point_composer3_class_init (GeglOperationPointComposer3Class *klass)
 {
-  /*GObjectClass       *object_class    = G_OBJECT_CLASS (klass);*/
-  GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
-  GeglOperationComposer3Class *composer_class = GEGL_OPERATION_COMPOSER3_CLASS (klass);
+  GeglOperationClass          *operation_class = GEGL_OPERATION_CLASS (klass);
+  GeglOperationComposer3Class *composer_class  = GEGL_OPERATION_COMPOSER3_CLASS (klass);
 
   composer_class->process = gegl_operation_point_composer3_process;
+  operation_class->process = gegl_operation_composer3_process;
   operation_class->prepare = prepare;
-  operation_class->no_cache = TRUE;
+  operation_class->no_cache =TRUE;
   operation_class->want_in_place = TRUE;
   operation_class->threaded = TRUE;
 }
@@ -81,50 +227,176 @@ gegl_operation_point_composer3_process (GeglOperation       *operation,
   const Babl *aux2_format = gegl_operation_get_format (operation, "aux2");
   const Babl *out_format  = gegl_operation_get_format (operation, "output");
 
+
   if ((result->width > 0) && (result->height > 0))
     {
-      GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level, out_format, 
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
-      gint read  = gegl_buffer_iterator_add (i, input, result, level, in_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+      const Babl *in_buf_format  = input?gegl_buffer_get_format(input):NULL;
+      const Babl *aux_buf_format = aux?gegl_buffer_get_format(aux):NULL;
+      const Babl *aux2_buf_format = aux2?gegl_buffer_get_format(aux2):NULL;
+      const Babl *output_buf_format = output?gegl_buffer_get_format(output):NULL;
 
-      if (aux)
+      if (gegl_operation_use_threading (operation, result) && result->height > 1)
+      {
+        gint threads = gegl_config ()->threads;
+        GThreadPool *pool = thread_pool ();
+        ThreadData thread_data[GEGL_MAX_THREADS];
+        GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level, output_buf_format, 
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
+        gint foo = 0, bar = 0, read = 0;
+
+        gint in_bpp = input?babl_format_get_bytes_per_pixel (in_format):0;
+        gint aux_bpp = aux?babl_format_get_bytes_per_pixel (aux_format):0;
+        gint aux2_bpp = aux2?babl_format_get_bytes_per_pixel (aux2_format):0;
+        gint out_bpp = babl_format_get_bytes_per_pixel (out_format);
+
+        gint in_buf_bpp = input?babl_format_get_bytes_per_pixel (in_buf_format):0;
+        gint aux_buf_bpp = aux?babl_format_get_bytes_per_pixel (aux_buf_format):0;
+        gint aux2_buf_bpp = aux2?babl_format_get_bytes_per_pixel (aux2_buf_format):0;
+        gint out_buf_bpp = babl_format_get_bytes_per_pixel (output_buf_format);
+
+        gint temp_id = 0;
+
+        if (input)
         {
-          gint foo = gegl_buffer_iterator_add (i, aux, result, level, aux_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
-          if (aux2)
+          read = gegl_buffer_iterator_add (i, input, result, level, in_buf_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+          for (gint j = 0; j < threads; j ++)
+          {
+            if (in_buf_format != in_format)
             {
-              gint bar = gegl_buffer_iterator_add (i, aux2, result, level, aux2_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
-
-              while (gegl_buffer_iterator_next (i))
-                {
-                   point_composer3_class->process (operation, i->data[read], i->data[foo], i->data[bar], 
i->data[0], i->length, &(i->roi[0]), level);
-                }
+              thread_data[j].input_fish = babl_fish (in_buf_format, in_format);
+              thread_data[j].in_tmp = gegl_temp_buffer (temp_id++, in_bpp * result->width * result->height);
             }
-          else
+            else
             {
-              while (gegl_buffer_iterator_next (i))
-                {
-                   point_composer3_class->process (operation, i->data[read], i->data[foo], NULL, i->data[0], 
i->length, &(i->roi[0]), level);
-                }
+              thread_data[j].input_fish = NULL;
             }
+          }
         }
-      else
+        else
+          for (gint j = 0; j < threads; j ++)
+            thread_data[j].input_fish = NULL;
+        if (aux)
         {
-          if (aux2)
+          foo = gegl_buffer_iterator_add (i, aux, result, level, aux_buf_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+          for (gint j = 0; j < threads; j ++)
+          {
+            if (aux_buf_format != aux_format)
             {
-              gint bar = gegl_buffer_iterator_add (i, aux2, result, level, aux2_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
-              while (gegl_buffer_iterator_next (i))
-                {
-                   point_composer3_class->process (operation, i->data[read], NULL, i->data[bar], i->data[0], 
i->length, &(i->roi[0]), level);
-                }
+              thread_data[j].aux_fish = babl_fish (aux_buf_format, aux_format);
+              thread_data[j].aux_tmp = gegl_temp_buffer (temp_id++, aux_bpp * result->width * 
result->height);
             }
-          else
+            else
+            {
+              thread_data[j].aux_fish = NULL;
+            }
+          }
+        }
+        else
+        {
+          for (gint j = 0; j < threads; j ++)
+            thread_data[j].aux_fish = NULL;
+        }
+        if (aux2)
+        {
+          bar = gegl_buffer_iterator_add (i, aux2, result, level, aux2_buf_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+          for (gint j = 0; j < threads; j ++)
+          {
+            if (aux2_buf_format != aux2_format)
             {
-              while (gegl_buffer_iterator_next (i))
-                {
-                   point_composer3_class->process (operation, i->data[read], NULL, NULL, i->data[0], 
i->length, &(i->roi[0]), level);
-                }
+              thread_data[j].aux2_fish = babl_fish (aux2_buf_format, aux2_format);
+              thread_data[j].aux2_tmp = gegl_temp_buffer (temp_id++, aux2_bpp * result->width * 
result->height);
             }
+            else
+            {
+              thread_data[j].aux2_fish = NULL;
+            }
+          }
+        }
+        else
+        {
+          for (gint j = 0; j < threads; j ++)
+            thread_data[j].aux2_fish = NULL;
+        }
+
+        for (gint j = 0; j < threads; j ++)
+        {
+          if (output_buf_format != gegl_buffer_get_format (output))
+          {
+            thread_data[j].output_fish = babl_fish (out_format, output_buf_format);
+            thread_data[j].output_tmp = gegl_temp_buffer (temp_id++, out_bpp * result->width * 
result->height);
+          }
+          else
+          {
+            thread_data[j].output_fish = NULL;
+          }
         }
-      return TRUE;
+
+        while (gegl_buffer_iterator_next (i))
+          {
+            gint threads = gegl_config()->threads;
+            gint pending;
+            gint bit;
+
+            if (i->roi[0].height < threads)
+            {
+              threads = i->roi[0].height;
+            }
+
+            bit = i->roi[0].height / threads;
+            pending = threads;
+
+            for (gint j = 0; j < threads; j++)
+            {
+              thread_data[j].roi.x = (i->roi[0]).x;
+              thread_data[j].roi.width = (i->roi[0]).width;
+              thread_data[j].roi.y = (i->roi[0]).y + bit * j;
+              thread_data[j].roi.height = bit;
+            }
+            thread_data[threads-1].roi.height = i->roi[0].height - (bit * (threads-1));
+            
+            for (gint j = 0; j < threads; j++)
+            {
+              thread_data[j].klass = point_composer3_class;
+              thread_data[j].operation = operation;
+              thread_data[j].input = input?((guchar*)i->data[read]) + (bit * j * i->roi[0].width * 
in_buf_bpp):NULL;
+              thread_data[j].aux = aux?((guchar*)i->data[foo]) + (bit * j * i->roi[0].width * 
aux_buf_bpp):NULL;
+              thread_data[j].aux2 = aux2?((guchar*)i->data[bar]) + (bit * j * i->roi[0].width * 
aux2_buf_bpp):NULL;
+              thread_data[j].output = ((guchar*)i->data[0]) + (bit * j * i->roi[0].width * out_buf_bpp);
+              thread_data[j].pending = &pending;
+              thread_data[j].level = level;
+              thread_data[j].success = TRUE;
+            }
+            pending = threads;
+
+            for (gint j = 1; j < threads; j++)
+              g_thread_pool_push (pool, &thread_data[j], NULL);
+            thread_process (&thread_data[0], NULL);
+
+            while (g_atomic_int_get (&pending)) {};
+          }
+
+        return TRUE;
+      }
+      else
+      {
+        GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level, out_format, 
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
+        gint foo = 0, bar = 0, read = 0;
+
+        if (input)
+          read = gegl_buffer_iterator_add (i, input, result, level, in_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+        if (aux)
+          foo = gegl_buffer_iterator_add (i, aux, result, level, aux_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+        if (aux2)
+          bar = gegl_buffer_iterator_add (i, aux2, result, level, aux2_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+
+        while (gegl_buffer_iterator_next (i))
+          {
+            point_composer3_class->process (operation, input?i->data[read]:NULL,
+                                                       aux?i->data[foo]:NULL,
+                                                       aux2?i->data[bar]:NULL,
+                                                       i->data[0], i->length, &(i->roi[0]), level);
+          }
+        return TRUE;
+      }
     }
   return TRUE;
 }
diff --git a/gegl/operation/gegl-operation-point-filter.c b/gegl/operation/gegl-operation-point-filter.c
index 0fbbf1c..fee7c91 100644
--- a/gegl/operation/gegl-operation-point-filter.c
+++ b/gegl/operation/gegl-operation-point-filter.c
@@ -20,15 +20,125 @@
 #include "config.h"
 
 #include <glib-object.h>
-#include <string.h>
 
 #include "gegl.h"
-#include "gegl/gegl-debug.h"
 #include "gegl-operation-point-filter.h"
 #include "gegl-operation-context.h"
+#include "gegl-config.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+
+typedef struct ThreadData
+{
+  GeglOperationPointFilterClass *klass;
+  GeglOperation                   *operation;
+  guchar                          *input;
+  guchar                          *output;
+  gint                            *pending;
+  gint                            *started;
+  gint                             level;
+  gboolean                         success;
+  GeglRectangle                    roi;
+
+  guchar                          *in_tmp;
+  guchar                          *output_tmp;
+  const Babl *input_fish;
+  const Babl *output_fish;
+} ThreadData;
+
+static void thread_process (gpointer thread_data, gpointer unused)
+{
+  ThreadData *data = thread_data;
+
+  guchar *input = data->input;
+  guchar *output = data->output;
+  glong samples = data->roi.width * data->roi.height;
+
+  if (data->input_fish && input)
+    {
+      babl_process (data->input_fish, data->input, data->in_tmp, samples);
+      input = data->in_tmp;
+    }
+  if (data->output_fish)
+    output = data->output_tmp;
+
+  if (!data->klass->process (data->operation,
+                       input, 
+                       output, samples,
+                       &data->roi, data->level))
+    data->success = FALSE;
+  
+  if (data->output_fish)
+    babl_process (data->output_fish, data->output_tmp, data->output, samples);
+
+  g_atomic_int_add (data->pending, -1);
+}
+
+static GThreadPool *thread_pool (void)
+{
+  static GThreadPool *pool = NULL;
+  if (!pool)
+    {
+      pool =  g_thread_pool_new (thread_process, NULL, gegl_config()->threads,
+                                 FALSE, NULL);
+    }
+  return pool;
+}
+
+static gboolean
+gegl_operation_filter_process (GeglOperation        *operation,
+                                 GeglOperationContext *context,
+                                 const gchar          *output_prop,
+                                 const GeglRectangle  *result,
+                                 gint                  level)
+{
+  GeglOperationFilterClass *klass    = GEGL_OPERATION_FILTER_GET_CLASS (operation);
+  GeglOperationClass         *op_class = GEGL_OPERATION_CLASS (klass);
+  GeglBuffer                 *input;
+  GeglBuffer                 *output;
+  gboolean                    success = FALSE;
+
+  if (strcmp (output_prop, "output"))
+    {
+      g_warning ("requested processing of %s pad on a filter", output_prop);
+      return FALSE;
+    }
+
+  if (result->width == 0 || result->height == 0)
+  {
+    output = gegl_operation_context_get_target (context, "output");
+    return TRUE;
+  }
+
+  input = gegl_operation_context_get_source (context, "input");
+
+  if (op_class->want_in_place && 
+      gegl_can_do_inplace_processing (operation, input, result))
+    {
+      output = g_object_ref (input);
+      gegl_operation_context_take_object (context, "output", G_OBJECT (output));
+    }
+  else
+    {
+      output = gegl_operation_context_get_target (context, "output");
+    }
+
+  if (input != NULL)
+    {
+      success = klass->process (operation, input, output, result, level);
 
-#include "opencl/gegl-cl.h"
-#include "gegl-buffer-cl-iterator.h"
+      if (input)
+        g_object_unref (input);
+    }
+  else
+    {
+      g_warning ("%s received NULL input",
+                 gegl_node_get_operation (operation->node));
+    }
+
+  return success;
+}
 
 static gboolean gegl_operation_point_filter_process
                               (GeglOperation       *operation,
@@ -41,145 +151,153 @@ G_DEFINE_TYPE (GeglOperationPointFilter, gegl_operation_point_filter, GEGL_TYPE_
 
 static void prepare (GeglOperation *operation)
 {
-  gegl_operation_set_format (operation, "input", babl_format ("RGBA float"));
-  gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
+  const Babl *format = babl_format ("RGBA float");
+  gegl_operation_set_format (operation, "input", format);
+  gegl_operation_set_format (operation, "output", format);
 }
 
 static void
 gegl_operation_point_filter_class_init (GeglOperationPointFilterClass *klass)
 {
-  GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+  GeglOperationClass          *operation_class = GEGL_OPERATION_CLASS (klass);
+  GeglOperationFilterClass *filter_class  = GEGL_OPERATION_FILTER_CLASS (klass);
 
-  GEGL_OPERATION_FILTER_CLASS(klass)->process = gegl_operation_point_filter_process;
+  filter_class->process = gegl_operation_point_filter_process;
+  operation_class->process = gegl_operation_filter_process;
   operation_class->prepare = prepare;
-  operation_class->no_cache = TRUE;
+  operation_class->no_cache =TRUE;
   operation_class->want_in_place = TRUE;
-
-  klass->process = NULL;
-  klass->cl_process = NULL;
+  operation_class->threaded = TRUE;
 }
 
 static void
 gegl_operation_point_filter_init (GeglOperationPointFilter *self)
 {
+
 }
 
 static gboolean
-gegl_operation_point_filter_cl_process (GeglOperation       *operation,
-                                        GeglBuffer          *input,
-                                        GeglBuffer          *output,
-                                        const GeglRectangle *result,
-                                        gint                 level)
+gegl_operation_point_filter_process (GeglOperation       *operation,
+                                       GeglBuffer          *input,
+                                       GeglBuffer          *output,
+                                       const GeglRectangle *result,
+                                       gint                 level)
 {
-  const Babl *in_format  = gegl_operation_get_format (operation, "input");
-  const Babl *out_format = gegl_operation_get_format (operation, "output");
-
-  GeglOperationClass *operation_class = GEGL_OPERATION_GET_CLASS (operation);
   GeglOperationPointFilterClass *point_filter_class = GEGL_OPERATION_POINT_FILTER_GET_CLASS (operation);
+  const Babl *in_format   = gegl_operation_get_format (operation, "input");
+  const Babl *out_format  = gegl_operation_get_format (operation, "output");
 
-  GeglBufferClIterator *iter = NULL;
-
-  cl_int cl_err = 0;
-  gboolean err;
 
-  /* non-texturizable format! */
-  if (!gegl_cl_color_babl (in_format,  NULL) ||
-      !gegl_cl_color_babl (out_format, NULL))
+  if ((result->width > 0) && (result->height > 0))
     {
-      GEGL_NOTE (GEGL_DEBUG_OPENCL, "Non-texturizable format!");
-      return FALSE;
-    }
-
-  GEGL_NOTE (GEGL_DEBUG_OPENCL, "GEGL_OPERATION_POINT_FILTER: %s", operation_class->name);
-
-  /* Process */
-  iter = gegl_buffer_cl_iterator_new (output, result, out_format, GEGL_CL_BUFFER_WRITE);
+      const Babl *in_buf_format  = input?gegl_buffer_get_format(input):NULL;
+      const Babl *output_buf_format = output?gegl_buffer_get_format(output):NULL;
 
-  gegl_buffer_cl_iterator_add (iter, input, result, in_format, GEGL_CL_BUFFER_READ, GEGL_ABYSS_NONE);
+      if (gegl_operation_use_threading (operation, result) && result->height > 1)
+      {
+        gint threads = gegl_config ()->threads;
+        GThreadPool *pool = thread_pool ();
+        ThreadData thread_data[GEGL_MAX_THREADS];
+        GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level, output_buf_format, 
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
+        gint read = 0;
 
-  while (gegl_buffer_cl_iterator_next (iter, &err))
-    {
-      if (err)
-        return FALSE;
+        gint in_bpp = input?babl_format_get_bytes_per_pixel (in_format):0;
+        gint out_bpp = babl_format_get_bytes_per_pixel (out_format);
+        gint in_buf_bpp = input?babl_format_get_bytes_per_pixel (in_buf_format):0;
+        gint out_buf_bpp = babl_format_get_bytes_per_pixel (output_buf_format);
+        gint temp_id = 0;
 
-      if (point_filter_class->cl_process)
+        if (input)
         {
-          err = point_filter_class->cl_process (operation, iter->tex[1], iter->tex[0],
-                                                iter->size[0], &iter->roi[0], level);
-          if (err)
+          read = gegl_buffer_iterator_add (i, input, result, level, in_buf_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+          for (gint j = 0; j < threads; j ++)
+          {
+            if (in_buf_format != in_format)
             {
-              GEGL_NOTE (GEGL_DEBUG_OPENCL, "Error: %s", operation_class->name);
-              gegl_buffer_cl_iterator_stop (iter);
-              return FALSE;
+              thread_data[j].input_fish = babl_fish (in_buf_format, in_format);
+              thread_data[j].in_tmp = gegl_temp_buffer (temp_id++, in_bpp * result->width * result->height);
             }
+            else
+            {
+              thread_data[j].input_fish = NULL;
+            }
+          }
         }
-      else if (operation_class->cl_data)
-        {
-          gint p = 0;
-          GeglClRunData *cl_data = operation_class->cl_data;
-
-          cl_err = gegl_clSetKernelArg (cl_data->kernel[0], p++, sizeof(cl_mem), (void*)&iter->tex[1]);
-          CL_CHECK;
-          cl_err = gegl_clSetKernelArg (cl_data->kernel[0], p++, sizeof(cl_mem), (void*)&iter->tex[0]);
-          CL_CHECK;
-
-          gegl_operation_cl_set_kernel_args (operation, cl_data->kernel[0], &p, &cl_err);
-          CL_CHECK;
-
-          cl_err = gegl_clEnqueueNDRangeKernel (gegl_cl_get_command_queue (),
-                                                cl_data->kernel[0], 1,
-                                                NULL, &iter->size[0], NULL,
-                                                0, NULL, NULL);
-          CL_CHECK;
-        }
-      else
+        else
+          for (gint j = 0; j < threads; j ++)
+            thread_data[j].input_fish = NULL;
+
+        for (gint j = 0; j < threads; j ++)
         {
-          g_warning ("OpenCL support enabled, but no way to execute");
-          gegl_buffer_cl_iterator_stop (iter);
-          return FALSE;
+          if (output_buf_format != gegl_buffer_get_format (output))
+          {
+            thread_data[j].output_fish = babl_fish (out_format, output_buf_format);
+            thread_data[j].output_tmp = gegl_temp_buffer (temp_id++, out_bpp * result->width * 
result->height);
+          }
+          else
+          {
+            thread_data[j].output_fish = NULL;
+          }
         }
-    }
 
-  return TRUE;
+        while (gegl_buffer_iterator_next (i))
+          {
+            gint threads = gegl_config()->threads;
+            gint pending;
+            gint bit;
 
-error:
-  GEGL_NOTE (GEGL_DEBUG_OPENCL, "Error: %s", gegl_cl_errstring (cl_err));
-  if (iter)
-    gegl_buffer_cl_iterator_stop (iter);
-  return FALSE;
-}
+            if (i->roi[0].height < threads)
+            {
+              threads = i->roi[0].height;
+            }
 
-static gboolean
-gegl_operation_point_filter_process (GeglOperation       *operation,
-                                     GeglBuffer          *input,
-                                     GeglBuffer          *output,
-                                     const GeglRectangle *result,
-                                     gint                 level)
-{
-  const Babl *in_format  = gegl_operation_get_format (operation, "input");
-  const Babl *out_format = gegl_operation_get_format (operation, "output");
-  GeglOperationPointFilterClass *point_filter_class;
+            bit = i->roi[0].height / threads;
+            pending = threads;
 
-  GeglOperationClass *operation_class = GEGL_OPERATION_GET_CLASS (operation);
-  point_filter_class = GEGL_OPERATION_POINT_FILTER_GET_CLASS (operation);
+            for (gint j = 0; j < threads; j++)
+            {
+              thread_data[j].roi.x = (i->roi[0]).x;
+              thread_data[j].roi.width = (i->roi[0]).width;
+              thread_data[j].roi.y = (i->roi[0]).y + bit * j;
+              thread_data[j].roi.height = bit;
+            }
+            thread_data[threads-1].roi.height = i->roi[0].height - (bit * (threads-1));
+            
+            for (gint j = 0; j < threads; j++)
+            {
+              thread_data[j].klass = point_filter_class;
+              thread_data[j].operation = operation;
+              thread_data[j].input = input?((guchar*)i->data[read]) + (bit * j * i->roi[0].width * 
in_buf_bpp):NULL;
+              thread_data[j].output = ((guchar*)i->data[0]) + (bit * j * i->roi[0].width * out_buf_bpp);
+              thread_data[j].pending = &pending;
+              thread_data[j].level = level;
+              thread_data[j].success = TRUE;
+            }
+            pending = threads;
 
-  if ((result->width > 0) && (result->height > 0))
-    {
-      if (gegl_operation_use_opencl (operation) &&
-          (operation_class->cl_data || point_filter_class->cl_process))
-        {
-          if (gegl_operation_point_filter_cl_process (operation, input, output, result, level))
-            return TRUE;
-        }
+            for (gint j = 1; j < threads; j++)
+              g_thread_pool_push (pool, &thread_data[j], NULL);
+            thread_process (&thread_data[0], NULL);
+
+            while (g_atomic_int_get (&pending)) {};
+          }
 
+        return TRUE;
+      }
+      else
       {
         GeglBufferIterator *i = gegl_buffer_iterator_new (output, result, level, out_format, 
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
-        gint read = /*output == input ? 0 :*/ gegl_buffer_iterator_add (i, input,  result, level, in_format, 
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
-        /* using separate read and write iterators for in-place ideally a single
-         * readwrite indice would be sufficient
-         */
-          while (gegl_buffer_iterator_next (i))
-            point_filter_class->process (operation, i->data[read], i->data[0], i->length, &i->roi[0], level);
+        gint read = 0;
+
+        if (input)
+          read = gegl_buffer_iterator_add (i, input, result, level, in_format, GEGL_BUFFER_READ, 
GEGL_ABYSS_NONE);
+
+        while (gegl_buffer_iterator_next (i))
+          {
+            point_filter_class->process (operation, input?i->data[read]:NULL,
+                                                    i->data[0], i->length, &(i->roi[0]), level);
+          }
+        return TRUE;
       }
     }
   return TRUE;
diff --git a/gegl/operation/gegl-operation-point-render.c b/gegl/operation/gegl-operation-point-render.c
index e46c2db..01c5daa 100644
--- a/gegl/operation/gegl-operation-point-render.c
+++ b/gegl/operation/gegl-operation-point-render.c
@@ -60,6 +60,7 @@ gegl_operation_point_render_class_init (GeglOperationPointRenderClass *klass)
   operation_class->get_cached_region = NULL; /* we are able to compute anything
                                                  anywhere when we're our kind
                                                  of class */
+  operation_class->threaded = TRUE;
 }
 
 static void



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