filters: Add initial version of multithreaded linear convolution.
[gfxprim.git] / libs / filters / GP_LinearThreads.c
blob5ccdebc9fadb6e3aab8885cb8e3a1e8a51cfb523
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
23 #include <unistd.h>
24 #include <pthread.h>
25 #include <string.h>
27 #include "core/GP_Common.h"
28 #include "core/GP_Debug.h"
30 #include "GP_Linear.h"
31 #include "GP_LinearThreads.h"
33 static int nr_threads(GP_Size w, GP_Size h)
35 int count = sysconf(_SC_NPROCESSORS_ONLN);
36 int threads = GP_MIN(count, (int)(w * h / 1024) + 1);
38 if (count < -1)
39 threads = 1;
41 GP_DEBUG(1, "Found %u CPUs size %ux%u runnig %u threads",
42 count, w, h, threads);
44 return threads;
48 * Multithreaded callback wrapper.
50 struct callback_priv {
51 float max;
52 int abort;
53 unsigned int nr_threads;
54 pthread_mutex_t mutex;
55 GP_ProgressCallback *orig_callback;
58 static int progress_callback_mp(GP_ProgressCallback *self)
60 struct callback_priv *priv = self->priv;
62 /* If any thread got non-zero return value from a callback, abort all */
63 if (priv->abort)
64 return 1;
66 if (pthread_mutex_trylock(&priv->mutex)) {
67 GP_DEBUG(1, "Mutex locked, skipping calllback.");
68 return 0;
71 /* Compute max value for the percentage */
72 priv->orig_callback->percentage = GP_MAX(self->percentage, priv->max);
73 priv->max = priv->orig_callback->percentage;
75 /* Call the original callback */
76 int ret = priv->orig_callback->callback(priv->orig_callback);
78 /* Turn on abort flag if callback returned nonzero */
79 if (ret)
80 priv->abort = 1;
82 pthread_mutex_unlock(&priv->mutex);
84 return ret;
87 static void callback_priv_init(struct callback_priv *priv, int nr_threads,
88 GP_ProgressCallback *orig_callback)
90 priv->nr_threads = nr_threads;
91 priv->orig_callback = orig_callback;
92 priv->max = 0;
93 priv->abort = 0;
94 pthread_mutex_init(&priv->mutex, NULL);
97 static void callback_priv_exit(struct callback_priv *priv)
99 pthread_mutex_destroy(&priv->mutex);
103 * This code just packs and unpacks convolution parameters.
105 struct convolution {
106 const GP_Context *src;
107 GP_Coord x_src;
108 GP_Coord y_src;
109 GP_Size w_src;
110 GP_Size h_src;
112 GP_Context *dst;
113 GP_Coord x_dst;
114 GP_Coord y_dst;
116 float *kernel;
117 unsigned int ks;
118 float kern_div;
120 GP_ProgressCallback *callback;
123 static void convolution_init(struct convolution *conv, const GP_Context *src,
124 GP_Coord x_src, GP_Coord y_src,
125 GP_Size w_src, GP_Size h_src,
126 GP_Context *dst, GP_Coord x_dst, GP_Coord y_dst,
127 float kernel[], unsigned int ks, float kern_div,
128 GP_ProgressCallback *callback)
130 conv->src = src;
131 conv->x_src = x_src;
132 conv->y_src = y_src;
133 conv->w_src = w_src;
134 conv->h_src = h_src;
135 conv->dst = dst;
136 conv->x_dst = x_dst;
137 conv->y_dst = y_dst;
138 conv->kernel = kernel;
139 conv->ks = ks;
140 conv->kern_div = kern_div;
141 conv->callback = callback;
144 static void *h_linear_convolution(void *arg)
146 struct convolution *conv = arg;
148 long ret;
150 ret = GP_FilterHLinearConvolution_Raw(conv->src, conv->x_src, conv->y_src,
151 conv->w_src, conv->h_src, conv->dst,
152 conv->x_dst, conv->y_dst, conv->kernel,
153 conv->ks, conv->kern_div, conv->callback);
154 return (void*)ret;
157 static void *v_linear_convolution(void *arg)
159 struct convolution *conv = arg;
161 long ret;
163 ret = GP_FilterVLinearConvolution_Raw(conv->src, conv->x_src, conv->y_src,
164 conv->w_src, conv->h_src, conv->dst,
165 conv->x_dst, conv->y_dst, conv->kernel,
166 conv->ks, conv->kern_div, conv->callback);
167 return (void*)ret;
171 int GP_FilterHLinearConvolutionMP_Raw(const GP_Context *src,
172 GP_Coord x_src, GP_Coord y_src,
173 GP_Size w_src, GP_Size h_src,
174 GP_Context *dst,
175 GP_Coord x_dst, GP_Coord y_dst,
176 float kernel[], uint32_t kw, float kern_div,
177 GP_ProgressCallback *callback)
179 int t = nr_threads(w_src, h_src);
181 if (t == 1) {
182 return GP_FilterHLinearConvolution_Raw(src, x_src, y_src,
183 w_src, h_src,
184 dst, x_dst, y_dst,
185 kernel, kw, kern_div,
186 callback);
189 /* Create multithreaded safe callback on the stack */
190 struct callback_priv priv;
191 callback_priv_init(&priv, t, callback);
192 GP_ProgressCallback callback_mp = {0, progress_callback_mp, &priv};
194 /* Run t threads */
195 int i;
196 pthread_t threads[t];
197 struct convolution convs[t];
198 GP_Size h = h_src/t;
200 for (i = 0; i < t; i++) {
201 GP_Coord y_src_2 = y_src + i * h;
202 GP_Coord y_dst_2 = y_dst + i * h;
203 GP_Size h_src_2 = h;
205 if (i == t - 1)
206 h_src_2 = h_src - i * h;
208 /* Pack convolution parameters into the structure */
209 convolution_init(&convs[i], src, x_src, y_src_2, w_src, h_src_2,
210 dst, x_dst, y_dst_2, kernel, kw, kern_div,
211 callback ? &callback_mp : NULL);
213 pthread_create(&threads[i], NULL, h_linear_convolution, &convs[i]);
216 int ret = 0;
218 for (i = 0; i < t; i++) {
219 long r;
221 pthread_join(threads[i], (void*)&r);
223 ret |= (int)r;
226 callback_priv_exit(&priv);
228 return ret;
232 int GP_FilterVLinearConvolutionMP_Raw(const GP_Context *src,
233 GP_Coord x_src, GP_Coord y_src,
234 GP_Size w_src, GP_Size h_src,
235 GP_Context *dst,
236 GP_Coord x_dst, GP_Coord y_dst,
237 float kernel[], uint32_t kh, float kern_div,
238 GP_ProgressCallback *callback)
240 int t = nr_threads(w_src, h_src);
242 if (t == 1) {
243 return GP_FilterVLinearConvolution_Raw(src, x_src, y_src,
244 w_src, h_src,
245 dst, x_dst, y_dst,
246 kernel, kh, kern_div,
247 callback);
250 /* Create multithreaded safe callback on the stack */
251 struct callback_priv priv;
252 callback_priv_init(&priv, t, callback);
253 GP_ProgressCallback callback_mp = {0, progress_callback_mp, &priv};
255 int i;
256 pthread_t threads[t];
257 struct convolution convs[t];
258 GP_Size h = h_src/t;
260 for (i = 0; i < t; i++) {
261 GP_Coord y_src_2 = y_src + i * h;
262 GP_Coord y_dst_2 = y_dst + i * h;
263 GP_Size h_src_2 = h;
265 if (i == t - 1)
266 h_src_2 = h_src - i * h;
268 /* Pack convolution parameters into the structure */
269 convolution_init(&convs[i], src, x_src, y_src_2, w_src, h_src_2,
270 dst, x_dst, y_dst_2, kernel, kh, kern_div,
271 callback ? &callback_mp : NULL);
273 pthread_create(&threads[i], NULL, v_linear_convolution, &convs[i]);
276 int ret = 0;
278 for (i = 0; i < t; i++) {
279 long r;
280 pthread_join(threads[i], (void*)&r);
282 ret |= (int)r;
285 callback_priv_exit(&priv);
287 return ret;
291 int GP_FilterLinearConvolutionMP_Raw(const GP_Context *src,
292 GP_Coord x_src, GP_Coord y_src,
293 GP_Size w_src, GP_Size h_src,
294 GP_Context *dst,
295 GP_Coord x_dst, GP_Coord y_dst,
296 float kernel[], uint32_t kw, uint32_t kh,
297 float kern_div, GP_ProgressCallback *callback)