[gimp] app: add gimp-parallel
- From: N/A <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: add gimp-parallel
- Date: Wed, 4 Apr 2018 21:53:53 +0000 (UTC)
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]