[gimp] app: add gimp-parallel



commit 86b89cf62a14b98056355d0c8ad2b27e29b3a56f
Author: Ell <ell_se yahoo com>
Date:   Wed Apr 4 15:16:42 2018 -0400

    app: add gimp-parallel
    
    Add gimp-parallel.[cc,h], which provides a set of parallel
    algorithms.
    
    These currently include:
    
      - gimp_parallel_distribute():  Calls a callback function in
        parallel on multiple threads, passing it the current thread
        index, and the total number of threads.  Allows specifying the
        maximal number of threads used.
    
      - gimp_parallel_distribute_range():  Splits a range of integers
        between multiple threads, passing the sub-range to a callback
        function.  Allows specifying the minimal sub-range size.
    
      - gimp_parallel_distribute_area():  Splits a rectangular area
        between multiple threads, passing the sub-area to a callback
        function.  Allows specifying the minimal sub-area.
    
    The callback function is passed using an appropriately-typed
    function pointer, and a user-data pointer.  Additionally, when used
    in a C++ file, each of the above functions has an overloaded
    template version, taking the callback through a generic parameter,
    without a user-data pointer, which allows using function objects.

 app/Makefile.am           |    4 +-
 app/app.c                 |    2 +
 app/core/Makefile.am      |    2 +
 app/core/gimp-parallel.cc |  355 +++++++++++++++++++++++++++++++++++++++++++++
 app/core/gimp-parallel.h  |  112 ++++++++++++++
 app/gegl/gimp-gegl.c      |   11 ++
 app/gegl/gimp-gegl.h      |    1 +
 7 files changed, 486 insertions(+), 1 deletions(-)
---
diff --git a/app/Makefile.am b/app/Makefile.am
index 605a270..f0a9536 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -127,7 +127,9 @@ AM_LDFLAGS = \
        -Wl,-u,$(SYMPREFIX)internal_procs_init                  \
        -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore         \
        -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec           \
-       -Wl,-u,$(SYMPREFIX)gimp_layer_mode_is_legacy
+       -Wl,-u,$(SYMPREFIX)gimp_layer_mode_is_legacy            \
+       -Wl,-u,$(SYMPREFIX)gimp_parallel_init                   \
+       -Wl,-u,$(SYMPREFIX)gimp_parallel_exit
 
 gimpconsoleldadd = \
        xcf/libappxcf.a                                         \
diff --git a/app/app.c b/app/app.c
index 06044a0..26d7b4c 100644
--- a/app/app.c
+++ b/app/app.c
@@ -427,6 +427,8 @@ app_run (const gchar         *full_prog_name,
 
   g_main_loop_unref (loop);
 
+  gimp_gegl_exit (gimp);
+
   g_object_unref (gimp);
 
   gimp_debug_instances ();
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index 0e19108..c3dbeb4 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -52,6 +52,8 @@ libappcore_a_sources = \
        gimp-modules.h                          \
        gimp-palettes.c                         \
        gimp-palettes.h                         \
+       gimp-parallel.cc                        \
+       gimp-parallel.h                         \
        gimp-parasites.c                        \
        gimp-parasites.h                        \
        gimp-spawn.c                            \
diff --git a/app/core/gimp-parallel.cc b/app/core/gimp-parallel.cc
new file mode 100644
index 0000000..82a9c86
--- /dev/null
+++ b/app/core/gimp-parallel.cc
@@ -0,0 +1,355 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-parallel.c
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gegl.h>
+
+extern "C"
+{
+
+#include "core-types.h"
+
+#include "config/gimpgeglconfig.h"
+
+#include "gimp.h"
+#include "gimp-parallel.h"
+
+
+#define GIMP_PARALLEL_MAX_THREADS 64
+
+
+typedef struct
+{
+  GimpParallelDistributeFunc func;
+  gint                       n;
+  gpointer                   user_data;
+} GimpParallelTask;
+
+typedef struct
+{
+  GThread          *thread;
+  GMutex            mutex;
+  GCond             cond;
+
+  gboolean          quit;
+
+  GimpParallelTask *volatile task;
+  volatile gint     i;
+} GimpParallelThread;
+
+
+/*  local function prototypes  */
+
+static void       gimp_parallel_notify_num_processors (GimpGeglConfig     *config);
+
+static void       gimp_parallel_set_n_threads         (gint                n_threads);
+static gpointer   gimp_parallel_thread_func           (GimpParallelThread *thread);
+
+
+/*  local variables  */
+
+static gint               gimp_parallel_n_threads = 1;
+static GimpParallelThread gimp_parallel_threads[GIMP_PARALLEL_MAX_THREADS];
+
+static GMutex             gimp_parallel_completion_mutex;
+static GCond              gimp_parallel_completion_cond;
+static volatile gint      gimp_parallel_completion_counter;
+static volatile gint      gimp_parallel_busy;
+
+
+/*  public functions  */
+
+
+void
+gimp_parallel_init (Gimp *gimp)
+{
+  GimpGeglConfig *config;
+
+  g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+  config = GIMP_GEGL_CONFIG (gimp->config);
+
+  g_signal_connect (config, "notify::num-processors",
+                    G_CALLBACK (gimp_parallel_notify_num_processors),
+                    NULL);
+
+  gimp_parallel_notify_num_processors (config);
+}
+
+void
+gimp_parallel_exit (Gimp *gimp)
+{
+  g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+  g_signal_handlers_disconnect_by_func (gimp->config,
+                                        (gpointer) gimp_parallel_notify_num_processors,
+                                        NULL);
+
+  /* stop all threads */
+  gimp_parallel_set_n_threads (1);
+}
+
+void
+gimp_parallel_distribute (gint                       max_n,
+                          GimpParallelDistributeFunc func,
+                          gpointer                   user_data)
+{
+  GimpParallelTask task;
+  gint             i;
+
+  g_return_if_fail (func != NULL);
+
+  if (max_n == 0)
+    return;
+
+  if (max_n < 0)
+    max_n = gimp_parallel_n_threads;
+  else
+    max_n = MIN (max_n, gimp_parallel_n_threads);
+
+  if (max_n == 1 ||
+      ! g_atomic_int_compare_and_exchange (&gimp_parallel_busy, 0, 1))
+    {
+      func (0, 1, user_data);
+
+      return;
+    }
+
+  task.n         = max_n;
+  task.func      = func;
+  task.user_data = user_data;
+
+  g_atomic_int_set (&gimp_parallel_completion_counter, task.n - 1);
+
+  for (i = 0; i < task.n - 1; i++)
+    {
+      GimpParallelThread *thread = &gimp_parallel_threads[i];
+
+      g_mutex_lock (&thread->mutex);
+
+      thread->task = &task;
+      thread->i    = i;
+
+      g_cond_signal (&thread->cond);
+
+      g_mutex_unlock (&thread->mutex);
+    }
+
+  func (i, task.n, user_data);
+
+  if (g_atomic_int_get (&gimp_parallel_completion_counter))
+    {
+      g_mutex_lock (&gimp_parallel_completion_mutex);
+
+      while (g_atomic_int_get (&gimp_parallel_completion_counter))
+        {
+          g_cond_wait (&gimp_parallel_completion_cond,
+                       &gimp_parallel_completion_mutex);
+        }
+
+      g_mutex_unlock (&gimp_parallel_completion_mutex);
+    }
+
+  g_atomic_int_set (&gimp_parallel_busy, 0);
+}
+
+void
+gimp_parallel_distribute_range (gsize                           size,
+                                gsize                           min_sub_size,
+                                GimpParallelDistributeRangeFunc func,
+                                gpointer                        user_data)
+{
+  gsize n = size;
+
+  g_return_if_fail (func != NULL);
+
+  if (size == 0)
+    return;
+
+  if (min_sub_size > 1)
+    n /= min_sub_size;
+
+  n = CLAMP (n, 1, gimp_parallel_n_threads);
+
+  gimp_parallel_distribute (n, [=] (gint i, gint n)
+    {
+      gsize offset;
+      gsize sub_size;
+
+      offset   = (2 * i       * size + n) / (2 * n);
+      sub_size = (2 * (i + 1) * size + n) / (2 * n) - offset;
+
+      func (offset, sub_size, user_data);
+    });
+}
+
+void
+gimp_parallel_distribute_area (const GeglRectangle            *area,
+                               gsize                           min_sub_area,
+                               GimpParallelDistributeAreaFunc  func,
+                               gpointer                        user_data)
+{
+  gsize n;
+
+  g_return_if_fail (area != NULL);
+  g_return_if_fail (func != NULL);
+
+  if (area->width <= 0 || area->height <= 0)
+    return;
+
+  n = (gsize) area->width * (gsize) area->height;
+
+  if (min_sub_area > 1)
+    n /= min_sub_area;
+
+  n = CLAMP (n, 1, gimp_parallel_n_threads);
+
+  gimp_parallel_distribute (n, [=] (gint i, gint n)
+    {
+      GeglRectangle sub_area;
+
+      if (area->width <= area->height)
+        {
+          sub_area.x       = area->x;
+          sub_area.width   = area->width;
+
+          sub_area.y       = (2 * i       * area->height + n) / (2 * n);
+          sub_area.height  = (2 * (i + 1) * area->height + n) / (2 * n);
+
+          sub_area.height -= sub_area.y;
+          sub_area.y      += area->y;
+        }
+      else
+        {
+          sub_area.y       = area->y;
+          sub_area.height  = area->height;
+
+          sub_area.x       = (2 * i       * area->width + n) / (2 * n);
+          sub_area.width   = (2 * (i + 1) * area->width + n) / (2 * n);
+
+          sub_area.width  -= sub_area.x;
+          sub_area.x      += area->x;
+        }
+
+      func (&sub_area, user_data);
+    });
+}
+
+
+/*  private functions  */
+
+
+static void
+gimp_parallel_notify_num_processors (GimpGeglConfig *config)
+{
+  gimp_parallel_set_n_threads (config->num_processors);
+}
+
+static void
+gimp_parallel_set_n_threads (gint n_threads)
+{
+  gint i;
+
+  if (! g_atomic_int_compare_and_exchange (&gimp_parallel_busy, 0, 1))
+    g_return_if_reached ();
+
+  n_threads = CLAMP (n_threads, 1, GIMP_PARALLEL_MAX_THREADS + 1);
+
+  if (n_threads > gimp_parallel_n_threads) /* need more threads */
+    {
+      for (i = gimp_parallel_n_threads - 1; i < n_threads - 1; i++)
+        {
+          GimpParallelThread *thread = &gimp_parallel_threads[i];
+
+          thread->quit = FALSE;
+          thread->task = NULL;
+
+          thread->thread = g_thread_new ("worker",
+                                         (GThreadFunc) gimp_parallel_thread_func,
+                                         thread);
+        }
+    }
+  else if (n_threads < gimp_parallel_n_threads) /* need less threads */
+    {
+      for (i = n_threads - 1; i < gimp_parallel_n_threads - 1; i++)
+        {
+          GimpParallelThread *thread = &gimp_parallel_threads[i];
+
+          g_mutex_lock (&thread->mutex);
+
+          thread->quit = TRUE;
+          g_cond_signal (&thread->cond);
+
+          g_mutex_unlock (&thread->mutex);
+        }
+
+      for (i = n_threads - 1; i < gimp_parallel_n_threads - 1; i++)
+        {
+          GimpParallelThread *thread = &gimp_parallel_threads[i];
+
+          g_thread_join (thread->thread);
+        }
+    }
+
+  gimp_parallel_n_threads = n_threads;
+
+  g_atomic_int_set (&gimp_parallel_busy, 0);
+}
+
+static gpointer
+gimp_parallel_thread_func (GimpParallelThread *thread)
+{
+  g_mutex_lock (&thread->mutex);
+
+  while (TRUE)
+    {
+      g_cond_wait (&thread->cond, &thread->mutex);
+
+      if (thread->quit)
+        {
+          break;
+        }
+      else if (thread->task)
+        {
+          thread->task->func (thread->i, thread->task->n,
+                              thread->task->user_data);
+
+          if (g_atomic_int_dec_and_test (&gimp_parallel_completion_counter))
+            {
+              g_mutex_lock (&gimp_parallel_completion_mutex);
+
+              g_cond_signal (&gimp_parallel_completion_cond);
+
+              g_mutex_unlock (&gimp_parallel_completion_mutex);
+            }
+
+          thread->task = NULL;
+        }
+    }
+
+  g_mutex_unlock (&thread->mutex);
+
+  return NULL;
+}
+
+} /* extern "C" */
diff --git a/app/core/gimp-parallel.h b/app/core/gimp-parallel.h
new file mode 100644
index 0000000..3c93526
--- /dev/null
+++ b/app/core/gimp-parallel.h
@@ -0,0 +1,112 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-parallel.h
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PARALLEL_H__
+#define __GIMP_PARALLEL_H__
+
+
+typedef void (* GimpParallelDistributeFunc)      (gint                 i,
+                                                  gint                 n,
+                                                  gpointer             user_data);
+typedef void (* GimpParallelDistributeRangeFunc) (gsize                offset,
+                                                  gsize                size,
+                                                  gpointer             user_data);
+typedef void (* GimpParallelDistributeAreaFunc)  (const GeglRectangle *area,
+                                                  gpointer             user_data);
+
+
+void   gimp_parallel_init             (Gimp                            *gimp);
+void   gimp_parallel_exit             (Gimp                            *gimp);
+
+void   gimp_parallel_distribute       (gint                             max_n,
+                                       GimpParallelDistributeFunc       func,
+                                       gpointer                         user_data);
+void   gimp_parallel_distribute_range (gsize                            size,
+                                       gsize                            min_sub_size,
+                                       GimpParallelDistributeRangeFunc  func,
+                                       gpointer                         user_data);
+void   gimp_parallel_distribute_area  (const GeglRectangle             *area,
+                                       gsize                            min_sub_area,
+                                       GimpParallelDistributeAreaFunc   func,
+                                       gpointer                         user_data);
+
+#ifdef __cplusplus
+
+extern "C++"
+{
+
+template <class ParallelDistributeFunc>
+inline void
+gimp_parallel_distribute (gint                   max_n,
+                          ParallelDistributeFunc func)
+{
+  gimp_parallel_distribute (max_n,
+                            [] (gint     i,
+                                gint     n,
+                                gpointer user_data)
+                            {
+                              ParallelDistributeFunc func_copy (
+                                *(const ParallelDistributeFunc *) user_data);
+
+                              func_copy (i, n);
+                            }, &func);
+}
+
+template <class ParallelDistributeRangeFunc>
+inline void
+gimp_parallel_distribute_range (gsize                       size,
+                                gsize                       min_sub_size,
+                                ParallelDistributeRangeFunc func)
+{
+  gimp_parallel_distribute_range (size, min_sub_size,
+                                  [] (gsize    offset,
+                                      gsize    size,
+                                      gpointer user_data)
+                                  {
+                                    ParallelDistributeRangeFunc func_copy (
+                                      *(const ParallelDistributeRangeFunc *) user_data);
+
+                                    func_copy (offset, size);
+                                  }, &func);
+}
+
+template <class ParallelDistributeAreaFunc>
+inline void
+gimp_parallel_distribute_area (const GeglRectangle        *area,
+                               gsize                       min_sub_area,
+                               ParallelDistributeAreaFunc  func)
+{
+  gimp_parallel_distribute_area (area, min_sub_area,
+                                 [] (const GeglRectangle *area,
+                                     gpointer             user_data)
+                                 {
+                                   ParallelDistributeAreaFunc func_copy (
+                                     *(const ParallelDistributeAreaFunc *) user_data);
+
+                                   func_copy (area);
+                                 }, &func);
+}
+
+}
+
+#endif /* __cplusplus */
+
+
+#endif /* __GIMP_PARALLEL_H__ */
diff --git a/app/gegl/gimp-gegl.c b/app/gegl/gimp-gegl.c
index 5028095..48fea1c 100644
--- a/app/gegl/gimp-gegl.c
+++ b/app/gegl/gimp-gegl.c
@@ -30,6 +30,7 @@
 #include "operations/gimp-operations.h"
 
 #include "core/gimp.h"
+#include "core/gimp-parallel.h"
 
 #include "gimp-babl.h"
 #include "gimp-gegl.h"
@@ -66,11 +67,21 @@ gimp_gegl_init (Gimp *gimp)
                     G_CALLBACK (gimp_gegl_notify_use_opencl),
                     NULL);
 
+  gimp_parallel_init (gimp);
+
   gimp_babl_init ();
 
   gimp_operations_init (gimp);
 }
 
+void
+gimp_gegl_exit (Gimp *gimp)
+{
+  g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+  gimp_parallel_exit (gimp);
+}
+
 static void
 gimp_gegl_notify_tile_cache_size (GimpGeglConfig *config)
 {
diff --git a/app/gegl/gimp-gegl.h b/app/gegl/gimp-gegl.h
index 532f178..e14f49d 100644
--- a/app/gegl/gimp-gegl.h
+++ b/app/gegl/gimp-gegl.h
@@ -23,6 +23,7 @@
 
 
 void   gimp_gegl_init (Gimp *gimp);
+void   gimp_gegl_exit (Gimp *gimp);
 
 
 #endif /* __GIMP_GEGL_H__ */


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