1 /* This file is part of GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2012 Victor Oliveira (victormatheus@gmail.com)
17  */
18 
19 #include "config.h"
20 #include <stdlib.h>
21 #include <string.h>
22 #include <math.h>
23 
24 #include <glib-object.h>
25 #include <glib/gprintf.h>
26 
27 #include "gegl.h"
28 #include "gegl/gegl-debug.h"
29 #include "gegl-buffer-types.h"
30 #include "gegl-buffer-cl-iterator.h"
31 #include "gegl-buffer-cl-cache.h"
32 #include "gegl-buffer-private.h"
33 #include "gegl-tile-storage.h"
34 
35 #include "opencl/gegl-cl.h"
36 
37 typedef struct GeglBufferClIterators
38 {
39   /* current region of interest */
40   size_t        size [GEGL_CL_BUFFER_MAX_ITERATORS];  /* length of current data in pixels */
41   cl_mem        tex  [GEGL_CL_BUFFER_MAX_ITERATORS];
42   GeglRectangle roi  [GEGL_CL_BUFFER_MAX_ITERATORS];
43 
44   /* the following is private: */
45   cl_mem        tex_buf [GEGL_CL_BUFFER_MAX_ITERATORS];
46   cl_mem        tex_op  [GEGL_CL_BUFFER_MAX_ITERATORS];
47 
48   /* don't free textures loaded from cache */
49   gboolean       tex_buf_from_cache [GEGL_CL_BUFFER_MAX_ITERATORS];
50 
51   gint           iterators;
52   gint           iteration_no;
53   gboolean       is_finished;
54 
55   guint          flags          [GEGL_CL_BUFFER_MAX_ITERATORS];
56   gint           area           [GEGL_CL_BUFFER_MAX_ITERATORS][4];
57 
58   GeglRectangle  rect           [GEGL_CL_BUFFER_MAX_ITERATORS]; /* the region we iterate on. They can be
59                                                                    different from each other, but width
60                                                                    and height are the same */
61 
62   const Babl    *format         [GEGL_CL_BUFFER_MAX_ITERATORS]; /* The format required for the data */
63   GeglBuffer    *buffer         [GEGL_CL_BUFFER_MAX_ITERATORS];
64 
65   /* GeglBuffer's buffer */
66   size_t buf_cl_format_size     [GEGL_CL_BUFFER_MAX_ITERATORS];
67   /* Iterator's format */
68   size_t op_cl_format_size      [GEGL_CL_BUFFER_MAX_ITERATORS];
69 
70   GeglClColorOp conv            [GEGL_CL_BUFFER_MAX_ITERATORS];
71 
72   GeglAbyssPolicy abyss_policy  [GEGL_CL_BUFFER_MAX_ITERATORS];
73 
74   /* total iteration */
75   gint           rois;
76   GeglRectangle *roi_all;
77 
78 } GeglBufferClIterators;
79 
80 gint
gegl_buffer_cl_iterator_add_2(GeglBufferClIterator * iterator,GeglBuffer * buffer,const GeglRectangle * result,const Babl * format,guint flags,gint left,gint right,gint top,gint bottom,GeglAbyssPolicy abyss_policy)81 gegl_buffer_cl_iterator_add_2 (GeglBufferClIterator  *iterator,
82                                GeglBuffer            *buffer,
83                                const GeglRectangle   *result,
84                                const Babl            *format,
85                                guint                  flags,
86                                gint                   left,
87                                gint                   right,
88                                gint                   top,
89                                gint                   bottom,
90                                GeglAbyssPolicy        abyss_policy)
91 {
92   GeglBufferClIterators *i = (gpointer)iterator;
93   gint self = 0;
94   if (i->iterators+1 > GEGL_CL_BUFFER_MAX_ITERATORS)
95     {
96       g_error ("too many iterators (%i)", i->iterators+1);
97     }
98 
99   if (i->iterators == 0) /* for sanity, we zero at init */
100     {
101       memset (i, 0, sizeof (GeglBufferClIterators));
102     }
103 
104   self = i->iterators++;
105 
106   if (!result)
107     result = self==0?&(buffer->extent):&(i->rect[0]);
108   i->rect[self]=*result;
109 
110   i->flags[self]=flags;
111 
112   i->abyss_policy[self]=abyss_policy;
113   if(flags != GEGL_CL_BUFFER_READ && abyss_policy != GEGL_ABYSS_NONE)
114     g_error ("invalid abyss");
115 
116   if (flags == GEGL_CL_BUFFER_WRITE || flags == GEGL_CL_BUFFER_READ)
117     {
118       const Babl *buffer_format = gegl_buffer_get_format (buffer);
119 
120       g_assert (buffer);
121 
122       i->buffer[self]= g_object_ref (buffer);
123 
124       if (format)
125         i->format[self] = format;
126       else
127         i->format[self] = buffer_format;
128 
129       if (flags == GEGL_CL_BUFFER_WRITE)
130         i->conv[self] = gegl_cl_color_supported (format, buffer_format);
131       else
132         i->conv[self] = gegl_cl_color_supported (buffer_format, format);
133 
134       gegl_cl_color_babl (buffer_format, &i->buf_cl_format_size[self]);
135       gegl_cl_color_babl (format,        &i->op_cl_format_size [self]);
136 
137       /* alpha, non-alpha & GEGL_ABYSS_NONE */
138       if (babl_format_has_alpha(buffer_format) != babl_format_has_alpha(format) &&
139           abyss_policy == GEGL_ABYSS_NONE &&
140           !gegl_rectangle_contains (gegl_buffer_get_extent (buffer), result))
141         {
142           i->conv[self] = GEGL_CL_COLOR_NOT_SUPPORTED;
143           GEGL_NOTE (GEGL_DEBUG_OPENCL, "Performance warning: not using color conversion "
144                                         "with OpenCL because abyss depends on alpha");
145         }
146     }
147   else /* GEGL_CL_BUFFER_AUX */
148     {
149       g_assert (buffer == NULL);
150 
151       i->buffer[self] = NULL;
152       i->format[self] = NULL;
153       i->conv[self]   = -1;
154       i->buf_cl_format_size[self] = SIZE_MAX;
155 
156       gegl_cl_color_babl (format, &i->op_cl_format_size [self]);
157     }
158 
159   i->area[self][0] = left;
160   i->area[self][1] = right;
161   i->area[self][2] = top;
162   i->area[self][3] = bottom;
163 
164   if (flags == GEGL_CL_BUFFER_WRITE
165       && (left > 0 || right > 0 || top > 0 || bottom > 0))
166 	g_assert(FALSE);
167 
168   if (self!=0)
169     {
170       /* we make all subsequently added iterators share the width and height of the first one */
171       i->rect[self].width  = i->rect[0].width;
172       i->rect[self].height = i->rect[0].height;
173     }
174   else
175     {
176       gint x, y, j;
177 
178       i->rois = 0;
179       for (y=result->y; y < result->y + result->height; y += gegl_cl_get_iter_height ())
180         for (x=result->x; x < result->x + result->width;  x += gegl_cl_get_iter_width ())
181           i->rois++;
182 
183       i->iteration_no = 0;
184 
185       i->roi_all = g_new0 (GeglRectangle, i->rois);
186 
187       j = 0;
188       for (y=0; y < result->height; y += gegl_cl_get_iter_height ())
189         for (x=0; x < result->width;  x += gegl_cl_get_iter_width ())
190           {
191             GeglRectangle r = {x, y,
192                                MIN(gegl_cl_get_iter_width (),  result->width  - x),
193                                MIN(gegl_cl_get_iter_height (), result->height - y)};
194             i->roi_all[j] = r;
195             j++;
196           }
197     }
198 
199   return self;
200 }
201 
202 gint
gegl_buffer_cl_iterator_add(GeglBufferClIterator * iterator,GeglBuffer * buffer,const GeglRectangle * roi,const Babl * format,guint flags,GeglAbyssPolicy abyss_policy)203 gegl_buffer_cl_iterator_add (GeglBufferClIterator  *iterator,
204                              GeglBuffer            *buffer,
205                              const GeglRectangle   *roi,
206                              const Babl            *format,
207                              guint                  flags,
208                              GeglAbyssPolicy        abyss_policy)
209 {
210   return gegl_buffer_cl_iterator_add_2 (iterator,
211                                         buffer, roi,
212                                         format, flags,
213                                         0, 0, 0, 0,
214                                         abyss_policy);
215 }
216 
217 gint
gegl_buffer_cl_iterator_add_aux(GeglBufferClIterator * iterator,const GeglRectangle * roi,const Babl * format,gint left,gint right,gint top,gint bottom)218 gegl_buffer_cl_iterator_add_aux  (GeglBufferClIterator  *iterator,
219                                   const GeglRectangle   *roi,
220                                   const Babl            *format,
221                                   gint                   left,
222                                   gint                   right,
223                                   gint                   top,
224                                   gint                   bottom)
225 {
226   return gegl_buffer_cl_iterator_add_2 (iterator,
227                                         NULL, roi,
228                                         format, GEGL_CL_BUFFER_AUX,
229                                         left, right,
230                                         top, bottom,
231                                         GEGL_ABYSS_NONE);
232 }
233 
234 static void
dealloc_iterator(GeglBufferClIterators * i)235 dealloc_iterator(GeglBufferClIterators *i)
236 {
237   int no;
238 
239   for (no=0; no<i->iterators;no++)
240     {
241       if (i->buffer[no])
242         {
243           gint j;
244           gboolean found = FALSE;
245           for (j=0; j<no; j++)
246             if (i->buffer[no]==i->buffer[j])
247               {
248                 found = TRUE;
249                 break;
250               }
251           if (!found)
252             gegl_buffer_unlock (i->buffer[no]);
253 
254           g_object_unref (i->buffer[no]);
255         }
256     }
257 
258   g_free (i->roi_all);
259   g_slice_free (GeglBufferClIterators, i);
260 }
261 
262 #define OPENCL_USE_CACHE 1
263 
264 gboolean
gegl_buffer_cl_iterator_next(GeglBufferClIterator * iterator,gboolean * err)265 gegl_buffer_cl_iterator_next (GeglBufferClIterator *iterator, gboolean *err)
266 {
267   GeglBufferClIterators *i = (gpointer)iterator;
268   gint no;
269   cl_int cl_err = 0;
270   int color_err = 0;
271   gboolean is_finished;
272 
273   if (i->is_finished)
274     g_error ("%s called on finished buffer iterator", G_STRFUNC);
275 
276   if (i->iteration_no == 0)
277     {
278       for (no=0; no<i->iterators;no++)
279         {
280           if (i->buffer[no])
281             {
282               gint j;
283               gboolean found = FALSE;
284               for (j=0; j<no; j++)
285                 if (i->buffer[no]==i->buffer[j])
286                   {
287                     found = TRUE;
288                     break;
289                   }
290               if (!found)
291                 gegl_buffer_lock (i->buffer[no]);
292 
293               if (i->flags[no] == GEGL_CL_BUFFER_WRITE
294                   || (i->flags[no] == GEGL_CL_BUFFER_READ
295                       && (i->area[no][0] > 0 || i->area[no][1] > 0 || i->area[no][2] > 0 || i->area[no][3] > 0)))
296                 {
297                   gegl_buffer_cl_cache_flush (i->buffer[no], &i->rect[no]);
298                 }
299             }
300         }
301     }
302   else
303     {
304       /* complete pending write work */
305       for (no=0; no<i->iterators;no++)
306         {
307           if (i->flags[no] == GEGL_CL_BUFFER_WRITE)
308             {
309               /* color conversion in the GPU (output) */
310               if (i->conv[no] == GEGL_CL_COLOR_CONVERT)
311                   {
312                     color_err = gegl_cl_color_conv (i->tex_op[no], i->tex_buf[no], i->size[no],
313                                                     i->format[no], gegl_buffer_get_format (i->buffer[no]));
314                     if (color_err) goto error;
315                   }
316 
317               /* GPU -> CPU */
318                 {
319                   gpointer data;
320 
321                   /* tile-ize */
322                   if (i->conv[no] == GEGL_CL_COLOR_NOT_SUPPORTED)
323                     {
324                       data = g_malloc(i->size[no] * i->op_cl_format_size [no]);
325 
326                       cl_err = gegl_clEnqueueReadBuffer(gegl_cl_get_command_queue(),
327                                                         i->tex_op[no], CL_TRUE,
328                                                         0, i->size[no] * i->op_cl_format_size[no], data,
329                                                         0, NULL, NULL);
330                       CL_CHECK;
331 
332                       /* color conversion using BABL */
333                       gegl_buffer_set (i->buffer[no], &i->roi[no], 0, i->format[no], data, GEGL_AUTO_ROWSTRIDE);
334 
335                       g_free(data);
336                     }
337                   else
338 #ifdef OPENCL_USE_CACHE
339                     {
340                       gegl_buffer_cl_cache_new (i->buffer[no], &i->roi[no], i->tex_buf[no]);
341                       /* don't release this texture */
342                       i->tex_buf[no] = NULL;
343                     }
344 #else
345                     {
346                       data = gegl_clEnqueueMapBuffer(gegl_cl_get_command_queue(), i->tex_buf[no], CL_TRUE,
347                                                      CL_MAP_READ,
348                                                      0, i->size[no] * i->buf_cl_format_size [no],
349                                                      0, NULL, NULL, &cl_err);
350                       CL_CHECK;
351 
352                       /* color conversion using BABL */
353                       gegl_buffer_set (i->buffer[no], &i->roi[no], 0, i->format[no], data, GEGL_AUTO_ROWSTRIDE);
354 
355                       cl_err = gegl_clEnqueueUnmapMemObject (gegl_cl_get_command_queue(), i->tex_buf[no], data,
356                                                              0, NULL, NULL);
357                       CL_CHECK;
358                     }
359 #endif
360                 }
361             }
362         }
363 
364       /* Run! */
365       cl_err = gegl_clFinish(gegl_cl_get_command_queue());
366       CL_CHECK;
367 
368       for (no=0; no < i->iterators; no++)
369           {
370             if (i->tex_buf_from_cache [no])
371               {
372                 gboolean ok = gegl_buffer_cl_cache_release (i->tex_buf[no]);
373                 g_assert (ok);
374               }
375 
376             if (i->tex_buf[no] && !i->tex_buf_from_cache [no])
377               gegl_clReleaseMemObject (i->tex_buf[no]);
378 
379             if (i->tex_op [no])
380               gegl_clReleaseMemObject (i->tex_op [no]);
381 
382             i->tex    [no] = NULL;
383             i->tex_buf[no] = NULL;
384             i->tex_op [no] = NULL;
385           }
386     }
387 
388   g_assert (i->iterators > 0);
389   is_finished = i->is_finished = (i->iteration_no >= i->rois);
390 
391   /* then we iterate all */
392   if (!i->is_finished)
393     {
394       for (no=0; no<i->iterators;no++)
395         {
396             {
397               GeglRectangle r = {i->rect[no].x + i->roi_all[i->iteration_no].x - i->area[no][0],
398                                  i->rect[no].y + i->roi_all[i->iteration_no].y - i->area[no][2],
399                                  i->roi_all[i->iteration_no].width             + i->area[no][0] + i->area[no][1],
400                                  i->roi_all[i->iteration_no].height            + i->area[no][2] + i->area[no][3]};
401               i->roi [no] = r;
402               i->size[no] = r.width * r.height;
403 
404               g_assert(i->size[no] > 0);
405             }
406 
407           if (i->flags[no] == GEGL_CL_BUFFER_READ)
408             {
409                 {
410                   gpointer data;
411 
412                   /* un-tile */
413                   switch (i->conv[no])
414                     {
415                       case GEGL_CL_COLOR_NOT_SUPPORTED:
416 
417                         {
418                         gegl_buffer_cl_cache_flush (i->buffer[no], &i->roi[no]);
419 
420                         g_assert (i->tex_op[no] == NULL);
421                         i->tex_op[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
422                                                                 CL_MEM_ALLOC_HOST_PTR | CL_MEM_READ_ONLY,
423                                                                 i->size[no] * i->op_cl_format_size [no],
424                                                                 NULL, &cl_err);
425                         CL_CHECK;
426 
427                         /* pre-pinned memory */
428                         data = gegl_clEnqueueMapBuffer(gegl_cl_get_command_queue(), i->tex_op[no], CL_TRUE,
429                                                        CL_MAP_WRITE,
430                                                        0, i->size[no] * i->op_cl_format_size [no],
431                                                        0, NULL, NULL, &cl_err);
432                         CL_CHECK;
433 
434                         /* color conversion using BABL */
435                         gegl_buffer_get (i->buffer[no], &i->roi[no], 1.0, i->format[no], data, GEGL_AUTO_ROWSTRIDE, i->abyss_policy[no]);
436 
437                         cl_err = gegl_clEnqueueUnmapMemObject (gegl_cl_get_command_queue(), i->tex_op[no], data,
438                                                                    0, NULL, NULL);
439                         CL_CHECK;
440 
441                         i->tex[no] = i->tex_op[no];
442 
443                         break;
444                         }
445 
446                       case GEGL_CL_COLOR_EQUAL:
447 
448                         {
449                         i->tex_buf[no] = gegl_buffer_cl_cache_get (i->buffer[no], &i->roi[no]);
450 
451                         if (i->tex_buf[no])
452                           i->tex_buf_from_cache [no] = TRUE; /* don't free texture from cache */
453                         else
454                           {
455                             gegl_buffer_cl_cache_flush (i->buffer[no], &i->roi[no]);
456 
457                             g_assert (i->tex_buf[no] == NULL);
458                             i->tex_buf[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
459                                                                      CL_MEM_ALLOC_HOST_PTR | CL_MEM_READ_ONLY,
460                                                                      i->size[no] * i->buf_cl_format_size [no],
461                                                                      NULL, &cl_err);
462                             CL_CHECK;
463 
464                             /* pre-pinned memory */
465                             data = gegl_clEnqueueMapBuffer(gegl_cl_get_command_queue(), i->tex_buf[no], CL_TRUE,
466                                                            CL_MAP_WRITE,
467                                                            0, i->size[no] * i->buf_cl_format_size [no],
468                                                            0, NULL, NULL, &cl_err);
469                             CL_CHECK;
470 
471                             /* color conversion will be performed in the GPU later */
472                             gegl_buffer_get (i->buffer[no], &i->roi[no], 1.0, NULL, data, GEGL_AUTO_ROWSTRIDE, i->abyss_policy[no]);
473 
474                             cl_err = gegl_clEnqueueUnmapMemObject (gegl_cl_get_command_queue(), i->tex_buf[no], data,
475                                                                    0, NULL, NULL);
476                             CL_CHECK;
477                           }
478 
479                         i->tex[no] = i->tex_buf[no];
480 
481                         break;
482                         }
483 
484                       case GEGL_CL_COLOR_CONVERT:
485 
486                         {
487                         i->tex_buf[no] = gegl_buffer_cl_cache_get (i->buffer[no], &i->roi[no]);
488 
489                         if (i->tex_buf[no])
490                           i->tex_buf_from_cache [no] = TRUE; /* don't free texture from cache */
491                         else
492                           {
493                             gegl_buffer_cl_cache_flush (i->buffer[no], &i->roi[no]);
494 
495                             g_assert (i->tex_buf[no] == NULL);
496                             i->tex_buf[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
497                                                                      CL_MEM_ALLOC_HOST_PTR | CL_MEM_READ_ONLY,
498                                                                      i->size[no] * i->buf_cl_format_size [no],
499                                                                      NULL, &cl_err);
500                             CL_CHECK;
501 
502                             /* pre-pinned memory */
503                             data = gegl_clEnqueueMapBuffer(gegl_cl_get_command_queue(), i->tex_buf[no], CL_TRUE,
504                                                            CL_MAP_WRITE,
505                                                            0, i->size[no] * i->buf_cl_format_size [no],
506                                                            0, NULL, NULL, &cl_err);
507                             CL_CHECK;
508 
509                             /* color conversion will be performed in the GPU later */
510                             gegl_buffer_get (i->buffer[no], &i->roi[no], 1.0, NULL, data, GEGL_AUTO_ROWSTRIDE, i->abyss_policy[no]);
511 
512                             cl_err = gegl_clEnqueueUnmapMemObject (gegl_cl_get_command_queue(), i->tex_buf[no], data,
513                                                                    0, NULL, NULL);
514                             CL_CHECK;
515                           }
516 
517                         g_assert (i->tex_op[no] == NULL);
518                         i->tex_op[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
519                                                                 CL_MEM_READ_WRITE,
520                                                                 i->size[no] * i->op_cl_format_size [no],
521                                                                 NULL, &cl_err);
522                         CL_CHECK;
523 
524                         /* color conversion in the GPU (input) */
525                         g_assert (i->tex_buf[no] && i->tex_op[no]);
526                         color_err = gegl_cl_color_conv (i->tex_buf[no], i->tex_op[no], i->size[no],
527                                                         gegl_buffer_get_format (i->buffer[no]), i->format[no]);
528                         if (color_err) goto error;
529 
530                         i->tex[no] = i->tex_op[no];
531 
532                         break;
533                         }
534                     }
535                 }
536             }
537           else if (i->flags[no] == GEGL_CL_BUFFER_WRITE)
538             {
539                 {
540                   switch (i->conv[no])
541                     {
542                       case GEGL_CL_COLOR_NOT_SUPPORTED:
543 
544                       {
545                       g_assert (i->tex_op[no] == NULL);
546                       i->tex_op[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
547                                                               CL_MEM_WRITE_ONLY,
548                                                               i->size[no] * i->op_cl_format_size [no],
549                                                               NULL, &cl_err);
550                       CL_CHECK;
551 
552                       i->tex[no] = i->tex_op[no];
553 
554                       break;
555                       }
556 
557                       case GEGL_CL_COLOR_EQUAL:
558 
559                       {
560                       g_assert (i->tex_buf[no] == NULL);
561                       i->tex_buf[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
562                                                                CL_MEM_READ_WRITE, /* cache */
563                                                                i->size[no] * i->buf_cl_format_size [no],
564                                                                NULL, &cl_err);
565                       CL_CHECK;
566 
567                       i->tex[no] = i->tex_buf[no];
568 
569                       break;
570                       }
571 
572                       case GEGL_CL_COLOR_CONVERT:
573 
574                       {
575                       g_assert (i->tex_buf[no] == NULL);
576                       i->tex_buf[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
577                                                                CL_MEM_READ_WRITE, /* cache */
578                                                                i->size[no] * i->buf_cl_format_size [no],
579                                                                NULL, &cl_err);
580                       CL_CHECK;
581 
582                       g_assert (i->tex_op[no] == NULL);
583                       i->tex_op[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
584                                                               CL_MEM_READ_WRITE,
585                                                               i->size[no] * i->op_cl_format_size [no],
586                                                               NULL, &cl_err);
587                       CL_CHECK;
588 
589                       i->tex[no] = i->tex_op[no];
590 
591                       break;
592                       }
593                    }
594                 }
595             }
596           else if (i->flags[no] == GEGL_CL_BUFFER_AUX)
597             {
598                 {
599                   g_assert (i->tex_op[no] == NULL);
600                   i->tex_op[no] = gegl_clCreateBuffer (gegl_cl_get_context (),
601                                                           CL_MEM_READ_WRITE,
602                                                           i->size[no] * i->op_cl_format_size [no],
603                                                           NULL, &cl_err);
604                   CL_CHECK;
605 
606                   i->tex[no] = i->tex_op[no];
607                 }
608             }
609         }
610 
611       i->iteration_no++;
612     }
613   else /* i->is_finished == TRUE */
614     {
615       dealloc_iterator(i);
616     }
617 
618   if (err)
619     *err = FALSE;
620   return !is_finished;
621 
622 error:
623   gegl_buffer_cl_iterator_stop ((GeglBufferClIterator *)i);
624 
625   if (err)
626     *err = TRUE;
627   return FALSE;
628 }
629 
630 void
gegl_buffer_cl_iterator_stop(GeglBufferClIterator * iterator)631 gegl_buffer_cl_iterator_stop (GeglBufferClIterator *iterator)
632 {
633   GeglBufferClIterators *i = (GeglBufferClIterators *)iterator;
634   int no;
635 
636   for (no = 0; no < i->iterators; no++)
637     {
638       if (i->tex_buf[no]) gegl_clReleaseMemObject (i->tex_buf[no]);
639       if (i->tex_op [no]) gegl_clReleaseMemObject (i->tex_op [no]);
640 
641       i->tex    [no] = NULL;
642       i->tex_buf[no] = NULL;
643       i->tex_op [no] = NULL;
644     }
645 
646   dealloc_iterator (i);
647 }
648 
649 GeglBufferClIterator *
gegl_buffer_cl_iterator_new(GeglBuffer * buffer,const GeglRectangle * roi,const Babl * format,guint flags)650 gegl_buffer_cl_iterator_new (GeglBuffer          *buffer,
651                              const GeglRectangle *roi,
652                              const Babl          *format,
653                              guint                flags)
654 {
655   GeglBufferClIterator *i = (gpointer)g_slice_new0 (GeglBufferClIterators);
656   /* Because the iterator is nulled above, we can forgo explicitly setting
657    * i->is_finished to FALSE. */
658   gegl_buffer_cl_iterator_add (i, buffer, roi, format, flags, GEGL_ABYSS_NONE);
659   return i;
660 }
661