blob: 446feb656e09a0af6b2cf90f586b80f73baa3336 [file] [log] [blame]
cristydbba8212013-07-19 14:53:50 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% OOO PPPP EEEEE N N CCCC L %
7% O O P P E NN N C L %
8% O O PPPP EEE N N N C L %
9% O O P E N NN C L %
10% OOO P EEEEE N N CCCC LLLLL %
11% %
12% %
13% MagickCore OpenCL Methods %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristydbba8212013-07-19 14:53:50 +000017% March 2000 %
18% %
19% %
Cristyd8420112021-01-01 14:52:00 -050020% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
cristydbba8212013-07-19 14:53:50 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
Cristy9ddfcca2018-09-09 19:46:34 -040026% https://imagemagick.org/script/license.php %
cristydbba8212013-07-19 14:53:50 +000027% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
Cristy1dd96da2015-10-06 07:52:01 -040039
cristydbba8212013-07-19 14:53:50 +000040/*
Cristy1dd96da2015-10-06 07:52:01 -040041 Include declarations.
cristydbba8212013-07-19 14:53:50 +000042*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
dirk21dc0312016-06-13 22:30:26 +020046#include "MagickCore/cache-private.h"
cristydbba8212013-07-19 14:53:50 +000047#include "MagickCore/color.h"
48#include "MagickCore/compare.h"
49#include "MagickCore/constitute.h"
dirk5a2575f2016-04-15 09:24:26 +020050#include "MagickCore/configure.h"
cristydbba8212013-07-19 14:53:50 +000051#include "MagickCore/distort.h"
52#include "MagickCore/draw.h"
53#include "MagickCore/effect.h"
54#include "MagickCore/exception.h"
55#include "MagickCore/exception-private.h"
56#include "MagickCore/fx.h"
57#include "MagickCore/gem.h"
58#include "MagickCore/geometry.h"
59#include "MagickCore/image.h"
60#include "MagickCore/image-private.h"
61#include "MagickCore/layer.h"
62#include "MagickCore/mime-private.h"
63#include "MagickCore/memory_.h"
Cristycca91aa2017-09-30 09:34:33 -040064#include "MagickCore/memory-private.h"
cristydbba8212013-07-19 14:53:50 +000065#include "MagickCore/monitor.h"
66#include "MagickCore/montage.h"
67#include "MagickCore/morphology.h"
cristyd1165552013-11-24 20:10:57 +000068#include "MagickCore/nt-base.h"
cristy1e37e8f2014-02-21 17:05:37 +000069#include "MagickCore/nt-base-private.h"
cristyf034abb2013-11-24 14:16:14 +000070#include "MagickCore/opencl.h"
71#include "MagickCore/opencl-private.h"
cristydbba8212013-07-19 14:53:50 +000072#include "MagickCore/option.h"
73#include "MagickCore/policy.h"
74#include "MagickCore/property.h"
75#include "MagickCore/quantize.h"
76#include "MagickCore/quantum.h"
cristy0c832c62014-03-07 22:21:04 +000077#include "MagickCore/random_.h"
78#include "MagickCore/random-private.h"
cristydbba8212013-07-19 14:53:50 +000079#include "MagickCore/resample.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/splay-tree.h"
cristyf034abb2013-11-24 14:16:14 +000082#include "MagickCore/semaphore.h"
cristydbba8212013-07-19 14:53:50 +000083#include "MagickCore/statistic.h"
84#include "MagickCore/string_.h"
dirk5a2575f2016-04-15 09:24:26 +020085#include "MagickCore/string-private.h"
cristydbba8212013-07-19 14:53:50 +000086#include "MagickCore/token.h"
87#include "MagickCore/utility.h"
dirk5a2575f2016-04-15 09:24:26 +020088#include "MagickCore/utility-private.h"
cristyf034abb2013-11-24 14:16:14 +000089
90#if defined(MAGICKCORE_OPENCL_SUPPORT)
Cristy89e83492017-11-12 08:09:05 -050091#if defined(MAGICKCORE_LTDL_DELEGATE)
92#include "ltdl.h"
93#endif
cristyf034abb2013-11-24 14:16:14 +000094
dirk5a2575f2016-04-15 09:24:26 +020095#ifndef MAGICKCORE_WINDOWS_SUPPORT
96#include <dlfcn.h>
97#endif
98
cristy0c832c62014-03-07 22:21:04 +000099#ifdef MAGICKCORE_HAVE_OPENCL_CL_H
100#define MAGICKCORE_OPENCL_MACOSX 1
101#endif
cristyf034abb2013-11-24 14:16:14 +0000102
dirk5a2575f2016-04-15 09:24:26 +0200103/*
104 Define declarations.
105*/
dirk8f012e52016-06-03 22:28:50 +0200106#define IMAGEMAGICK_PROFILE_FILE "ImagemagickOpenCLDeviceProfile.xml"
dirk5a2575f2016-04-15 09:24:26 +0200107
108/*
109 Typedef declarations.
110*/
111typedef struct
112{
113 long long freq;
114 long long clocks;
115 long long start;
116} AccelerateTimer;
dirk99731742015-11-14 22:54:38 +0100117
118typedef struct
119{
dirk5a2575f2016-04-15 09:24:26 +0200120 char
121 *name,
dirk8f012e52016-06-03 22:28:50 +0200122 *platform_name,
dirk9ca8c472016-09-20 21:40:52 +0200123 *vendor_name,
dirk5a2575f2016-04-15 09:24:26 +0200124 *version;
dirk99731742015-11-14 22:54:38 +0100125
dirk5a2575f2016-04-15 09:24:26 +0200126 cl_uint
127 max_clock_frequency,
128 max_compute_units;
129
130 double
131 score;
132} MagickCLDeviceBenchmark;
133
134/*
135 Forward declarations.
136*/
137
138static MagickBooleanType
139 HasOpenCLDevices(MagickCLEnv,ExceptionInfo *),
140 LoadOpenCLLibrary(void);
141
142static MagickCLDevice
143 RelinquishMagickCLDevice(MagickCLDevice);
144
145static MagickCLEnv
146 RelinquishMagickCLEnv(MagickCLEnv);
147
148static void
149 BenchmarkOpenCLDevices(MagickCLEnv);
150
151extern const char
152 *accelerateKernels, *accelerateKernels2;
153
dirk5a2575f2016-04-15 09:24:26 +0200154/* OpenCL library */
155MagickLibrary
156 *openCL_library;
157
158/* Default OpenCL environment */
159MagickCLEnv
160 default_CLEnv;
161MagickThreadType
162 test_thread_id=0;
163SemaphoreInfo
dirk21dc0312016-06-13 22:30:26 +0200164 *openCL_lock;
dirk5a2575f2016-04-15 09:24:26 +0200165
166/* Cached location of the OpenCL cache files */
167char
168 *cache_directory;
169SemaphoreInfo
170 *cache_directory_lock;
171
172static inline MagickBooleanType IsSameOpenCLDevice(MagickCLDevice a,
173 MagickCLDevice b)
dirk99731742015-11-14 22:54:38 +0100174{
dirk8f012e52016-06-03 22:28:50 +0200175 if ((LocaleCompare(a->platform_name,b->platform_name) == 0) &&
dirk9ca8c472016-09-20 21:40:52 +0200176 (LocaleCompare(a->vendor_name,b->vendor_name) == 0) &&
dirk8f012e52016-06-03 22:28:50 +0200177 (LocaleCompare(a->name,b->name) == 0) &&
dirk5a2575f2016-04-15 09:24:26 +0200178 (LocaleCompare(a->version,b->version) == 0) &&
179 (a->max_clock_frequency == b->max_clock_frequency) &&
180 (a->max_compute_units == b->max_compute_units))
181 return(MagickTrue);
182
183 return(MagickFalse);
dirk99731742015-11-14 22:54:38 +0100184}
185
dirk5a2575f2016-04-15 09:24:26 +0200186static inline MagickBooleanType IsBenchmarkedOpenCLDevice(MagickCLDevice a,
187 MagickCLDeviceBenchmark *b)
dirk99731742015-11-14 22:54:38 +0100188{
dirk8f012e52016-06-03 22:28:50 +0200189 if ((LocaleCompare(a->platform_name,b->platform_name) == 0) &&
dirk9ca8c472016-09-20 21:40:52 +0200190 (LocaleCompare(a->vendor_name,b->vendor_name) == 0) &&
dirk8f012e52016-06-03 22:28:50 +0200191 (LocaleCompare(a->name,b->name) == 0) &&
dirk5a2575f2016-04-15 09:24:26 +0200192 (LocaleCompare(a->version,b->version) == 0) &&
193 (a->max_clock_frequency == b->max_clock_frequency) &&
194 (a->max_compute_units == b->max_compute_units))
195 return(MagickTrue);
dirk99731742015-11-14 22:54:38 +0100196
dirk5a2575f2016-04-15 09:24:26 +0200197 return(MagickFalse);
dirk99731742015-11-14 22:54:38 +0100198}
cristyf034abb2013-11-24 14:16:14 +0000199
dirk5a2575f2016-04-15 09:24:26 +0200200static inline void RelinquishMagickCLDevices(MagickCLEnv clEnv)
cristy0c832c62014-03-07 22:21:04 +0000201{
dirk5a2575f2016-04-15 09:24:26 +0200202 size_t
203 i;
cristy0c832c62014-03-07 22:21:04 +0000204
dirk5a2575f2016-04-15 09:24:26 +0200205 if (clEnv->devices != (MagickCLDevice *) NULL)
cristy0c832c62014-03-07 22:21:04 +0000206 {
dirk5a2575f2016-04-15 09:24:26 +0200207 for (i = 0; i < clEnv->number_devices; i++)
208 clEnv->devices[i]=RelinquishMagickCLDevice(clEnv->devices[i]);
209 clEnv->devices=(MagickCLDevice *) RelinquishMagickMemory(clEnv->devices);
cristy0c832c62014-03-07 22:21:04 +0000210 }
dirk5a2575f2016-04-15 09:24:26 +0200211 clEnv->number_devices=0;
cristy0c832c62014-03-07 22:21:04 +0000212}
213
dirk5a2575f2016-04-15 09:24:26 +0200214static inline MagickBooleanType MagickCreateDirectory(const char *path)
cristy0c832c62014-03-07 22:21:04 +0000215{
dirk5a2575f2016-04-15 09:24:26 +0200216 int
217 status;
218
cristy0c832c62014-03-07 22:21:04 +0000219#ifdef MAGICKCORE_WINDOWS_SUPPORT
dirk5a2575f2016-04-15 09:24:26 +0200220 status=mkdir(path);
cristy0c832c62014-03-07 22:21:04 +0000221#else
Elliott Hughes5d41fba2021-04-12 16:36:42 -0700222 status=mkdir(path,0777);
dirk5a2575f2016-04-15 09:24:26 +0200223#endif
224 return(status == 0 ? MagickTrue : MagickFalse);
225}
226
227static inline void InitAccelerateTimer(AccelerateTimer *timer)
228{
229#ifdef _WIN32
230 QueryPerformanceFrequency((LARGE_INTEGER*)&timer->freq);
231#else
232 timer->freq=(long long)1.0E3;
233#endif
234 timer->clocks=0;
235 timer->start=0;
236}
237
238static inline double ReadAccelerateTimer(AccelerateTimer *timer)
239{
240 return (double)timer->clocks/(double)timer->freq;
241}
242
243static inline void StartAccelerateTimer(AccelerateTimer* timer)
244{
245#ifdef _WIN32
246 QueryPerformanceCounter((LARGE_INTEGER*)&timer->start);
247#else
248 struct timeval
249 s;
250 gettimeofday(&s,0);
251 timer->start=(long long)s.tv_sec*(long long)1.0E3+(long long)s.tv_usec/
252 (long long)1.0E3;
cristy0c832c62014-03-07 22:21:04 +0000253#endif
254}
cristyf034abb2013-11-24 14:16:14 +0000255
dirk5a2575f2016-04-15 09:24:26 +0200256static inline void StopAccelerateTimer(AccelerateTimer *timer)
cristyf034abb2013-11-24 14:16:14 +0000257{
dirk5a2575f2016-04-15 09:24:26 +0200258 long long
259 n;
cristyf034abb2013-11-24 14:16:14 +0000260
dirk5a2575f2016-04-15 09:24:26 +0200261 n=0;
262#ifdef _WIN32
263 QueryPerformanceCounter((LARGE_INTEGER*)&(n));
cristy0c832c62014-03-07 22:21:04 +0000264#else
dirk5a2575f2016-04-15 09:24:26 +0200265 struct timeval
266 s;
267 gettimeofday(&s,0);
268 n=(long long)s.tv_sec*(long long)1.0E3+(long long)s.tv_usec/
269 (long long)1.0E3;
cristy0c832c62014-03-07 22:21:04 +0000270#endif
dirk5a2575f2016-04-15 09:24:26 +0200271 n-=timer->start;
272 timer->start=0;
273 timer->clocks+=n;
cristy0c832c62014-03-07 22:21:04 +0000274}
275
dirk5a2575f2016-04-15 09:24:26 +0200276static const char *GetOpenCLCacheDirectory()
Cristy1dd96da2015-10-06 07:52:01 -0400277{
dirk5a2575f2016-04-15 09:24:26 +0200278 if (cache_directory == (char *) NULL)
cristy0c832c62014-03-07 22:21:04 +0000279 {
dirk01314602016-07-05 23:09:45 +0200280 if (cache_directory_lock == (SemaphoreInfo *) NULL)
281 ActivateSemaphoreInfo(&cache_directory_lock);
282 LockSemaphoreInfo(cache_directory_lock);
283 if (cache_directory == (char *) NULL)
dirk5a2575f2016-04-15 09:24:26 +0200284 {
dirk01314602016-07-05 23:09:45 +0200285 char
286 *home,
287 path[MagickPathExtent],
288 *temp;
cristy0c832c62014-03-07 22:21:04 +0000289
dirk01314602016-07-05 23:09:45 +0200290 MagickBooleanType
291 status;
Cristy1dd96da2015-10-06 07:52:01 -0400292
dirk01314602016-07-05 23:09:45 +0200293 struct stat
294 attributes;
295
296 temp=(char *) NULL;
297 home=GetEnvironmentValue("MAGICK_OPENCL_CACHE_DIR");
298 if (home == (char *) NULL)
dirk5a2575f2016-04-15 09:24:26 +0200299 {
dirk01314602016-07-05 23:09:45 +0200300 home=GetEnvironmentValue("XDG_CACHE_HOME");
Dirk Lemstradfb9ecb2019-04-12 22:24:33 +0200301#if defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__MINGW32__)
dirk01314602016-07-05 23:09:45 +0200302 if (home == (char *) NULL)
303 home=GetEnvironmentValue("LOCALAPPDATA");
304 if (home == (char *) NULL)
305 home=GetEnvironmentValue("APPDATA");
306 if (home == (char *) NULL)
307 home=GetEnvironmentValue("USERPROFILE");
Dirk Lemstradfb9ecb2019-04-12 22:24:33 +0200308#endif
dirk5a2575f2016-04-15 09:24:26 +0200309 }
310
dirk5a2575f2016-04-15 09:24:26 +0200311 if (home != (char *) NULL)
312 {
dirk01314602016-07-05 23:09:45 +0200313 /* first check if $HOME exists */
314 (void) FormatLocaleString(path,MagickPathExtent,"%s",home);
dirk5a2575f2016-04-15 09:24:26 +0200315 status=GetPathAttributes(path,&attributes);
316 if (status == MagickFalse)
317 status=MagickCreateDirectory(path);
318
dirk01314602016-07-05 23:09:45 +0200319 /* first check if $HOME/ImageMagick exists */
dirk5a2575f2016-04-15 09:24:26 +0200320 if (status != MagickFalse)
321 {
322 (void) FormatLocaleString(path,MagickPathExtent,
dirk01314602016-07-05 23:09:45 +0200323 "%s%sImageMagick",home,DirectorySeparator);
324
dirk5a2575f2016-04-15 09:24:26 +0200325 status=GetPathAttributes(path,&attributes);
326 if (status == MagickFalse)
327 status=MagickCreateDirectory(path);
328 }
329
330 if (status != MagickFalse)
331 {
Cristycca91aa2017-09-30 09:34:33 -0400332 temp=(char*) AcquireCriticalMemory(strlen(path)+1);
dirk5a2575f2016-04-15 09:24:26 +0200333 CopyMagickString(temp,path,strlen(path)+1);
334 }
335 home=DestroyString(home);
336 }
dirk01314602016-07-05 23:09:45 +0200337 else
338 {
339 home=GetEnvironmentValue("HOME");
340 if (home != (char *) NULL)
341 {
342 /* first check if $HOME/.cache exists */
343 (void) FormatLocaleString(path,MagickPathExtent,"%s%s.cache",
344 home,DirectorySeparator);
345 status=GetPathAttributes(path,&attributes);
346 if (status == MagickFalse)
347 status=MagickCreateDirectory(path);
348
349 /* first check if $HOME/.cache/ImageMagick exists */
350 if (status != MagickFalse)
351 {
352 (void) FormatLocaleString(path,MagickPathExtent,
353 "%s%s.cache%sImageMagick",home,DirectorySeparator,
354 DirectorySeparator);
355 status=GetPathAttributes(path,&attributes);
356 if (status == MagickFalse)
357 status=MagickCreateDirectory(path);
358 }
359
360 if (status != MagickFalse)
361 {
Cristya5022382017-09-30 10:32:06 -0400362 temp=(char*) AcquireCriticalMemory(strlen(path)+1);
dirk01314602016-07-05 23:09:45 +0200363 CopyMagickString(temp,path,strlen(path)+1);
364 }
365 home=DestroyString(home);
366 }
367 }
368 if (temp == (char *) NULL)
Dirk Lemstracbbaf512019-08-29 22:23:14 +0200369 {
370 temp=AcquireString("?");
371 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
372 "Cannot use cache directory: \"%s\"",path);
373 }
374 else
375 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
376 "Using cache directory: \"%s\"",temp);
dirk01314602016-07-05 23:09:45 +0200377 cache_directory=temp;
dirk5a2575f2016-04-15 09:24:26 +0200378 }
dirk01314602016-07-05 23:09:45 +0200379 UnlockSemaphoreInfo(cache_directory_lock);
cristy0c832c62014-03-07 22:21:04 +0000380 }
dirk01314602016-07-05 23:09:45 +0200381 if (*cache_directory == '?')
382 return((const char *) NULL);
dirk5a2575f2016-04-15 09:24:26 +0200383 return(cache_directory);
cristy0c832c62014-03-07 22:21:04 +0000384}
385
dirk5a2575f2016-04-15 09:24:26 +0200386static void SelectOpenCLDevice(MagickCLEnv clEnv,cl_device_type type)
Cristy1dd96da2015-10-06 07:52:01 -0400387{
dirk5a2575f2016-04-15 09:24:26 +0200388 MagickCLDevice
389 device;
390
391 size_t
392 i,
393 j;
394
Dirk Lemstracbbaf512019-08-29 22:23:14 +0200395 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
Dirk Lemstraec839642019-08-29 22:31:10 +0200396 "Selecting device for type: %d",(int) type);
dirk5a2575f2016-04-15 09:24:26 +0200397 for (i = 0; i < clEnv->number_devices; i++)
398 clEnv->devices[i]->enabled=MagickFalse;
399
400 for (i = 0; i < clEnv->number_devices; i++)
cristyf034abb2013-11-24 14:16:14 +0000401 {
dirk5a2575f2016-04-15 09:24:26 +0200402 device=clEnv->devices[i];
403 if (device->type != type)
404 continue;
405
406 device->enabled=MagickTrue;
Dirk Lemstracbbaf512019-08-29 22:23:14 +0200407 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
408 "Selected device: %s",device->name);
dirk5a2575f2016-04-15 09:24:26 +0200409 for (j = i+1; j < clEnv->number_devices; j++)
cristyf034abb2013-11-24 14:16:14 +0000410 {
dirk5a2575f2016-04-15 09:24:26 +0200411 MagickCLDevice
412 other_device;
413
414 other_device=clEnv->devices[j];
415 if (IsSameOpenCLDevice(device,other_device))
416 other_device->enabled=MagickTrue;
cristyf034abb2013-11-24 14:16:14 +0000417 }
cristyf034abb2013-11-24 14:16:14 +0000418 }
cristyf034abb2013-11-24 14:16:14 +0000419}
420
dirk8f012e52016-06-03 22:28:50 +0200421static size_t StringSignature(const char* string)
cristyf034abb2013-11-24 14:16:14 +0000422{
dirk8f012e52016-06-03 22:28:50 +0200423 size_t
dirk5a2575f2016-04-15 09:24:26 +0200424 n,
425 i,
426 j,
427 signature,
428 stringLength;
cristyf034abb2013-11-24 14:16:14 +0000429
cristyf034abb2013-11-24 14:16:14 +0000430 union
431 {
432 const char* s;
dirk8f012e52016-06-03 22:28:50 +0200433 const size_t* u;
dirk5a2575f2016-04-15 09:24:26 +0200434 } p;
cristyf034abb2013-11-24 14:16:14 +0000435
dirk8f012e52016-06-03 22:28:50 +0200436 stringLength=(size_t) strlen(string);
dirk5a2575f2016-04-15 09:24:26 +0200437 signature=stringLength;
dirk8f012e52016-06-03 22:28:50 +0200438 n=stringLength/sizeof(size_t);
dirk5a2575f2016-04-15 09:24:26 +0200439 p.s=string;
cristyf034abb2013-11-24 14:16:14 +0000440 for (i = 0; i < n; i++)
cristyf034abb2013-11-24 14:16:14 +0000441 signature^=p.u[i];
dirk8f012e52016-06-03 22:28:50 +0200442 if (n * sizeof(size_t) != stringLength)
cristyf034abb2013-11-24 14:16:14 +0000443 {
dirk5a2575f2016-04-15 09:24:26 +0200444 char
445 padded[4];
446
dirk8f012e52016-06-03 22:28:50 +0200447 j=n*sizeof(size_t);
dirk5a2575f2016-04-15 09:24:26 +0200448 for (i = 0; i < 4; i++, j++)
449 {
450 if (j < stringLength)
451 padded[i]=p.s[j];
452 else
453 padded[i]=0;
454 }
455 p.s=padded;
456 signature^=p.u[0];
cristyf034abb2013-11-24 14:16:14 +0000457 }
dirk5a2575f2016-04-15 09:24:26 +0200458 return(signature);
cristyf034abb2013-11-24 14:16:14 +0000459}
460
Dirk Lemstracf250862017-10-15 09:29:32 +0200461static void DestroyMagickCLCacheInfo(MagickCLCacheInfo info)
462{
463 ssize_t
464 i;
465
466 for (i=0; i < (ssize_t) info->event_count; i++)
467 openCL_library->clReleaseEvent(info->events[i]);
468 info->events=(cl_event *) RelinquishMagickMemory(info->events);
469 if (info->buffer != (cl_mem) NULL)
470 openCL_library->clReleaseMemObject(info->buffer);
471 RelinquishSemaphoreInfo(&info->events_semaphore);
472 ReleaseOpenCLDevice(info->device);
473 RelinquishMagickMemory(info);
474}
475
dirk21dc0312016-06-13 22:30:26 +0200476/*
477 Provide call to OpenCL library methods
478*/
479
480MagickPrivate cl_mem CreateOpenCLBuffer(MagickCLDevice device,
dirk66acef52016-06-21 00:02:08 +0200481 cl_mem_flags flags,size_t size,void *host_ptr)
dirk21dc0312016-06-13 22:30:26 +0200482{
dirke1200372016-06-19 12:27:18 +0200483 return(openCL_library->clCreateBuffer(device->context,flags,size,host_ptr,
dirk21dc0312016-06-13 22:30:26 +0200484 (cl_int *) NULL));
485}
486
487MagickPrivate void ReleaseOpenCLKernel(cl_kernel kernel)
488{
489 (void) openCL_library->clReleaseKernel(kernel);
490}
491
492MagickPrivate void ReleaseOpenCLMemObject(cl_mem memobj)
493{
494 (void) openCL_library->clReleaseMemObject(memobj);
495}
496
Dirk Lemstraf65e2912017-10-25 07:22:41 +0200497MagickPrivate void RetainOpenCLMemObject(cl_mem memobj)
498{
499 (void) openCL_library->clRetainMemObject(memobj);
500}
501
dirkeb01bef2016-09-18 12:19:09 +0200502MagickPrivate cl_int SetOpenCLKernelArg(cl_kernel kernel,size_t arg_index,
dirk21dc0312016-06-13 22:30:26 +0200503 size_t arg_size,const void *arg_value)
504{
dirkeb01bef2016-09-18 12:19:09 +0200505 return(openCL_library->clSetKernelArg(kernel,(cl_uint) arg_index,arg_size,
506 arg_value));
dirk21dc0312016-06-13 22:30:26 +0200507}
508
509/*
510%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511% %
512% %
513% %
514+ A c q u i r e M a g i c k C L C a c h e I n f o %
515% %
516% %
517% %
518%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519%
520% AcquireMagickCLCacheInfo() acquires an OpenCL cache info structure.
521%
522% The format of the AcquireMagickCLCacheInfo method is:
523%
524% MagickCLCacheInfo AcquireMagickCLCacheInfo(MagickCLDevice device,
525% Quantum *pixels,const MagickSizeType length)
526%
527% A description of each parameter follows:
528%
529% o device: the OpenCL device.
530%
531% o pixels: the pixel buffer of the image.
532%
533% o length: the length of the pixel buffer.
534%
535*/
536
537MagickPrivate MagickCLCacheInfo AcquireMagickCLCacheInfo(MagickCLDevice device,
538 Quantum *pixels,const MagickSizeType length)
539{
dirke1200372016-06-19 12:27:18 +0200540 cl_int
541 status;
542
dirk21dc0312016-06-13 22:30:26 +0200543 MagickCLCacheInfo
544 info;
545
Dirk Lemstracf250862017-10-15 09:29:32 +0200546 info=(MagickCLCacheInfo) AcquireCriticalMemory(sizeof(*info));
Cristy81bfff22018-03-10 07:58:31 -0500547 (void) memset(info,0,sizeof(*info));
dirk21dc0312016-06-13 22:30:26 +0200548 LockSemaphoreInfo(openCL_lock);
549 device->requested++;
550 UnlockSemaphoreInfo(openCL_lock);
551 info->device=device;
552 info->length=length;
553 info->pixels=pixels;
Dirk Lemstraba78ae32017-09-28 15:23:55 +0200554 info->events_semaphore=AcquireSemaphoreInfo();
dirk21dc0312016-06-13 22:30:26 +0200555 info->buffer=openCL_library->clCreateBuffer(device->context,
dirk66acef52016-06-21 00:02:08 +0200556 CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR,(size_t) length,(void *) pixels,
557 &status);
dirke1200372016-06-19 12:27:18 +0200558 if (status == CL_SUCCESS)
559 return(info);
Dirk Lemstracf250862017-10-15 09:29:32 +0200560 DestroyMagickCLCacheInfo(info);
561 return((MagickCLCacheInfo) NULL);
dirk21dc0312016-06-13 22:30:26 +0200562}
563
cristyf034abb2013-11-24 14:16:14 +0000564/*
565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566% %
567% %
568% %
dirk5a2575f2016-04-15 09:24:26 +0200569% A c q u i r e M a g i c k C L D e v i c e %
cristyf034abb2013-11-24 14:16:14 +0000570% %
571% %
572% %
573%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
574%
dirk5a2575f2016-04-15 09:24:26 +0200575% AcquireMagickCLDevice() acquires an OpenCL device
cristyf034abb2013-11-24 14:16:14 +0000576%
dirk5a2575f2016-04-15 09:24:26 +0200577% The format of the AcquireMagickCLDevice method is:
cristyf034abb2013-11-24 14:16:14 +0000578%
dirk5a2575f2016-04-15 09:24:26 +0200579% MagickCLDevice AcquireMagickCLDevice()
cristyf034abb2013-11-24 14:16:14 +0000580%
581*/
582
dirk5a2575f2016-04-15 09:24:26 +0200583static MagickCLDevice AcquireMagickCLDevice()
584{
585 MagickCLDevice
586 device;
cristyf034abb2013-11-24 14:16:14 +0000587
Cristy8357b5d2020-11-22 12:39:10 +0000588 device=(MagickCLDevice) AcquireMagickMemory(sizeof(*device));
dirk5a2575f2016-04-15 09:24:26 +0200589 if (device != NULL)
cristy0c832c62014-03-07 22:21:04 +0000590 {
Cristy81bfff22018-03-10 07:58:31 -0500591 (void) memset(device,0,sizeof(*device));
dirk5a2575f2016-04-15 09:24:26 +0200592 ActivateSemaphoreInfo(&device->lock);
593 device->score=MAGICKCORE_OPENCL_UNDEFINED_SCORE;
594 device->command_queues_index=-1;
595 device->enabled=MagickTrue;
cristy0c832c62014-03-07 22:21:04 +0000596 }
dirk5a2575f2016-04-15 09:24:26 +0200597 return(device);
cristyf034abb2013-11-24 14:16:14 +0000598}
599
cristyf034abb2013-11-24 14:16:14 +0000600/*
601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
602% %
603% %
604% %
dirk5a2575f2016-04-15 09:24:26 +0200605% A c q u i r e M a g i c k C L E n v %
606% %
607% %
608% %
609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610%
611% AcquireMagickCLEnv() allocates the MagickCLEnv structure
612%
613*/
614
615static MagickCLEnv AcquireMagickCLEnv(void)
616{
617 const char
618 *option;
619
620 MagickCLEnv
621 clEnv;
622
Cristy8357b5d2020-11-22 12:39:10 +0000623 clEnv=(MagickCLEnv) AcquireMagickMemory(sizeof(*clEnv));
dirk5a2575f2016-04-15 09:24:26 +0200624 if (clEnv != (MagickCLEnv) NULL)
625 {
Cristy81bfff22018-03-10 07:58:31 -0500626 (void) memset(clEnv,0,sizeof(*clEnv));
dirk5a2575f2016-04-15 09:24:26 +0200627 ActivateSemaphoreInfo(&clEnv->lock);
628 clEnv->cpu_score=MAGICKCORE_OPENCL_UNDEFINED_SCORE;
Elliott Hughes5d41fba2021-04-12 16:36:42 -0700629 clEnv->enabled=MagickFalse;
dirk5a2575f2016-04-15 09:24:26 +0200630 option=getenv("MAGICK_OCL_DEVICE");
Elliott Hughes5d41fba2021-04-12 16:36:42 -0700631 if (option != (const char *) NULL)
632 {
633 if ((IsStringTrue(option) != MagickFalse) ||
634 (strcmp(option,"GPU") == 0) ||
635 (strcmp(option,"CPU") == 0))
636 clEnv->enabled=MagickTrue;
637 }
dirk5a2575f2016-04-15 09:24:26 +0200638 }
639 return clEnv;
640}
641
642/*
643%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
644% %
645% %
646% %
dirk21dc0312016-06-13 22:30:26 +0200647+ A c q u i r e O p e n C L C o m m a n d Q u e u e %
cristyf034abb2013-11-24 14:16:14 +0000648% %
649% %
650% %
651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
652%
653% AcquireOpenCLCommandQueue() acquires an OpenCL command queue
654%
655% The format of the AcquireOpenCLCommandQueue method is:
656%
dirk5a2575f2016-04-15 09:24:26 +0200657% cl_command_queue AcquireOpenCLCommandQueue(MagickCLDevice device)
cristyf034abb2013-11-24 14:16:14 +0000658%
659% A description of each parameter follows:
660%
dirk5a2575f2016-04-15 09:24:26 +0200661% o device: the OpenCL device.
cristyf034abb2013-11-24 14:16:14 +0000662%
663*/
664
dirk5a2575f2016-04-15 09:24:26 +0200665MagickPrivate cl_command_queue AcquireOpenCLCommandQueue(MagickCLDevice device)
cristyf034abb2013-11-24 14:16:14 +0000666{
dirk99731742015-11-14 22:54:38 +0100667 cl_command_queue
668 queue;
cristyf034abb2013-11-24 14:16:14 +0000669
dirk99731742015-11-14 22:54:38 +0100670 cl_command_queue_properties
671 properties;
672
dirk5a2575f2016-04-15 09:24:26 +0200673 assert(device != (MagickCLDevice) NULL);
674 LockSemaphoreInfo(device->lock);
dirkd55bcc32016-04-22 17:43:02 +0200675 if ((device->profile_kernels == MagickFalse) &&
676 (device->command_queues_index >= 0))
dirk5a2575f2016-04-15 09:24:26 +0200677 {
678 queue=device->command_queues[device->command_queues_index--];
679 UnlockSemaphoreInfo(device->lock);
dirk99731742015-11-14 22:54:38 +0100680 }
dirk5a2575f2016-04-15 09:24:26 +0200681 else
682 {
683 UnlockSemaphoreInfo(device->lock);
Dirk Lemstraa61abe72018-03-25 09:59:12 +0200684 properties=0;
dirk7d42b3c2016-04-18 23:12:54 +0200685 if (device->profile_kernels != MagickFalse)
686 properties=CL_QUEUE_PROFILING_ENABLE;
dirk5a2575f2016-04-15 09:24:26 +0200687 queue=openCL_library->clCreateCommandQueue(device->context,
dirke1200372016-06-19 12:27:18 +0200688 device->deviceID,properties,(cl_int *) NULL);
dirk99731742015-11-14 22:54:38 +0100689 }
690 return(queue);
691}
cristyf034abb2013-11-24 14:16:14 +0000692
693/*
694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
695% %
696% %
697% %
dirk21dc0312016-06-13 22:30:26 +0200698+ A c q u i r e O p e n C L K e r n e l %
cristyf034abb2013-11-24 14:16:14 +0000699% %
700% %
701% %
702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
703%
704% AcquireOpenCLKernel() acquires an OpenCL kernel
705%
706% The format of the AcquireOpenCLKernel method is:
707%
Cristy1dd96da2015-10-06 07:52:01 -0400708% cl_kernel AcquireOpenCLKernel(MagickCLEnv clEnv,
cristyf034abb2013-11-24 14:16:14 +0000709% MagickOpenCLProgram program, const char* kernelName)
710%
711% A description of each parameter follows:
712%
713% o clEnv: the OpenCL environment.
714%
715% o program: the OpenCL program module that the kernel belongs to.
716%
717% o kernelName: the name of the kernel
718%
719*/
720
dirk5a2575f2016-04-15 09:24:26 +0200721MagickPrivate cl_kernel AcquireOpenCLKernel(MagickCLDevice device,
722 const char *kernel_name)
cristyf034abb2013-11-24 14:16:14 +0000723{
dirk8d097d02016-03-27 14:04:58 +0200724 cl_kernel
725 kernel;
726
dirk5a2575f2016-04-15 09:24:26 +0200727 assert(device != (MagickCLDevice) NULL);
Dirk Lemstracbbaf512019-08-29 22:23:14 +0200728 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),"Using kernel: %s",
729 kernel_name);
dirke1200372016-06-19 12:27:18 +0200730 kernel=openCL_library->clCreateKernel(device->program,kernel_name,
731 (cl_int *) NULL);
dirk8d097d02016-03-27 14:04:58 +0200732 return(kernel);
cristyf034abb2013-11-24 14:16:14 +0000733}
734
cristyf034abb2013-11-24 14:16:14 +0000735/*
736%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
737% %
738% %
739% %
dirk5a2575f2016-04-15 09:24:26 +0200740% A u t o S e l e c t O p e n C L D e v i c e s %
cristyf034abb2013-11-24 14:16:14 +0000741% %
742% %
743% %
744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745%
Cristy9099bbb2018-05-26 06:19:13 -0400746% AutoSelectOpenCLDevices() determines the best device based on the
dirk5a2575f2016-04-15 09:24:26 +0200747% information from the micro-benchmark.
cristyf034abb2013-11-24 14:16:14 +0000748%
dirk5a2575f2016-04-15 09:24:26 +0200749% The format of the AutoSelectOpenCLDevices method is:
cristyf034abb2013-11-24 14:16:14 +0000750%
dirk5a2575f2016-04-15 09:24:26 +0200751% void AcquireOpenCLKernel(MagickCLEnv clEnv,ExceptionInfo *exception)
cristyf034abb2013-11-24 14:16:14 +0000752%
753% A description of each parameter follows:
754%
755% o clEnv: the OpenCL environment.
756%
dirk5a2575f2016-04-15 09:24:26 +0200757% o exception: return any errors or warnings in this structure.
cristyf034abb2013-11-24 14:16:14 +0000758%
759*/
760
dirk5a2575f2016-04-15 09:24:26 +0200761static void LoadOpenCLDeviceBenchmark(MagickCLEnv clEnv,const char *xml)
cristyf034abb2013-11-24 14:16:14 +0000762{
dirk5a2575f2016-04-15 09:24:26 +0200763 char
764 keyword[MagickPathExtent],
765 *token;
766
767 const char
768 *q;
769
770 MagickCLDeviceBenchmark
771 *device_benchmark;
772
dirk5a2575f2016-04-15 09:24:26 +0200773 size_t
774 i,
775 extent;
776
777 if (xml == (char *) NULL)
778 return;
dirk5a2575f2016-04-15 09:24:26 +0200779 device_benchmark=(MagickCLDeviceBenchmark *) NULL;
780 token=AcquireString(xml);
781 extent=strlen(token)+MagickPathExtent;
782 for (q=(char *) xml; *q != '\0'; )
cristyf034abb2013-11-24 14:16:14 +0000783 {
dirk5a2575f2016-04-15 09:24:26 +0200784 /*
785 Interpret XML.
786 */
Cristy448fd182019-07-27 16:26:38 -0400787 (void) GetNextToken(q,&q,extent,token);
dirk5a2575f2016-04-15 09:24:26 +0200788 if (*token == '\0')
789 break;
790 (void) CopyMagickString(keyword,token,MagickPathExtent);
791 if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
792 {
793 /*
794 Doctype element.
795 */
796 while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
Cristy448fd182019-07-27 16:26:38 -0400797 (void) GetNextToken(q,&q,extent,token);
dirk5a2575f2016-04-15 09:24:26 +0200798 continue;
799 }
800 if (LocaleNCompare(keyword,"<!--",4) == 0)
801 {
802 /*
803 Comment element.
804 */
805 while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
Cristy448fd182019-07-27 16:26:38 -0400806 (void) GetNextToken(q,&q,extent,token);
dirk5a2575f2016-04-15 09:24:26 +0200807 continue;
808 }
809 if (LocaleCompare(keyword,"<device") == 0)
810 {
811 /*
812 Device element.
813 */
Cristy566eaf12020-11-15 17:46:43 +0000814 device_benchmark=(MagickCLDeviceBenchmark *) AcquireQuantumMemory(1,
dirk5a2575f2016-04-15 09:24:26 +0200815 sizeof(*device_benchmark));
816 if (device_benchmark == (MagickCLDeviceBenchmark *) NULL)
817 break;
Cristy81bfff22018-03-10 07:58:31 -0500818 (void) memset(device_benchmark,0,sizeof(*device_benchmark));
dirk5a2575f2016-04-15 09:24:26 +0200819 device_benchmark->score=MAGICKCORE_OPENCL_UNDEFINED_SCORE;
820 continue;
821 }
822 if (device_benchmark == (MagickCLDeviceBenchmark *) NULL)
823 continue;
824 if (LocaleCompare(keyword,"/>") == 0)
825 {
826 if (device_benchmark->score != MAGICKCORE_OPENCL_UNDEFINED_SCORE)
827 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -0700828 if (LocaleCompare(device_benchmark->name,"CPU") == 0)
dirk5a2575f2016-04-15 09:24:26 +0200829 clEnv->cpu_score=device_benchmark->score;
830 else
831 {
832 MagickCLDevice
833 device;
834
835 /*
836 Set the score for all devices that match this device.
837 */
838 for (i = 0; i < clEnv->number_devices; i++)
839 {
840 device=clEnv->devices[i];
841 if (IsBenchmarkedOpenCLDevice(device,device_benchmark))
842 device->score=device_benchmark->score;
843 }
844 }
845 }
846
dirk23fc9902016-06-18 14:42:13 +0200847 device_benchmark->platform_name=RelinquishMagickMemory(
848 device_benchmark->platform_name);
dirk9ca8c472016-09-20 21:40:52 +0200849 device_benchmark->vendor_name=RelinquishMagickMemory(
850 device_benchmark->vendor_name);
dirk23fc9902016-06-18 14:42:13 +0200851 device_benchmark->name=RelinquishMagickMemory(device_benchmark->name);
852 device_benchmark->version=RelinquishMagickMemory(
853 device_benchmark->version);
dirk5a2575f2016-04-15 09:24:26 +0200854 device_benchmark=(MagickCLDeviceBenchmark *) RelinquishMagickMemory(
855 device_benchmark);
856 continue;
857 }
Cristy448fd182019-07-27 16:26:38 -0400858 (void) GetNextToken(q,(const char **) NULL,extent,token);
dirk5a2575f2016-04-15 09:24:26 +0200859 if (*token != '=')
860 continue;
Cristy448fd182019-07-27 16:26:38 -0400861 (void) GetNextToken(q,&q,extent,token);
862 (void) GetNextToken(q,&q,extent,token);
dirk5a2575f2016-04-15 09:24:26 +0200863 switch (*keyword)
864 {
865 case 'M':
866 case 'm':
867 {
868 if (LocaleCompare((char *) keyword,"maxClockFrequency") == 0)
869 {
870 device_benchmark->max_clock_frequency=StringToInteger(token);
871 break;
872 }
873 if (LocaleCompare((char *) keyword,"maxComputeUnits") == 0)
874 {
875 device_benchmark->max_compute_units=StringToInteger(token);
876 break;
877 }
878 break;
879 }
880 case 'N':
881 case 'n':
882 {
883 if (LocaleCompare((char *) keyword,"name") == 0)
884 device_benchmark->name=ConstantString(token);
885 break;
886 }
dirk8f012e52016-06-03 22:28:50 +0200887 case 'P':
888 case 'p':
889 {
890 if (LocaleCompare((char *) keyword,"platform") == 0)
891 device_benchmark->platform_name=ConstantString(token);
892 break;
893 }
dirk5a2575f2016-04-15 09:24:26 +0200894 case 'S':
895 case 's':
896 {
897 if (LocaleCompare((char *) keyword,"score") == 0)
898 device_benchmark->score=StringToDouble(token,(char **) NULL);
899 break;
900 }
901 case 'V':
902 case 'v':
903 {
dirk9ca8c472016-09-20 21:40:52 +0200904 if (LocaleCompare((char *) keyword,"vendor") == 0)
905 device_benchmark->vendor_name=ConstantString(token);
dirk5a2575f2016-04-15 09:24:26 +0200906 if (LocaleCompare((char *) keyword,"version") == 0)
907 device_benchmark->version=ConstantString(token);
908 break;
909 }
910 default:
911 break;
912 }
cristyf034abb2013-11-24 14:16:14 +0000913 }
dirk5a2575f2016-04-15 09:24:26 +0200914 token=(char *) RelinquishMagickMemory(token);
915 device_benchmark=(MagickCLDeviceBenchmark *) RelinquishMagickMemory(
916 device_benchmark);
917}
918
919static MagickBooleanType CanWriteProfileToFile(const char *filename)
920{
921 FILE
922 *profileFile;
923
924 profileFile=fopen(filename,"ab");
925
Dirk Lemstracbbaf512019-08-29 22:23:14 +0200926 if (profileFile == (FILE *) NULL)
927 {
928 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
929 "Unable to save profile to: \"%s\"",filename);
930 return(MagickFalse);
931 }
dirk5a2575f2016-04-15 09:24:26 +0200932
933 fclose(profileFile);
934 return(MagickTrue);
935}
936
dirk3eec1182016-08-14 19:57:09 +0200937static MagickBooleanType LoadOpenCLBenchmarks(MagickCLEnv clEnv)
dirk5a2575f2016-04-15 09:24:26 +0200938{
939 char
940 filename[MagickPathExtent];
941
dirk54fa3612016-08-14 19:55:19 +0200942 StringInfo
dirk5a2575f2016-04-15 09:24:26 +0200943 *option;
944
dirk5a2575f2016-04-15 09:24:26 +0200945 size_t
946 i;
947
948 (void) FormatLocaleString(filename,MagickPathExtent,"%s%s%s",
dirkc81649c2016-08-26 13:27:53 +0200949 GetOpenCLCacheDirectory(),DirectorySeparator,IMAGEMAGICK_PROFILE_FILE);
dirk5a2575f2016-04-15 09:24:26 +0200950
951 /*
dirkb35e60b2016-07-17 11:41:53 +0200952 We don't run the benchmark when we can not write out a device profile. The
953 first GPU device will be used.
dirk5a2575f2016-04-15 09:24:26 +0200954 */
Dirk Lemstrafecddef2019-12-09 22:04:39 +0100955#if !MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
dirk5a2575f2016-04-15 09:24:26 +0200956 if (CanWriteProfileToFile(filename) == MagickFalse)
dirkb18d8642016-07-17 19:32:43 +0200957#endif
dirk5a2575f2016-04-15 09:24:26 +0200958 {
959 for (i = 0; i < clEnv->number_devices; i++)
960 clEnv->devices[i]->score=1.0;
961
962 SelectOpenCLDevice(clEnv,CL_DEVICE_TYPE_GPU);
963 return(MagickFalse);
964 }
965
dirk871215d2016-08-14 19:23:59 +0200966 option=ConfigureFileToStringInfo(filename);
dirk3886d282016-08-13 22:42:49 +0200967 LoadOpenCLDeviceBenchmark(clEnv,(const char *) GetStringInfoDatum(option));
968 option=DestroyStringInfo(option);
dirk5a2575f2016-04-15 09:24:26 +0200969 return(MagickTrue);
970}
971
dirk3eec1182016-08-14 19:57:09 +0200972static void AutoSelectOpenCLDevices(MagickCLEnv clEnv)
dirk5a2575f2016-04-15 09:24:26 +0200973{
974 const char
975 *option;
976
977 double
978 best_score;
979
980 MagickBooleanType
981 benchmark;
982
983 size_t
984 i;
985
986 option=getenv("MAGICK_OCL_DEVICE");
987 if (option != (const char *) NULL)
988 {
989 if (strcmp(option,"GPU") == 0)
990 SelectOpenCLDevice(clEnv,CL_DEVICE_TYPE_GPU);
991 else if (strcmp(option,"CPU") == 0)
992 SelectOpenCLDevice(clEnv,CL_DEVICE_TYPE_CPU);
dirk5a2575f2016-04-15 09:24:26 +0200993 }
994
dirk3eec1182016-08-14 19:57:09 +0200995 if (LoadOpenCLBenchmarks(clEnv) == MagickFalse)
dirk5a2575f2016-04-15 09:24:26 +0200996 return;
997
998 benchmark=MagickFalse;
999 if (clEnv->cpu_score == MAGICKCORE_OPENCL_UNDEFINED_SCORE)
1000 benchmark=MagickTrue;
1001 else
1002 {
1003 for (i = 0; i < clEnv->number_devices; i++)
1004 {
1005 if (clEnv->devices[i]->score == MAGICKCORE_OPENCL_UNDEFINED_SCORE)
1006 {
1007 benchmark=MagickTrue;
1008 break;
1009 }
1010 }
1011 }
1012
1013 if (benchmark != MagickFalse)
1014 BenchmarkOpenCLDevices(clEnv);
1015
1016 best_score=clEnv->cpu_score;
1017 for (i = 0; i < clEnv->number_devices; i++)
1018 best_score=MagickMin(clEnv->devices[i]->score,best_score);
1019
1020 for (i = 0; i < clEnv->number_devices; i++)
1021 {
1022 if (clEnv->devices[i]->score != best_score)
1023 clEnv->devices[i]->enabled=MagickFalse;
1024 }
cristyf034abb2013-11-24 14:16:14 +00001025}
1026
1027/*
1028%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1029% %
1030% %
1031% %
dirk5a2575f2016-04-15 09:24:26 +02001032% B e n c h m a r k O p e n C L D e v i c e s %
cristyf034abb2013-11-24 14:16:14 +00001033% %
1034% %
1035% %
1036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037%
dirk5a2575f2016-04-15 09:24:26 +02001038% BenchmarkOpenCLDevices() benchmarks the OpenCL devices and the CPU to help
1039% the automatic selection of the best device.
cristyf034abb2013-11-24 14:16:14 +00001040%
dirk5a2575f2016-04-15 09:24:26 +02001041% The format of the BenchmarkOpenCLDevices method is:
cristyf034abb2013-11-24 14:16:14 +00001042%
dirk5a2575f2016-04-15 09:24:26 +02001043% void BenchmarkOpenCLDevices(MagickCLEnv clEnv,ExceptionInfo *exception)
cristyf034abb2013-11-24 14:16:14 +00001044%
1045% A description of each parameter follows:
1046%
1047% o clEnv: the OpenCL environment.
1048%
dirk5a2575f2016-04-15 09:24:26 +02001049% o exception: return any errors or warnings
cristyf034abb2013-11-24 14:16:14 +00001050*/
1051
dirk21dc0312016-06-13 22:30:26 +02001052static double RunOpenCLBenchmark(MagickBooleanType is_cpu)
cristyf034abb2013-11-24 14:16:14 +00001053{
dirk22624f12013-12-01 17:16:37 +00001054 AccelerateTimer
1055 timer;
cristyf034abb2013-11-24 14:16:14 +00001056
dirk22624f12013-12-01 17:16:37 +00001057 ExceptionInfo
dirk5a2575f2016-04-15 09:24:26 +02001058 *exception;
cristyf034abb2013-11-24 14:16:14 +00001059
dirk5a2575f2016-04-15 09:24:26 +02001060 Image
1061 *inputImage;
cristyf034abb2013-11-24 14:16:14 +00001062
dirk5a2575f2016-04-15 09:24:26 +02001063 ImageInfo
1064 *imageInfo;
dirk22624f12013-12-01 17:16:37 +00001065
dirk5a2575f2016-04-15 09:24:26 +02001066 size_t
1067 i;
dirk22624f12013-12-01 17:16:37 +00001068
dirk22624f12013-12-01 17:16:37 +00001069 exception=AcquireExceptionInfo();
dirk5a2575f2016-04-15 09:24:26 +02001070 imageInfo=AcquireImageInfo();
1071 CloneString(&imageInfo->size,"2048x1536");
1072 CopyMagickString(imageInfo->filename,"xc:none",MagickPathExtent);
1073 inputImage=ReadImage(imageInfo,exception);
dirk22624f12013-12-01 17:16:37 +00001074
dirk5a2575f2016-04-15 09:24:26 +02001075 InitAccelerateTimer(&timer);
dirk22624f12013-12-01 17:16:37 +00001076
dirk5a2575f2016-04-15 09:24:26 +02001077 for (i=0; i<=2; i++)
cristyf034abb2013-11-24 14:16:14 +00001078 {
dirk22624f12013-12-01 17:16:37 +00001079 Image
dirk5a2575f2016-04-15 09:24:26 +02001080 *bluredImage,
1081 *resizedImage,
1082 *unsharpedImage;
cristyf034abb2013-11-24 14:16:14 +00001083
dirk5a2575f2016-04-15 09:24:26 +02001084 if (i > 0)
1085 StartAccelerateTimer(&timer);
cristyf034abb2013-11-24 14:16:14 +00001086
dirk5a2575f2016-04-15 09:24:26 +02001087 bluredImage=BlurImage(inputImage,10.0f,3.5f,exception);
1088 unsharpedImage=UnsharpMaskImage(bluredImage,2.0f,2.0f,50.0f,10.0f,
1089 exception);
1090 resizedImage=ResizeImage(unsharpedImage,640,480,LanczosFilter,
1091 exception);
dirk22624f12013-12-01 17:16:37 +00001092
Cristy9099bbb2018-05-26 06:19:13 -04001093 /*
dirk21dc0312016-06-13 22:30:26 +02001094 We need this to get a proper performance benchmark, the operations
1095 are executed asynchronous.
1096 */
1097 if (is_cpu == MagickFalse)
1098 {
1099 CacheInfo
1100 *cache_info;
1101
1102 cache_info=(CacheInfo *) resizedImage->cache;
1103 if (cache_info->opencl != (MagickCLCacheInfo) NULL)
1104 openCL_library->clWaitForEvents(cache_info->opencl->event_count,
1105 cache_info->opencl->events);
1106 }
1107
dirk5a2575f2016-04-15 09:24:26 +02001108 if (i > 0)
1109 StopAccelerateTimer(&timer);
cristyf034abb2013-11-24 14:16:14 +00001110
dirk5a2575f2016-04-15 09:24:26 +02001111 if (bluredImage != (Image *) NULL)
1112 DestroyImage(bluredImage);
1113 if (unsharpedImage != (Image *) NULL)
1114 DestroyImage(unsharpedImage);
1115 if (resizedImage != (Image *) NULL)
1116 DestroyImage(resizedImage);
cristyf034abb2013-11-24 14:16:14 +00001117 }
dirk5a2575f2016-04-15 09:24:26 +02001118 DestroyImage(inputImage);
1119 return(ReadAccelerateTimer(&timer));
cristyf034abb2013-11-24 14:16:14 +00001120}
1121
dirk5a2575f2016-04-15 09:24:26 +02001122static void RunDeviceBenckmark(MagickCLEnv clEnv,MagickCLEnv testEnv,
1123 MagickCLDevice device)
dirkb05dcc92014-08-27 15:30:53 +00001124{
dirk5a2575f2016-04-15 09:24:26 +02001125 testEnv->devices[0]=device;
1126 default_CLEnv=testEnv;
dirk21dc0312016-06-13 22:30:26 +02001127 device->score=RunOpenCLBenchmark(MagickFalse);
dirk5a2575f2016-04-15 09:24:26 +02001128 default_CLEnv=clEnv;
1129 testEnv->devices[0]=(MagickCLDevice) NULL;
dirkb05dcc92014-08-27 15:30:53 +00001130}
cristyf034abb2013-11-24 14:16:14 +00001131
dirk5a2575f2016-04-15 09:24:26 +02001132static void CacheOpenCLBenchmarks(MagickCLEnv clEnv)
1133{
1134 char
1135 filename[MagickPathExtent];
cristyf034abb2013-11-24 14:16:14 +00001136
dirk5a2575f2016-04-15 09:24:26 +02001137 FILE
1138 *cache_file;
cristyf034abb2013-11-24 14:16:14 +00001139
dirk5a2575f2016-04-15 09:24:26 +02001140 MagickCLDevice
1141 device;
cristyf034abb2013-11-24 14:16:14 +00001142
dirk5a2575f2016-04-15 09:24:26 +02001143 size_t
1144 i,
1145 j;
cristya22457d2013-12-07 14:03:06 +00001146
dirk5a2575f2016-04-15 09:24:26 +02001147 (void) FormatLocaleString(filename,MagickPathExtent,"%s%s%s",
1148 GetOpenCLCacheDirectory(),DirectorySeparator,
1149 IMAGEMAGICK_PROFILE_FILE);
1150
1151 cache_file=fopen_utf8(filename,"wb");
1152 if (cache_file == (FILE *) NULL)
1153 return;
1154 fwrite("<devices>\n",sizeof(char),10,cache_file);
1155 fprintf(cache_file," <device name=\"CPU\" score=\"%.4g\"/>\n",
1156 clEnv->cpu_score);
1157 for (i = 0; i < clEnv->number_devices; i++)
cristy0c832c62014-03-07 22:21:04 +00001158 {
dirk5a2575f2016-04-15 09:24:26 +02001159 MagickBooleanType
1160 duplicate;
cristy0c832c62014-03-07 22:21:04 +00001161
dirk5a2575f2016-04-15 09:24:26 +02001162 device=clEnv->devices[i];
1163 duplicate=MagickFalse;
1164 for (j = 0; j < i; j++)
1165 {
1166 if (IsSameOpenCLDevice(clEnv->devices[j],device))
1167 {
1168 duplicate=MagickTrue;
dirkb05dcc92014-08-27 15:30:53 +00001169 break;
1170 }
cristyf034abb2013-11-24 14:16:14 +00001171 }
cristyf034abb2013-11-24 14:16:14 +00001172
dirk5a2575f2016-04-15 09:24:26 +02001173 if (duplicate)
1174 continue;
dirkb05dcc92014-08-27 15:30:53 +00001175
dirk5a2575f2016-04-15 09:24:26 +02001176 if (device->score != MAGICKCORE_OPENCL_UNDEFINED_SCORE)
dirk9ca8c472016-09-20 21:40:52 +02001177 fprintf(cache_file," <device platform=\"%s\" vendor=\"%s\" name=\"%s\"\
1178 version=\"%s\" maxClockFrequency=\"%d\" maxComputeUnits=\"%d\"\
1179 score=\"%.4g\"/>\n",
1180 device->platform_name,device->vendor_name,device->name,device->version,
dirk8f012e52016-06-03 22:28:50 +02001181 (int)device->max_clock_frequency,(int)device->max_compute_units,
1182 device->score);
cristyf034abb2013-11-24 14:16:14 +00001183 }
dirk5a2575f2016-04-15 09:24:26 +02001184 fwrite("</devices>",sizeof(char),10,cache_file);
cristyf034abb2013-11-24 14:16:14 +00001185
dirk5a2575f2016-04-15 09:24:26 +02001186 fclose(cache_file);
cristyf034abb2013-11-24 14:16:14 +00001187}
1188
dirk5a2575f2016-04-15 09:24:26 +02001189static void BenchmarkOpenCLDevices(MagickCLEnv clEnv)
1190{
1191 MagickCLDevice
1192 device;
1193
1194 MagickCLEnv
1195 testEnv;
1196
1197 size_t
1198 i,
1199 j;
1200
Dirk Lemstracbbaf512019-08-29 22:23:14 +02001201 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
1202 "Starting benchmark");
dirk5a2575f2016-04-15 09:24:26 +02001203 testEnv=AcquireMagickCLEnv();
1204 testEnv->library=openCL_library;
Cristyd2b87b42017-09-30 09:42:04 -04001205 testEnv->devices=(MagickCLDevice *) AcquireCriticalMemory(
dirk5a2575f2016-04-15 09:24:26 +02001206 sizeof(MagickCLDevice));
1207 testEnv->number_devices=1;
1208 testEnv->benchmark_thread_id=GetMagickThreadId();
1209 testEnv->initialized=MagickTrue;
1210
1211 for (i = 0; i < clEnv->number_devices; i++)
1212 clEnv->devices[i]->score=MAGICKCORE_OPENCL_UNDEFINED_SCORE;
1213
1214 for (i = 0; i < clEnv->number_devices; i++)
1215 {
1216 device=clEnv->devices[i];
1217 if (device->score == MAGICKCORE_OPENCL_UNDEFINED_SCORE)
1218 RunDeviceBenckmark(clEnv,testEnv,device);
1219
1220 /* Set the score on all the other devices that are the same */
1221 for (j = i+1; j < clEnv->number_devices; j++)
1222 {
1223 MagickCLDevice
1224 other_device;
1225
1226 other_device=clEnv->devices[j];
1227 if (IsSameOpenCLDevice(device,other_device))
1228 other_device->score=device->score;
1229 }
1230 }
1231
1232 testEnv->enabled=MagickFalse;
1233 default_CLEnv=testEnv;
dirk21dc0312016-06-13 22:30:26 +02001234 clEnv->cpu_score=RunOpenCLBenchmark(MagickTrue);
dirk5a2575f2016-04-15 09:24:26 +02001235 default_CLEnv=clEnv;
1236
1237 testEnv=RelinquishMagickCLEnv(testEnv);
1238 CacheOpenCLBenchmarks(clEnv);
1239}
cristyf034abb2013-11-24 14:16:14 +00001240
1241/*
1242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1243% %
1244% %
1245% %
dirk5a2575f2016-04-15 09:24:26 +02001246% C o m p i l e O p e n C L K e r n e l %
cristyf034abb2013-11-24 14:16:14 +00001247% %
1248% %
1249% %
1250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1251%
dirk5a2575f2016-04-15 09:24:26 +02001252% CompileOpenCLKernel() compiles the kernel for the specified device. The
1253% kernel will be cached on disk to reduce the compilation time.
Cristy1dd96da2015-10-06 07:52:01 -04001254%
dirk5a2575f2016-04-15 09:24:26 +02001255% The format of the CompileOpenCLKernel method is:
cristyf034abb2013-11-24 14:16:14 +00001256%
dirk5a2575f2016-04-15 09:24:26 +02001257% MagickBooleanType AcquireOpenCLKernel(MagickCLDevice clEnv,
1258% unsigned int signature,const char *kernel,const char *options,
1259% ExceptionInfo *exception)
cristyf034abb2013-11-24 14:16:14 +00001260%
1261% A description of each parameter follows:
1262%
dirk5a2575f2016-04-15 09:24:26 +02001263% o device: the OpenCL device.
cristyf034abb2013-11-24 14:16:14 +00001264%
dirk5a2575f2016-04-15 09:24:26 +02001265% o kernel: the source code of the kernel.
cristyf034abb2013-11-24 14:16:14 +00001266%
dirk5a2575f2016-04-15 09:24:26 +02001267% o options: options for the compiler.
cristyf034abb2013-11-24 14:16:14 +00001268%
dirk5a2575f2016-04-15 09:24:26 +02001269% o signature: a number to uniquely identify the kernel
1270%
1271% o exception: return any errors or warnings in this structure.
cristyf034abb2013-11-24 14:16:14 +00001272%
1273*/
dirk5a2575f2016-04-15 09:24:26 +02001274
1275static void CacheOpenCLKernel(MagickCLDevice device,char *filename,
dirked7eb1e2013-12-04 05:53:08 +00001276 ExceptionInfo *exception)
1277{
dirk5a2575f2016-04-15 09:24:26 +02001278 cl_uint
1279 status;
cristyf034abb2013-11-24 14:16:14 +00001280
dirk5a2575f2016-04-15 09:24:26 +02001281 size_t
Cristy76eb72f2016-11-27 08:21:05 -05001282 binaryProgramSize;
1283
1284 unsigned char
1285 *binaryProgram;
cristyf034abb2013-11-24 14:16:14 +00001286
dirk5a2575f2016-04-15 09:24:26 +02001287 status=openCL_library->clGetProgramInfo(device->program,
Cristy76eb72f2016-11-27 08:21:05 -05001288 CL_PROGRAM_BINARY_SIZES,sizeof(size_t),&binaryProgramSize,NULL);
dirk5a2575f2016-04-15 09:24:26 +02001289 if (status != CL_SUCCESS)
1290 return;
Cristy566eaf12020-11-15 17:46:43 +00001291 binaryProgram=(unsigned char*) AcquireQuantumMemory(1,binaryProgramSize);
Dirk Lemstra22eec832017-10-04 06:55:57 +02001292 if (binaryProgram == (unsigned char *) NULL)
1293 {
1294 (void) ThrowMagickException(exception,GetMagickModule(),
1295 ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
1296 return;
1297 }
dirk5a2575f2016-04-15 09:24:26 +02001298 status=openCL_library->clGetProgramInfo(device->program,
Cristy76eb72f2016-11-27 08:21:05 -05001299 CL_PROGRAM_BINARIES,sizeof(unsigned char*),&binaryProgram,NULL);
dirk5a2575f2016-04-15 09:24:26 +02001300 if (status == CL_SUCCESS)
Dirk Lemstracbbaf512019-08-29 22:23:14 +02001301 {
1302 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
1303 "Creating cache file: \"%s\"",filename);
1304 (void) BlobToFile(filename,binaryProgram,binaryProgramSize,exception);
1305 }
Cristy76eb72f2016-11-27 08:21:05 -05001306 binaryProgram=(unsigned char *) RelinquishMagickMemory(binaryProgram);
cristyf034abb2013-11-24 14:16:14 +00001307}
1308
Dirk Lemstracbbaf512019-08-29 22:23:14 +02001309static MagickBooleanType LoadCachedOpenCLKernels(MagickCLDevice device,
dirk5a2575f2016-04-15 09:24:26 +02001310 const char *filename)
1311{
1312 cl_int
1313 binaryStatus,
1314 status;
cristyf034abb2013-11-24 14:16:14 +00001315
dirk5a2575f2016-04-15 09:24:26 +02001316 ExceptionInfo
Dirk Lemstra41c41332018-03-17 00:29:25 +01001317 *sans_exception;
dirk5a2575f2016-04-15 09:24:26 +02001318
1319 size_t
1320 length;
1321
1322 unsigned char
1323 *binaryProgram;
1324
Dirk Lemstra41c41332018-03-17 00:29:25 +01001325 sans_exception=AcquireExceptionInfo();
1326 binaryProgram=(unsigned char *) FileToBlob(filename,~0UL,&length,
1327 sans_exception);
1328 sans_exception=DestroyExceptionInfo(sans_exception);
dirk5a2575f2016-04-15 09:24:26 +02001329 if (binaryProgram == (unsigned char *) NULL)
1330 return(MagickFalse);
Dirk Lemstracbbaf512019-08-29 22:23:14 +02001331 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
1332 "Loaded cached kernels: \"%s\"",filename);
dirk5a2575f2016-04-15 09:24:26 +02001333 device->program=openCL_library->clCreateProgramWithBinary(device->context,1,
1334 &device->deviceID,&length,(const unsigned char**)&binaryProgram,
1335 &binaryStatus,&status);
1336 binaryProgram=(unsigned char *) RelinquishMagickMemory(binaryProgram);
1337 return((status != CL_SUCCESS) || (binaryStatus != CL_SUCCESS) ? MagickFalse :
1338 MagickTrue);
1339}
1340
dirk4a8f7242016-04-23 20:40:54 +02001341static void LogOpenCLBuildFailure(MagickCLDevice device,const char *kernel,
dirk5a2575f2016-04-15 09:24:26 +02001342 ExceptionInfo *exception)
1343{
1344 char
1345 filename[MagickPathExtent],
1346 *log;
1347
1348 size_t
Dirk Lemstra05a23672017-04-01 17:28:48 +02001349 log_size;
dirk5a2575f2016-04-15 09:24:26 +02001350
1351 (void) FormatLocaleString(filename,MagickPathExtent,"%s%s%s",
1352 GetOpenCLCacheDirectory(),DirectorySeparator,"magick_badcl.cl");
1353
dirk4a8f7242016-04-23 20:40:54 +02001354 (void) remove_utf8(filename);
dirk5a2575f2016-04-15 09:24:26 +02001355 (void) BlobToFile(filename,kernel,strlen(kernel),exception);
1356
1357 openCL_library->clGetProgramBuildInfo(device->program,device->deviceID,
Dirk Lemstra05a23672017-04-01 17:28:48 +02001358 CL_PROGRAM_BUILD_LOG,0,NULL,&log_size);
Cristy2f368e72017-09-30 09:44:14 -04001359 log=(char*)AcquireCriticalMemory(log_size);
dirk5a2575f2016-04-15 09:24:26 +02001360 openCL_library->clGetProgramBuildInfo(device->program,device->deviceID,
Dirk Lemstra05a23672017-04-01 17:28:48 +02001361 CL_PROGRAM_BUILD_LOG,log_size,log,&log_size);
dirk5a2575f2016-04-15 09:24:26 +02001362
1363 (void) FormatLocaleString(filename,MagickPathExtent,"%s%s%s",
1364 GetOpenCLCacheDirectory(),DirectorySeparator,"magick_badcl.log");
1365
dirk4a8f7242016-04-23 20:40:54 +02001366 (void) remove_utf8(filename);
Dirk Lemstra05a23672017-04-01 17:28:48 +02001367 (void) BlobToFile(filename,log,log_size,exception);
1368 log=(char*)RelinquishMagickMemory(log);
dirk5a2575f2016-04-15 09:24:26 +02001369}
1370
1371static MagickBooleanType CompileOpenCLKernel(MagickCLDevice device,
dirk8f012e52016-06-03 22:28:50 +02001372 const char *kernel,const char *options,size_t signature,
dirk5a2575f2016-04-15 09:24:26 +02001373 ExceptionInfo *exception)
1374{
1375 char
1376 deviceName[MagickPathExtent],
1377 filename[MagickPathExtent],
1378 *ptr;
1379
dirk5a2575f2016-04-15 09:24:26 +02001380 cl_int
1381 status;
1382
1383 MagickBooleanType
1384 loaded;
1385
1386 size_t
1387 length;
1388
1389 (void) CopyMagickString(deviceName,device->name,MagickPathExtent);
1390 ptr=deviceName;
1391 /* Strip out illegal characters for file names */
1392 while (*ptr != '\0')
1393 {
1394 if ((*ptr == ' ') || (*ptr == '\\') || (*ptr == '/') || (*ptr == ':') ||
1395 (*ptr == '*') || (*ptr == '?') || (*ptr == '"') || (*ptr == '<') ||
1396 (*ptr == '>' || *ptr == '|'))
1397 *ptr = '_';
1398 ptr++;
1399 }
1400 (void) FormatLocaleString(filename,MagickPathExtent,
1401 "%s%s%s_%s_%08x_%.20g.bin",GetOpenCLCacheDirectory(),
Cristy89e83492017-11-12 08:09:05 -05001402 DirectorySeparator,"magick_opencl",deviceName,(unsigned int) signature,
dirk5a2575f2016-04-15 09:24:26 +02001403 (double) sizeof(char*)*8);
Dirk Lemstracbbaf512019-08-29 22:23:14 +02001404 loaded=LoadCachedOpenCLKernels(device,filename);
dirk5a2575f2016-04-15 09:24:26 +02001405 if (loaded == MagickFalse)
1406 {
1407 /* Binary CL program unavailable, compile the program from source */
1408 length=strlen(kernel);
1409 device->program=openCL_library->clCreateProgramWithSource(
1410 device->context,1,&kernel,&length,&status);
1411 if (status != CL_SUCCESS)
1412 return(MagickFalse);
1413 }
1414
1415 status=openCL_library->clBuildProgram(device->program,1,&device->deviceID,
1416 options,NULL,NULL);
1417 if (status != CL_SUCCESS)
1418 {
1419 (void) ThrowMagickException(exception,GetMagickModule(),DelegateWarning,
1420 "clBuildProgram failed.","(%d)",(int)status);
dirk4a8f7242016-04-23 20:40:54 +02001421 LogOpenCLBuildFailure(device,kernel,exception);
dirk5a2575f2016-04-15 09:24:26 +02001422 return(MagickFalse);
1423 }
1424
1425 /* Save the binary to a file to avoid re-compilation of the kernels */
1426 if (loaded == MagickFalse)
1427 CacheOpenCLKernel(device,filename,exception);
1428
1429 return(MagickTrue);
1430}
1431
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001432static cl_event* CopyOpenCLEvents(MagickCLCacheInfo first,
1433 MagickCLCacheInfo second,cl_uint *event_count)
1434{
1435 cl_event
1436 *events;
1437
Cristyf2dc1dd2020-12-28 13:59:26 -05001438 size_t
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001439 i;
1440
1441 size_t
1442 j;
1443
1444 assert(first != (MagickCLCacheInfo) NULL);
1445 assert(event_count != (cl_uint *) NULL);
1446 events=(cl_event *) NULL;
1447 LockSemaphoreInfo(first->events_semaphore);
1448 if (second != (MagickCLCacheInfo) NULL)
1449 LockSemaphoreInfo(second->events_semaphore);
1450 *event_count=first->event_count;
1451 if (second != (MagickCLCacheInfo) NULL)
1452 *event_count+=second->event_count;
1453 if (*event_count > 0)
1454 {
1455 events=AcquireQuantumMemory(*event_count,sizeof(*events));
Dirk Lemstra8d83ff32017-10-04 06:58:33 +02001456 if (events == (cl_event *) NULL)
Dirk Lemstraa38d1652017-09-30 10:05:05 +02001457 *event_count=0;
1458 else
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001459 {
Dirk Lemstraa38d1652017-09-30 10:05:05 +02001460 j=0;
1461 for (i=0; i < first->event_count; i++, j++)
1462 events[j]=first->events[i];
1463 if (second != (MagickCLCacheInfo) NULL)
1464 {
1465 for (i=0; i < second->event_count; i++, j++)
1466 events[j]=second->events[i];
1467 }
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001468 }
1469 }
1470 UnlockSemaphoreInfo(first->events_semaphore);
1471 if (second != (MagickCLCacheInfo) NULL)
1472 UnlockSemaphoreInfo(second->events_semaphore);
1473 return(events);
1474}
1475
dirk5a2575f2016-04-15 09:24:26 +02001476/*
1477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1478% %
1479% %
1480% %
dirk21dc0312016-06-13 22:30:26 +02001481+ C o p y M a g i c k C L C a c h e I n f o %
1482% %
1483% %
1484% %
1485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486%
1487% CopyMagickCLCacheInfo() copies the memory from the device into host memory.
1488%
1489% The format of the CopyMagickCLCacheInfo method is:
1490%
1491% void CopyMagickCLCacheInfo(MagickCLCacheInfo info)
1492%
1493% A description of each parameter follows:
1494%
1495% o info: the OpenCL cache info.
1496%
1497*/
1498MagickPrivate MagickCLCacheInfo CopyMagickCLCacheInfo(MagickCLCacheInfo info)
1499{
1500 cl_command_queue
1501 queue;
1502
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001503 cl_event
1504 *events;
1505
1506 cl_uint
1507 event_count;
1508
dirk21dc0312016-06-13 22:30:26 +02001509 Quantum
1510 *pixels;
1511
dirka5135ba2016-06-19 12:33:28 +02001512 if (info == (MagickCLCacheInfo) NULL)
dirk21dc0312016-06-13 22:30:26 +02001513 return((MagickCLCacheInfo) NULL);
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001514 events=CopyOpenCLEvents(info,(MagickCLCacheInfo) NULL,&event_count);
1515 if (events != (cl_event *) NULL)
dirka5135ba2016-06-19 12:33:28 +02001516 {
1517 queue=AcquireOpenCLCommandQueue(info->device);
dirk66acef52016-06-21 00:02:08 +02001518 pixels=openCL_library->clEnqueueMapBuffer(queue,info->buffer,CL_TRUE,
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001519 CL_MAP_READ | CL_MAP_WRITE,0,info->length,event_count,events,
1520 (cl_event *) NULL,(cl_int *) NULL);
dirka5135ba2016-06-19 12:33:28 +02001521 assert(pixels == info->pixels);
dirk66acef52016-06-21 00:02:08 +02001522 ReleaseOpenCLCommandQueue(info->device,queue);
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001523 events=(cl_event *) RelinquishMagickMemory(events);
dirka5135ba2016-06-19 12:33:28 +02001524 }
dirk21dc0312016-06-13 22:30:26 +02001525 return(RelinquishMagickCLCacheInfo(info,MagickFalse));
1526}
1527
1528/*
1529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1530% %
1531% %
1532% %
1533+ D u m p O p e n C L P r o f i l e D a t a %
dirk5a2575f2016-04-15 09:24:26 +02001534% %
1535% %
1536% %
1537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1538%
1539% DumpOpenCLProfileData() dumps the kernel profile data.
1540%
1541% The format of the DumpProfileData method is:
1542%
1543% void DumpProfileData()
1544%
1545*/
1546
1547MagickPrivate void DumpOpenCLProfileData()
1548{
dirk5a2575f2016-04-15 09:24:26 +02001549#define OpenCLLog(message) \
1550 fwrite(message,sizeof(char),strlen(message),log); \
1551 fwrite("\n",sizeof(char),1,log);
1552
1553 char
1554 buf[4096],
1555 filename[MagickPathExtent],
1556 indent[160];
1557
1558 FILE
1559 *log;
1560
dirk5a2575f2016-04-15 09:24:26 +02001561 size_t
1562 i,
1563 j;
1564
Dirk Lemstra09d85392019-08-29 22:00:14 +02001565 if (default_CLEnv == (MagickCLEnv) NULL)
dirk01314602016-07-05 23:09:45 +02001566 return;
dirk5a2575f2016-04-15 09:24:26 +02001567
Dirk Lemstra09d85392019-08-29 22:00:14 +02001568 for (i = 0; i < default_CLEnv->number_devices; i++)
1569 if (default_CLEnv->devices[i]->profile_kernels != MagickFalse)
dirk7d42b3c2016-04-18 23:12:54 +02001570 break;
Dirk Lemstra09d85392019-08-29 22:00:14 +02001571 if (i == default_CLEnv->number_devices)
dirk7d42b3c2016-04-18 23:12:54 +02001572 return;
1573
dirk5a2575f2016-04-15 09:24:26 +02001574 (void) FormatLocaleString(filename,MagickPathExtent,"%s%s%s",
1575 GetOpenCLCacheDirectory(),DirectorySeparator,"ImageMagickOpenCL.log");
1576
Dirk Lemstra09d85392019-08-29 22:00:14 +02001577 log=fopen_utf8(filename,"wb");
malekremy05c12102019-04-06 12:37:45 -04001578 if (log == (FILE *) NULL)
1579 return;
Dirk Lemstra09d85392019-08-29 22:00:14 +02001580 for (i = 0; i < default_CLEnv->number_devices; i++)
dirk5a2575f2016-04-15 09:24:26 +02001581 {
1582 MagickCLDevice
1583 device;
1584
Dirk Lemstra09d85392019-08-29 22:00:14 +02001585 device=default_CLEnv->devices[i];
dirk7d42b3c2016-04-18 23:12:54 +02001586 if ((device->profile_kernels == MagickFalse) ||
1587 (device->profile_records == (KernelProfileRecord *) NULL))
1588 continue;
1589
dirk5a2575f2016-04-15 09:24:26 +02001590 OpenCLLog("====================================================");
1591 fprintf(log,"Device: %s\n",device->name);
1592 fprintf(log,"Version: %s\n",device->version);
1593 OpenCLLog("====================================================");
1594 OpenCLLog(" average calls min max");
1595 OpenCLLog(" ------- ----- --- ---");
dirk7d42b3c2016-04-18 23:12:54 +02001596 j=0;
1597 while (device->profile_records[j] != (KernelProfileRecord) NULL)
dirk5a2575f2016-04-15 09:24:26 +02001598 {
1599 KernelProfileRecord
1600 profile;
1601
dirk7d42b3c2016-04-18 23:12:54 +02001602 profile=device->profile_records[j];
dirk5a2575f2016-04-15 09:24:26 +02001603 strcpy(indent," ");
Dirk Lemstrace06cc52020-05-16 17:29:38 +02001604 CopyMagickString(indent,profile->kernel_name,MagickMin(strlen(
1605 profile->kernel_name),strlen(indent)));
dirk7d42b3c2016-04-18 23:12:54 +02001606 sprintf(buf,"%s %7d %7d %7d %7d",indent,(int) (profile->total/
1607 profile->count),(int) profile->count,(int) profile->min,
1608 (int) profile->max);
dirk5a2575f2016-04-15 09:24:26 +02001609 OpenCLLog(buf);
dirk7d42b3c2016-04-18 23:12:54 +02001610 j++;
dirk5a2575f2016-04-15 09:24:26 +02001611 }
1612 OpenCLLog("====================================================");
1613 fwrite("\n\n",sizeof(char),2,log);
1614 }
1615 fclose(log);
dirk5a2575f2016-04-15 09:24:26 +02001616}
dirk21dc0312016-06-13 22:30:26 +02001617/*
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619% %
1620% %
1621% %
1622+ E n q u e u e O p e n C L K e r n e l %
1623% %
1624% %
1625% %
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627%
1628% EnqueueOpenCLKernel() enques the specified kernel and registers the OpenCL
1629% events with the images.
1630%
1631% The format of the EnqueueOpenCLKernel method is:
1632%
1633% MagickBooleanType EnqueueOpenCLKernel(cl_kernel kernel,cl_uint work_dim,
1634% const size_t *global_work_offset,const size_t *global_work_size,
1635% const size_t *local_work_size,const Image *input_image,
1636% const Image *output_image,ExceptionInfo *exception)
1637%
1638% A description of each parameter follows:
1639%
1640% o kernel: the OpenCL kernel.
1641%
1642% o work_dim: the number of dimensions used to specify the global work-items
1643% and work-items in the work-group.
1644%
1645% o offset: can be used to specify an array of work_dim unsigned values
1646% that describe the offset used to calculate the global ID of a
1647% work-item.
1648%
1649% o gsize: points to an array of work_dim unsigned values that describe the
1650% number of global work-items in work_dim dimensions that will
1651% execute the kernel function.
1652%
1653% o lsize: points to an array of work_dim unsigned values that describe the
1654% number of work-items that make up a work-group that will execute
1655% the kernel specified by kernel.
1656%
1657% o input_image: the input image of the operation.
1658%
1659% o output_image: the output or secondairy image of the operation.
1660%
1661% o exception: return any errors or warnings in this structure.
1662%
1663*/
1664
Dirk Lemstra08b187b2017-10-10 20:05:18 +02001665static MagickBooleanType RegisterCacheEvent(MagickCLCacheInfo info,
1666 cl_event event)
dirk21dc0312016-06-13 22:30:26 +02001667{
1668 assert(info != (MagickCLCacheInfo) NULL);
1669 assert(event != (cl_event) NULL);
Dirk Lemstra08b187b2017-10-10 20:05:18 +02001670 if (openCL_library->clRetainEvent(event) != CL_SUCCESS)
1671 {
1672 openCL_library->clWaitForEvents(1,&event);
1673 return(MagickFalse);
1674 }
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001675 LockSemaphoreInfo(info->events_semaphore);
dirk21dc0312016-06-13 22:30:26 +02001676 if (info->events == (cl_event *) NULL)
1677 {
1678 info->events=AcquireMagickMemory(sizeof(*info->events));
1679 info->event_count=1;
1680 }
1681 else
1682 info->events=ResizeQuantumMemory(info->events,++info->event_count,
1683 sizeof(*info->events));
1684 if (info->events == (cl_event *) NULL)
1685 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1686 info->events[info->event_count-1]=event;
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001687 UnlockSemaphoreInfo(info->events_semaphore);
Dirk Lemstra08b187b2017-10-10 20:05:18 +02001688 return(MagickTrue);
dirk21dc0312016-06-13 22:30:26 +02001689}
1690
dirk66acef52016-06-21 00:02:08 +02001691MagickPrivate MagickBooleanType EnqueueOpenCLKernel(cl_command_queue queue,
1692 cl_kernel kernel,cl_uint work_dim,const size_t *offset,const size_t *gsize,
dirk21dc0312016-06-13 22:30:26 +02001693 const size_t *lsize,const Image *input_image,const Image *output_image,
dirkad91ea82016-09-20 22:38:49 +02001694 MagickBooleanType flush,ExceptionInfo *exception)
dirk21dc0312016-06-13 22:30:26 +02001695{
1696 CacheInfo
1697 *output_info,
1698 *input_info;
1699
dirk21dc0312016-06-13 22:30:26 +02001700 cl_event
1701 event,
1702 *events;
1703
1704 cl_int
1705 status;
1706
1707 cl_uint
1708 event_count;
1709
1710 assert(input_image != (const Image *) NULL);
1711 input_info=(CacheInfo *) input_image->cache;
1712 assert(input_info != (CacheInfo *) NULL);
1713 assert(input_info->opencl != (MagickCLCacheInfo) NULL);
dirk21dc0312016-06-13 22:30:26 +02001714 output_info=(CacheInfo *) NULL;
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001715 if (output_image == (const Image *) NULL)
1716 events=CopyOpenCLEvents(input_info->opencl,(MagickCLCacheInfo) NULL,
1717 &event_count);
1718 else
dirk21dc0312016-06-13 22:30:26 +02001719 {
1720 output_info=(CacheInfo *) output_image->cache;
1721 assert(output_info != (CacheInfo *) NULL);
1722 assert(output_info->opencl != (MagickCLCacheInfo) NULL);
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001723 events=CopyOpenCLEvents(input_info->opencl,output_info->opencl,
1724 &event_count);
dirk21dc0312016-06-13 22:30:26 +02001725 }
1726 status=openCL_library->clEnqueueNDRangeKernel(queue,kernel,work_dim,offset,
1727 gsize,lsize,event_count,events,&event);
Dirk Lemstra21ab7952017-04-06 12:46:19 +02001728 /* This can fail due to memory issues and calling clFinish might help. */
1729 if ((status != CL_SUCCESS) && (event_count > 0))
1730 {
1731 openCL_library->clFinish(queue);
1732 status=openCL_library->clEnqueueNDRangeKernel(queue,kernel,work_dim,
1733 offset,gsize,lsize,event_count,events,&event);
1734 }
Dirk Lemstraba78ae32017-09-28 15:23:55 +02001735 events=(cl_event *) RelinquishMagickMemory(events);
dirk21dc0312016-06-13 22:30:26 +02001736 if (status != CL_SUCCESS)
1737 {
1738 (void) OpenCLThrowMagickException(input_info->opencl->device,exception,
dirke1200372016-06-19 12:27:18 +02001739 GetMagickModule(),ResourceLimitWarning,
1740 "clEnqueueNDRangeKernel failed.","'%s'",".");
dirk21dc0312016-06-13 22:30:26 +02001741 return(MagickFalse);
1742 }
dirkad91ea82016-09-20 22:38:49 +02001743 if (flush != MagickFalse)
1744 openCL_library->clFlush(queue);
dirkf0ae4ef2016-06-21 21:47:46 +02001745 if (RecordProfileData(input_info->opencl->device,kernel,event) == MagickFalse)
1746 {
Dirk Lemstra08b187b2017-10-10 20:05:18 +02001747 if (RegisterCacheEvent(input_info->opencl,event) != MagickFalse)
1748 {
1749 if (output_info != (CacheInfo *) NULL)
1750 (void) RegisterCacheEvent(output_info->opencl,event);
1751 }
dirkf0ae4ef2016-06-21 21:47:46 +02001752 }
dirk21dc0312016-06-13 22:30:26 +02001753 openCL_library->clReleaseEvent(event);
1754 return(MagickTrue);
1755}
dirk5a2575f2016-04-15 09:24:26 +02001756
1757/*
1758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1759% %
1760% %
1761% %
Dirk Lemstra09d85392019-08-29 22:00:14 +02001762+ G e t C u r r e n t O p e n C L E n v %
dirk5a2575f2016-04-15 09:24:26 +02001763% %
1764% %
1765% %
1766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1767%
1768% GetCurrentOpenCLEnv() returns the current OpenCL env
1769%
1770% The format of the GetCurrentOpenCLEnv method is:
1771%
1772% MagickCLEnv GetCurrentOpenCLEnv()
1773%
1774*/
1775
1776MagickPrivate MagickCLEnv GetCurrentOpenCLEnv(void)
1777{
1778 if (default_CLEnv != (MagickCLEnv) NULL)
1779 {
1780 if ((default_CLEnv->benchmark_thread_id != (MagickThreadType) 0) &&
1781 (default_CLEnv->benchmark_thread_id != GetMagickThreadId()))
1782 return((MagickCLEnv) NULL);
1783 else
1784 return(default_CLEnv);
1785 }
1786
dirk01314602016-07-05 23:09:45 +02001787 if (GetOpenCLCacheDirectory() == (char *) NULL)
1788 return((MagickCLEnv) NULL);
1789
dirk21dc0312016-06-13 22:30:26 +02001790 if (openCL_lock == (SemaphoreInfo *) NULL)
1791 ActivateSemaphoreInfo(&openCL_lock);
dirk5a2575f2016-04-15 09:24:26 +02001792
dirk21dc0312016-06-13 22:30:26 +02001793 LockSemaphoreInfo(openCL_lock);
dirk5a2575f2016-04-15 09:24:26 +02001794 if (default_CLEnv == (MagickCLEnv) NULL)
1795 default_CLEnv=AcquireMagickCLEnv();
dirk21dc0312016-06-13 22:30:26 +02001796 UnlockSemaphoreInfo(openCL_lock);
dirk5a2575f2016-04-15 09:24:26 +02001797
1798 return(default_CLEnv);
1799}
1800
1801/*
1802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1803% %
1804% %
1805% %
dirk5a2575f2016-04-15 09:24:26 +02001806% G e t O p e n C L D e v i c e B e n c h m a r k D u r a t i o n %
1807% %
1808% %
1809% %
1810%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1811%
1812% GetOpenCLDeviceBenchmarkScore() returns the score of the benchmark for the
1813% device. The score is determined by the duration of the micro benchmark so
1814% that means a lower score is better than a higher score.
1815%
1816% The format of the GetOpenCLDeviceBenchmarkScore method is:
1817%
1818% double GetOpenCLDeviceBenchmarkScore(const MagickCLDevice device)
1819%
1820% A description of each parameter follows:
1821%
1822% o device: the OpenCL device.
1823*/
1824
1825MagickExport double GetOpenCLDeviceBenchmarkScore(
1826 const MagickCLDevice device)
1827{
1828 if (device == (MagickCLDevice) NULL)
1829 return(MAGICKCORE_OPENCL_UNDEFINED_SCORE);
1830 return(device->score);
1831}
1832
1833/*
1834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1835% %
1836% %
1837% %
1838% G e t O p e n C L D e v i c e E n a b l e d %
1839% %
1840% %
1841% %
1842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1843%
1844% GetOpenCLDeviceEnabled() returns true if the device is enabled.
1845%
1846% The format of the GetOpenCLDeviceEnabled method is:
1847%
1848% MagickBooleanType GetOpenCLDeviceEnabled(const MagickCLDevice device)
1849%
1850% A description of each parameter follows:
1851%
1852% o device: the OpenCL device.
1853*/
1854
1855MagickExport MagickBooleanType GetOpenCLDeviceEnabled(
1856 const MagickCLDevice device)
1857{
1858 if (device == (MagickCLDevice) NULL)
1859 return(MagickFalse);
1860 return(device->enabled);
1861}
1862
1863/*
1864%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1865% %
1866% %
1867% %
1868% G e t O p e n C L D e v i c e N a m e %
1869% %
1870% %
1871% %
1872%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1873%
1874% GetOpenCLDeviceName() returns the name of the device.
1875%
1876% The format of the GetOpenCLDeviceName method is:
1877%
1878% const char *GetOpenCLDeviceName(const MagickCLDevice device)
1879%
1880% A description of each parameter follows:
1881%
1882% o device: the OpenCL device.
1883*/
1884
1885MagickExport const char *GetOpenCLDeviceName(const MagickCLDevice device)
1886{
1887 if (device == (MagickCLDevice) NULL)
1888 return((const char *) NULL);
1889 return(device->name);
1890}
1891
1892/*
1893%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1894% %
1895% %
1896% %
Dirk Lemstra2aaab8f2016-12-06 23:06:35 +01001897% G e t O p e n C L D e v i c e V e n d o r N a m e %
1898% %
1899% %
1900% %
1901%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1902%
1903% GetOpenCLDeviceVendorName() returns the vendor name of the device.
1904%
1905% The format of the GetOpenCLDeviceVendorName method is:
1906%
1907% const char *GetOpenCLDeviceVendorName(const MagickCLDevice device)
1908%
1909% A description of each parameter follows:
1910%
1911% o device: the OpenCL device.
1912*/
1913
1914MagickExport const char *GetOpenCLDeviceVendorName(const MagickCLDevice device)
1915{
1916 if (device == (MagickCLDevice) NULL)
1917 return((const char *) NULL);
1918 return(device->vendor_name);
1919}
1920
1921/*
1922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1923% %
1924% %
1925% %
dirk5a2575f2016-04-15 09:24:26 +02001926% G e t O p e n C L D e v i c e s %
1927% %
1928% %
1929% %
1930%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1931%
1932% GetOpenCLDevices() returns the devices of the OpenCL environment at sets the
1933% value of length to the number of devices that are available.
1934%
1935% The format of the GetOpenCLDevices method is:
1936%
dirk7d42b3c2016-04-18 23:12:54 +02001937% const MagickCLDevice *GetOpenCLDevices(size_t *length,
1938% ExceptionInfo *exception)
dirk5a2575f2016-04-15 09:24:26 +02001939%
1940% A description of each parameter follows:
1941%
dirk7d42b3c2016-04-18 23:12:54 +02001942% o length: the number of device.
1943%
1944% o exception: return any errors or warnings in this structure.
1945%
dirk5a2575f2016-04-15 09:24:26 +02001946*/
1947
dirk7d42b3c2016-04-18 23:12:54 +02001948MagickExport MagickCLDevice *GetOpenCLDevices(size_t *length,
1949 ExceptionInfo *exception)
dirk5a2575f2016-04-15 09:24:26 +02001950{
1951 MagickCLEnv
1952 clEnv;
1953
1954 clEnv=GetCurrentOpenCLEnv();
1955 if (clEnv == (MagickCLEnv) NULL)
dirk7d42b3c2016-04-18 23:12:54 +02001956 {
1957 if (length != (size_t *) NULL)
1958 *length=0;
1959 return((MagickCLDevice *) NULL);
1960 }
1961 InitializeOpenCL(clEnv,exception);
dirk5a2575f2016-04-15 09:24:26 +02001962 if (length != (size_t *) NULL)
1963 *length=clEnv->number_devices;
1964 return(clEnv->devices);
1965}
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% G e t O p e n C L D e v i c e T y p e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% GetOpenCLDeviceType() returns the type of the device.
1979%
1980% The format of the GetOpenCLDeviceType method is:
1981%
1982% MagickCLDeviceType GetOpenCLDeviceType(const MagickCLDevice device)
1983%
1984% A description of each parameter follows:
1985%
1986% o device: the OpenCL device.
1987*/
1988
1989MagickExport MagickCLDeviceType GetOpenCLDeviceType(
1990 const MagickCLDevice device)
1991{
1992 if (device == (MagickCLDevice) NULL)
1993 return(UndefinedCLDeviceType);
1994 if (device->type == CL_DEVICE_TYPE_GPU)
1995 return(GpuCLDeviceType);
1996 if (device->type == CL_DEVICE_TYPE_CPU)
1997 return(CpuCLDeviceType);
1998 return(UndefinedCLDeviceType);
1999}
2000
2001/*
2002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2003% %
2004% %
2005% %
2006% G e t O p e n C L D e v i c e V e r s i o n %
2007% %
2008% %
2009% %
2010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2011%
2012% GetOpenCLDeviceVersion() returns the version of the device.
2013%
2014% The format of the GetOpenCLDeviceName method is:
2015%
dirk7d42b3c2016-04-18 23:12:54 +02002016% const char *GetOpenCLDeviceVersion(MagickCLDevice device)
dirk5a2575f2016-04-15 09:24:26 +02002017%
2018% A description of each parameter follows:
2019%
2020% o device: the OpenCL device.
2021*/
2022
2023MagickExport const char *GetOpenCLDeviceVersion(const MagickCLDevice device)
2024{
2025 if (device == (MagickCLDevice) NULL)
2026 return((const char *) NULL);
2027 return(device->version);
2028}
2029
2030/*
2031%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2032% %
2033% %
2034% %
2035% G e t O p e n C L E n a b l e d %
2036% %
2037% %
2038% %
2039%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2040%
2041% GetOpenCLEnabled() returns true if OpenCL acceleration is enabled.
2042%
2043% The format of the GetOpenCLEnabled method is:
2044%
2045% MagickBooleanType GetOpenCLEnabled()
2046%
2047*/
2048
2049MagickExport MagickBooleanType GetOpenCLEnabled(void)
2050{
2051 MagickCLEnv
2052 clEnv;
2053
2054 clEnv=GetCurrentOpenCLEnv();
2055 if (clEnv == (MagickCLEnv) NULL)
2056 return(MagickFalse);
2057 return(clEnv->enabled);
2058}
2059
2060/*
2061%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2062% %
2063% %
2064% %
dirk7d42b3c2016-04-18 23:12:54 +02002065% G e t O p e n C L K e r n e l P r o f i l e R e c o r d s %
2066% %
2067% %
2068% %
2069%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2070%
2071% GetOpenCLKernelProfileRecords() returns the profile records for the
2072% specified device and sets length to the number of profile records.
2073%
2074% The format of the GetOpenCLKernelProfileRecords method is:
2075%
2076% const KernelProfileRecord *GetOpenCLKernelProfileRecords(size *length)
2077%
2078% A description of each parameter follows:
2079%
2080% o length: the number of profiles records.
2081*/
2082
2083MagickExport const KernelProfileRecord *GetOpenCLKernelProfileRecords(
2084 const MagickCLDevice device,size_t *length)
2085{
2086 if ((device == (const MagickCLDevice) NULL) || (device->profile_records ==
2087 (KernelProfileRecord *) NULL))
2088 {
2089 if (length != (size_t *) NULL)
2090 *length=0;
2091 return((const KernelProfileRecord *) NULL);
2092 }
2093 if (length != (size_t *) NULL)
2094 {
dirk83b804e2016-04-21 22:49:23 +02002095 *length=0;
dirkd55bcc32016-04-22 17:43:02 +02002096 LockSemaphoreInfo(device->lock);
dirk7d42b3c2016-04-18 23:12:54 +02002097 while (device->profile_records[*length] != (KernelProfileRecord) NULL)
2098 *length=*length+1;
dirkd55bcc32016-04-22 17:43:02 +02002099 UnlockSemaphoreInfo(device->lock);
dirk7d42b3c2016-04-18 23:12:54 +02002100 }
2101 return(device->profile_records);
2102}
2103
2104/*
2105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2106% %
2107% %
2108% %
dirk5a2575f2016-04-15 09:24:26 +02002109% H a s O p e n C L D e v i c e s %
2110% %
2111% %
2112% %
2113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2114%
2115% HasOpenCLDevices() checks if the OpenCL environment has devices that are
2116% enabled and compiles the kernel for the device when necessary. False will be
2117% returned if no enabled devices could be found
2118%
2119% The format of the HasOpenCLDevices method is:
2120%
2121% MagickBooleanType HasOpenCLDevices(MagickCLEnv clEnv,
2122% ExceptionInfo exception)
2123%
2124% A description of each parameter follows:
2125%
2126% o clEnv: the OpenCL environment.
2127%
2128% o exception: return any errors or warnings in this structure.
2129%
2130*/
2131
2132static MagickBooleanType HasOpenCLDevices(MagickCLEnv clEnv,
2133 ExceptionInfo *exception)
2134{
2135 char
2136 *accelerateKernelsBuffer,
2137 options[MagickPathExtent];
2138
2139 MagickStatusType
2140 status;
2141
2142 size_t
2143 i;
2144
dirk8f012e52016-06-03 22:28:50 +02002145 size_t
dirk5a2575f2016-04-15 09:24:26 +02002146 signature;
2147
2148 /* Check if there are enabled devices */
2149 for (i = 0; i < clEnv->number_devices; i++)
2150 {
2151 if ((clEnv->devices[i]->enabled != MagickFalse))
2152 break;
2153 }
2154 if (i == clEnv->number_devices)
2155 return(MagickFalse);
2156
2157 /* Check if we need to compile a kernel for one of the devices */
dirkc5a1b3f2016-04-24 23:04:34 +02002158 status=MagickTrue;
dirk5a2575f2016-04-15 09:24:26 +02002159 for (i = 0; i < clEnv->number_devices; i++)
2160 {
2161 if ((clEnv->devices[i]->enabled != MagickFalse) &&
2162 (clEnv->devices[i]->program == (cl_program) NULL))
dirkc5a1b3f2016-04-24 23:04:34 +02002163 {
2164 status=MagickFalse;
dirk5a2575f2016-04-15 09:24:26 +02002165 break;
dirkc5a1b3f2016-04-24 23:04:34 +02002166 }
dirk5a2575f2016-04-15 09:24:26 +02002167 }
dirkc5a1b3f2016-04-24 23:04:34 +02002168 if (status != MagickFalse)
dirk5a2575f2016-04-15 09:24:26 +02002169 return(MagickTrue);
2170
2171 /* Get additional options */
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002172 (void) FormatLocaleString(options,MagickPathExtent,CLOptions,
dirk5a2575f2016-04-15 09:24:26 +02002173 (float)QuantumRange,(float)QuantumScale,(float)CLCharQuantumScale,
2174 (float)MagickEpsilon,(float)MagickPI,(unsigned int)MaxMap,
2175 (unsigned int)MAGICKCORE_QUANTUM_DEPTH);
2176
2177 signature=StringSignature(options);
Cristy566eaf12020-11-15 17:46:43 +00002178 accelerateKernelsBuffer=(char*) AcquireQuantumMemory(1,
dirk5a2575f2016-04-15 09:24:26 +02002179 strlen(accelerateKernels)+strlen(accelerateKernels2)+1);
2180 if (accelerateKernelsBuffer == (char*) NULL)
2181 return(MagickFalse);
2182 sprintf(accelerateKernelsBuffer,"%s%s",accelerateKernels,accelerateKernels2);
2183 signature^=StringSignature(accelerateKernelsBuffer);
2184
2185 status=MagickTrue;
2186 for (i = 0; i < clEnv->number_devices; i++)
2187 {
2188 MagickCLDevice
2189 device;
2190
dirk8f012e52016-06-03 22:28:50 +02002191 size_t
2192 device_signature;
2193
dirk5a2575f2016-04-15 09:24:26 +02002194 device=clEnv->devices[i];
2195 if ((device->enabled == MagickFalse) ||
2196 (device->program != (cl_program) NULL))
2197 continue;
2198
2199 LockSemaphoreInfo(device->lock);
2200 if (device->program != (cl_program) NULL)
2201 {
2202 UnlockSemaphoreInfo(device->lock);
2203 continue;
2204 }
dirk8f012e52016-06-03 22:28:50 +02002205 device_signature=signature;
dirk33f78c82016-06-19 12:36:11 +02002206 device_signature^=StringSignature(device->platform_name);
dirk5a2575f2016-04-15 09:24:26 +02002207 status=CompileOpenCLKernel(device,accelerateKernelsBuffer,options,
dirk8f012e52016-06-03 22:28:50 +02002208 device_signature,exception);
dirk5a2575f2016-04-15 09:24:26 +02002209 UnlockSemaphoreInfo(device->lock);
2210 if (status == MagickFalse)
2211 break;
2212 }
2213 accelerateKernelsBuffer=RelinquishMagickMemory(accelerateKernelsBuffer);
2214 return(status);
2215}
2216
2217/*
2218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2219% %
2220% %
2221% %
dirk21dc0312016-06-13 22:30:26 +02002222+ I n i t i a l i z e O p e n C L %
dirk5a2575f2016-04-15 09:24:26 +02002223% %
2224% %
2225% %
2226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2227%
2228% InitializeOpenCL() is used to initialize the OpenCL environment. This method
2229% makes sure the devices are propertly initialized and benchmarked.
2230%
2231% The format of the InitializeOpenCL method is:
2232%
2233% MagickBooleanType InitializeOpenCL(ExceptionInfo exception)
2234%
2235% A description of each parameter follows:
2236%
2237% o exception: return any errors or warnings in this structure.
2238%
2239*/
2240
2241static cl_uint GetOpenCLDeviceCount(MagickCLEnv clEnv,cl_platform_id platform)
2242{
2243 char
2244 version[MagickPathExtent];
2245
2246 cl_uint
2247 num;
2248
2249 if (clEnv->library->clGetPlatformInfo(platform,CL_PLATFORM_VERSION,
2250 MagickPathExtent,version,NULL) != CL_SUCCESS)
2251 return(0);
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002252 if (strncmp(version,"OpenCL 1.0 ",11) == 0)
dirk5a2575f2016-04-15 09:24:26 +02002253 return(0);
2254 if (clEnv->library->clGetDeviceIDs(platform,
2255 CL_DEVICE_TYPE_CPU|CL_DEVICE_TYPE_GPU,0,NULL,&num) != CL_SUCCESS)
2256 return(0);
2257 return(num);
2258}
2259
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002260static inline char *GetOpenCLPlatformString(cl_platform_id platform,
2261 cl_platform_info param_name)
2262{
2263 char
2264 *value;
2265
2266 size_t
2267 length;
2268
2269 openCL_library->clGetPlatformInfo(platform,param_name,0,NULL,&length);
2270 value=AcquireCriticalMemory(length*sizeof(*value));
2271 openCL_library->clGetPlatformInfo(platform,param_name,length,value,NULL);
2272 return(value);
2273}
2274
2275static inline char *GetOpenCLDeviceString(cl_device_id device,
2276 cl_device_info param_name)
2277{
2278 char
2279 *value;
2280
2281 size_t
2282 length;
2283
2284 openCL_library->clGetDeviceInfo(device,param_name,0,NULL,&length);
2285 value=AcquireCriticalMemory(length*sizeof(*value));
2286 openCL_library->clGetDeviceInfo(device,param_name,length,value,NULL);
2287 return(value);
2288}
2289
dirk5a2575f2016-04-15 09:24:26 +02002290static void LoadOpenCLDevices(MagickCLEnv clEnv)
2291{
dirk8f012e52016-06-03 22:28:50 +02002292 cl_context_properties
2293 properties[3];
2294
dirk5a2575f2016-04-15 09:24:26 +02002295 cl_device_id
2296 *devices;
2297
dirk8f012e52016-06-03 22:28:50 +02002298 cl_int
2299 status;
2300
dirk5a2575f2016-04-15 09:24:26 +02002301 cl_platform_id
2302 *platforms;
2303
2304 cl_uint
2305 i,
2306 j,
2307 next,
dirk8f012e52016-06-03 22:28:50 +02002308 number_devices,
dirk5a2575f2016-04-15 09:24:26 +02002309 number_platforms;
2310
2311 size_t
2312 length;
2313
2314 number_platforms=0;
2315 if (openCL_library->clGetPlatformIDs(0,NULL,&number_platforms) != CL_SUCCESS)
2316 return;
2317 if (number_platforms == 0)
2318 return;
Cristy566eaf12020-11-15 17:46:43 +00002319 platforms=(cl_platform_id *) AcquireQuantumMemory(1,number_platforms*
dirk5a2575f2016-04-15 09:24:26 +02002320 sizeof(cl_platform_id));
2321 if (platforms == (cl_platform_id *) NULL)
2322 return;
2323 if (openCL_library->clGetPlatformIDs(number_platforms,platforms,NULL) != CL_SUCCESS)
2324 {
2325 platforms=(cl_platform_id *) RelinquishMagickMemory(platforms);
2326 return;
2327 }
2328 for (i = 0; i < number_platforms; i++)
2329 {
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002330 char
2331 *platform_name;
2332
2333 number_devices=0;
2334 platform_name=GetOpenCLPlatformString(platforms[i],CL_PLATFORM_NAME);
2335 /* NVIDIA is disabled by default due to reported access violation */
2336 if (strncmp(platform_name,"NVIDIA",6) != 0)
2337 {
2338 number_devices=GetOpenCLDeviceCount(clEnv,platforms[i]);
2339 clEnv->number_devices+=number_devices;
2340 }
2341 platform_name=(char *) RelinquishMagickMemory(platform_name);
dirk8f012e52016-06-03 22:28:50 +02002342 if (number_devices == 0)
dirk5a2575f2016-04-15 09:24:26 +02002343 platforms[i]=(cl_platform_id) NULL;
dirk5a2575f2016-04-15 09:24:26 +02002344 }
2345 if (clEnv->number_devices == 0)
2346 {
2347 platforms=(cl_platform_id *) RelinquishMagickMemory(platforms);
2348 return;
2349 }
dirk8f012e52016-06-03 22:28:50 +02002350 clEnv->devices=(MagickCLDevice *) AcquireQuantumMemory(clEnv->number_devices,
dirk5a2575f2016-04-15 09:24:26 +02002351 sizeof(MagickCLDevice));
2352 if (clEnv->devices == (MagickCLDevice *) NULL)
2353 {
2354 RelinquishMagickCLDevices(clEnv);
2355 platforms=(cl_platform_id *) RelinquishMagickMemory(platforms);
2356 return;
2357 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002358 (void) memset(clEnv->devices,0,clEnv->number_devices*sizeof(MagickCLDevice));
dirk8f012e52016-06-03 22:28:50 +02002359 devices=(cl_device_id *) AcquireQuantumMemory(clEnv->number_devices,
dirk5a2575f2016-04-15 09:24:26 +02002360 sizeof(cl_device_id));
2361 if (devices == (cl_device_id *) NULL)
2362 {
2363 platforms=(cl_platform_id *) RelinquishMagickMemory(platforms);
2364 RelinquishMagickCLDevices(clEnv);
2365 return;
2366 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002367 (void) memset(devices,0,clEnv->number_devices*sizeof(cl_device_id));
dirk8f012e52016-06-03 22:28:50 +02002368 clEnv->number_contexts=(size_t) number_platforms;
2369 clEnv->contexts=(cl_context *) AcquireQuantumMemory(clEnv->number_contexts,
2370 sizeof(cl_context));
2371 if (clEnv->contexts == (cl_context *) NULL)
2372 {
2373 devices=(cl_device_id *) RelinquishMagickMemory(devices);
2374 platforms=(cl_platform_id *) RelinquishMagickMemory(platforms);
2375 RelinquishMagickCLDevices(clEnv);
2376 return;
2377 }
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002378 (void) memset(clEnv->contexts,0,clEnv->number_contexts*sizeof(cl_context));
dirk5a2575f2016-04-15 09:24:26 +02002379 next=0;
2380 for (i = 0; i < number_platforms; i++)
2381 {
2382 if (platforms[i] == (cl_platform_id) NULL)
2383 continue;
2384
Cristy9099bbb2018-05-26 06:19:13 -04002385 status=clEnv->library->clGetDeviceIDs(platforms[i],CL_DEVICE_TYPE_CPU |
dirkeb01bef2016-09-18 12:19:09 +02002386 CL_DEVICE_TYPE_GPU,(cl_uint) clEnv->number_devices,devices,&number_devices);
dirk8f012e52016-06-03 22:28:50 +02002387 if (status != CL_SUCCESS)
dirk5a2575f2016-04-15 09:24:26 +02002388 continue;
2389
dirk8f012e52016-06-03 22:28:50 +02002390 properties[0]=CL_CONTEXT_PLATFORM;
2391 properties[1]=(cl_context_properties) platforms[i];
2392 properties[2]=0;
2393 clEnv->contexts[i]=openCL_library->clCreateContext(properties,number_devices,
2394 devices,NULL,NULL,&status);
2395 if (status != CL_SUCCESS)
2396 continue;
2397
2398 for (j = 0; j < number_devices; j++,next++)
dirk5a2575f2016-04-15 09:24:26 +02002399 {
2400 MagickCLDevice
2401 device;
2402
2403 device=AcquireMagickCLDevice();
2404 if (device == (MagickCLDevice) NULL)
2405 break;
2406
dirk8f012e52016-06-03 22:28:50 +02002407 device->context=clEnv->contexts[i];
dirk5a2575f2016-04-15 09:24:26 +02002408 device->deviceID=devices[j];
dirk5a2575f2016-04-15 09:24:26 +02002409
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002410 device->platform_name=GetOpenCLPlatformString(platforms[i],
2411 CL_PLATFORM_NAME);
dirk8f012e52016-06-03 22:28:50 +02002412
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002413 device->vendor_name=GetOpenCLPlatformString(platforms[i],
2414 CL_PLATFORM_VENDOR);
dirk9ca8c472016-09-20 21:40:52 +02002415
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002416 device->name=GetOpenCLDeviceString(devices[j],CL_DEVICE_NAME);
dirk5a2575f2016-04-15 09:24:26 +02002417
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002418 device->version=GetOpenCLDeviceString(devices[j],CL_DRIVER_VERSION);
dirk5a2575f2016-04-15 09:24:26 +02002419
2420 openCL_library->clGetDeviceInfo(devices[j],CL_DEVICE_MAX_CLOCK_FREQUENCY,
2421 sizeof(cl_uint),&device->max_clock_frequency,NULL);
2422
2423 openCL_library->clGetDeviceInfo(devices[j],CL_DEVICE_MAX_COMPUTE_UNITS,
2424 sizeof(cl_uint),&device->max_compute_units,NULL);
2425
2426 openCL_library->clGetDeviceInfo(devices[j],CL_DEVICE_TYPE,
2427 sizeof(cl_device_type),&device->type,NULL);
2428
2429 openCL_library->clGetDeviceInfo(devices[j],CL_DEVICE_LOCAL_MEM_SIZE,
2430 sizeof(cl_ulong),&device->local_memory_size,NULL);
2431
2432 clEnv->devices[next]=device;
Dirk Lemstraec839642019-08-29 22:31:10 +02002433 (void) LogMagickEvent(AccelerateEvent,GetMagickModule(),
Dirk Lemstra3b6c93b2020-05-17 16:08:12 +02002434 "Found device: %s (%s)",device->name,device->platform_name);
dirk5a2575f2016-04-15 09:24:26 +02002435 }
2436 }
2437 if (next != clEnv->number_devices)
2438 RelinquishMagickCLDevices(clEnv);
2439 platforms=(cl_platform_id *) RelinquishMagickMemory(platforms);
2440 devices=(cl_device_id *) RelinquishMagickMemory(devices);
2441}
2442
2443MagickPrivate MagickBooleanType InitializeOpenCL(MagickCLEnv clEnv,
2444 ExceptionInfo *exception)
2445{
Dirk Lemstra506256c2018-04-17 21:09:12 +02002446 register
2447 size_t i;
2448
dirk5a2575f2016-04-15 09:24:26 +02002449 LockSemaphoreInfo(clEnv->lock);
2450 if (clEnv->initialized != MagickFalse)
2451 {
2452 UnlockSemaphoreInfo(clEnv->lock);
2453 return(HasOpenCLDevices(clEnv,exception));
2454 }
2455 if (LoadOpenCLLibrary() != MagickFalse)
2456 {
2457 clEnv->library=openCL_library;
2458 LoadOpenCLDevices(clEnv);
2459 if (clEnv->number_devices > 0)
dirk3eec1182016-08-14 19:57:09 +02002460 AutoSelectOpenCLDevices(clEnv);
dirk5a2575f2016-04-15 09:24:26 +02002461 }
2462 clEnv->initialized=MagickTrue;
Dirk Lemstra506256c2018-04-17 21:09:12 +02002463 /* NVIDIA is disabled by default due to reported access violation */
2464 for (i=0; i < (ssize_t) clEnv->number_devices; i++)
2465 {
2466 if (strncmp(clEnv->devices[i]->platform_name,"NVIDIA",6) == 0)
Dirk Lemstra9c43e2a2018-09-21 19:45:40 +02002467 clEnv->devices[i]->enabled=MagickFalse;
Dirk Lemstra506256c2018-04-17 21:09:12 +02002468 }
dirk5a2575f2016-04-15 09:24:26 +02002469 UnlockSemaphoreInfo(clEnv->lock);
2470 return(HasOpenCLDevices(clEnv,exception));
2471}
2472
2473/*
2474%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2475% %
2476% %
2477% %
2478% L o a d O p e n C L L i b r a r y %
2479% %
2480% %
2481% %
2482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2483%
2484% LoadOpenCLLibrary() load and binds the OpenCL library.
2485%
2486% The format of the LoadOpenCLLibrary method is:
2487%
2488% MagickBooleanType LoadOpenCLLibrary(void)
2489%
2490*/
2491
2492void *OsLibraryGetFunctionAddress(void *library,const char *functionName)
2493{
2494 if ((library == (void *) NULL) || (functionName == (const char *) NULL))
2495 return (void *) NULL;
2496#ifdef MAGICKCORE_WINDOWS_SUPPORT
2497 return (void *) GetProcAddress((HMODULE)library,functionName);
2498#else
2499 return (void *) dlsym(library,functionName);
2500#endif
2501}
2502
dirk8f012e52016-06-03 22:28:50 +02002503static MagickBooleanType BindOpenCLFunctions()
dirk5a2575f2016-04-15 09:24:26 +02002504{
dirk5a2575f2016-04-15 09:24:26 +02002505#ifdef MAGICKCORE_OPENCL_MACOSX
2506#define BIND(X) openCL_library->X= &X;
2507#else
Cristy81bfff22018-03-10 07:58:31 -05002508 (void) memset(openCL_library,0,sizeof(MagickLibrary));
dirk5a2575f2016-04-15 09:24:26 +02002509#ifdef MAGICKCORE_WINDOWS_SUPPORT
dirk51a66f32016-10-08 09:01:00 +02002510 openCL_library->library=(void *)LoadLibraryA("OpenCL.dll");
dirk5a2575f2016-04-15 09:24:26 +02002511#else
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002512 openCL_library->library=(void *)dlopen("libOpenCL.so",RTLD_NOW);
dirk5a2575f2016-04-15 09:24:26 +02002513#endif
dirk5a2575f2016-04-15 09:24:26 +02002514#define BIND(X) \
dirk51a66f32016-10-08 09:01:00 +02002515 if ((openCL_library->X=(MAGICKpfn_##X)OsLibraryGetFunctionAddress(openCL_library->library,#X)) == NULL) \
dirk5a2575f2016-04-15 09:24:26 +02002516 return(MagickFalse);
2517#endif
2518
dirk51a66f32016-10-08 09:01:00 +02002519 if (openCL_library->library == (void*) NULL)
2520 return(MagickFalse);
2521
dirk5a2575f2016-04-15 09:24:26 +02002522 BIND(clGetPlatformIDs);
2523 BIND(clGetPlatformInfo);
2524
2525 BIND(clGetDeviceIDs);
2526 BIND(clGetDeviceInfo);
2527
2528 BIND(clCreateBuffer);
2529 BIND(clReleaseMemObject);
Dirk Lemstraf65e2912017-10-25 07:22:41 +02002530 BIND(clRetainMemObject);
dirk5a2575f2016-04-15 09:24:26 +02002531
2532 BIND(clCreateContext);
2533 BIND(clReleaseContext);
2534
2535 BIND(clCreateCommandQueue);
2536 BIND(clReleaseCommandQueue);
dirk66acef52016-06-21 00:02:08 +02002537 BIND(clFlush);
2538 BIND(clFinish);
dirk5a2575f2016-04-15 09:24:26 +02002539
2540 BIND(clCreateProgramWithSource);
2541 BIND(clCreateProgramWithBinary);
2542 BIND(clReleaseProgram);
2543 BIND(clBuildProgram);
2544 BIND(clGetProgramBuildInfo);
2545 BIND(clGetProgramInfo);
2546
2547 BIND(clCreateKernel);
2548 BIND(clReleaseKernel);
2549 BIND(clSetKernelArg);
dirk7d42b3c2016-04-18 23:12:54 +02002550 BIND(clGetKernelInfo);
dirk5a2575f2016-04-15 09:24:26 +02002551
2552 BIND(clEnqueueReadBuffer);
2553 BIND(clEnqueueMapBuffer);
2554 BIND(clEnqueueUnmapMemObject);
2555 BIND(clEnqueueNDRangeKernel);
2556
Dirk Lemstra27b44902017-02-27 22:06:33 +01002557 BIND(clGetEventInfo);
dirk5a2575f2016-04-15 09:24:26 +02002558 BIND(clWaitForEvents);
2559 BIND(clReleaseEvent);
dirk21dc0312016-06-13 22:30:26 +02002560 BIND(clRetainEvent);
2561 BIND(clSetEventCallback);
2562
2563 BIND(clGetEventProfilingInfo);
dirk5a2575f2016-04-15 09:24:26 +02002564
dirk5a2575f2016-04-15 09:24:26 +02002565 return(MagickTrue);
2566}
2567
2568static MagickBooleanType LoadOpenCLLibrary(void)
2569{
Cristy8357b5d2020-11-22 12:39:10 +00002570 openCL_library=(MagickLibrary *) AcquireMagickMemory(sizeof(MagickLibrary));
dirk5a2575f2016-04-15 09:24:26 +02002571 if (openCL_library == (MagickLibrary *) NULL)
2572 return(MagickFalse);
2573
dirk8f012e52016-06-03 22:28:50 +02002574 if (BindOpenCLFunctions() == MagickFalse)
dirk5a2575f2016-04-15 09:24:26 +02002575 {
2576 openCL_library=(MagickLibrary *)RelinquishMagickMemory(openCL_library);
2577 return(MagickFalse);
2578 }
2579
2580 return(MagickTrue);
2581}
2582
2583/*
2584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2585% %
2586% %
2587% %
dirk21dc0312016-06-13 22:30:26 +02002588+ O p e n C L T e r m i n u s %
dirk5a2575f2016-04-15 09:24:26 +02002589% %
2590% %
2591% %
2592%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2593%
dirk51a66f32016-10-08 09:01:00 +02002594% OpenCLTerminus() destroys the OpenCL component.
dirk5a2575f2016-04-15 09:24:26 +02002595%
dirk51a66f32016-10-08 09:01:00 +02002596% The format of the OpenCLTerminus method is:
dirk5a2575f2016-04-15 09:24:26 +02002597%
dirk51a66f32016-10-08 09:01:00 +02002598% OpenCLTerminus(void)
dirk5a2575f2016-04-15 09:24:26 +02002599%
2600*/
2601
2602MagickPrivate void OpenCLTerminus()
2603{
2604 DumpOpenCLProfileData();
2605 if (cache_directory != (char *) NULL)
2606 cache_directory=DestroyString(cache_directory);
2607 if (cache_directory_lock != (SemaphoreInfo *) NULL)
2608 RelinquishSemaphoreInfo(&cache_directory_lock);
2609 if (default_CLEnv != (MagickCLEnv) NULL)
2610 default_CLEnv=RelinquishMagickCLEnv(default_CLEnv);
dirk21dc0312016-06-13 22:30:26 +02002611 if (openCL_lock != (SemaphoreInfo *) NULL)
2612 RelinquishSemaphoreInfo(&openCL_lock);
dirk5a2575f2016-04-15 09:24:26 +02002613 if (openCL_library != (MagickLibrary *) NULL)
dirk51a66f32016-10-08 09:01:00 +02002614 {
2615 if (openCL_library->library != (void *) NULL)
2616 (void) lt_dlclose(openCL_library->library);
2617 openCL_library=(MagickLibrary *) RelinquishMagickMemory(openCL_library);
2618 }
dirk5a2575f2016-04-15 09:24:26 +02002619}
2620
2621/*
2622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2623% %
2624% %
2625% %
dirk21dc0312016-06-13 22:30:26 +02002626+ O p e n C L T h r o w M a g i c k E x c e p t i o n %
dirk5a2575f2016-04-15 09:24:26 +02002627% %
2628% %
2629% %
2630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2631%
2632% OpenCLThrowMagickException logs an OpenCL exception as determined by the log
2633% configuration file. If an error occurs, MagickFalse is returned
2634% otherwise MagickTrue.
2635%
2636% The format of the OpenCLThrowMagickException method is:
2637%
Dirk Lemstrab7514012018-03-17 00:38:48 +01002638% MagickBooleanType OpenCLThrowMagickException(ExceptionInfo *exception,
dirk5a2575f2016-04-15 09:24:26 +02002639% const char *module,const char *function,const size_t line,
2640% const ExceptionType severity,const char *tag,const char *format,...)
2641%
2642% A description of each parameter follows:
2643%
2644% o exception: the exception info.
2645%
2646% o filename: the source module filename.
2647%
2648% o function: the function name.
2649%
2650% o line: the line number of the source module.
2651%
2652% o severity: Specifies the numeric error category.
2653%
2654% o tag: the locale tag.
2655%
2656% o format: the output format.
2657%
2658*/
2659
2660MagickPrivate MagickBooleanType OpenCLThrowMagickException(
2661 MagickCLDevice device,ExceptionInfo *exception,const char *module,
2662 const char *function,const size_t line,const ExceptionType severity,
2663 const char *tag,const char *format,...)
2664{
cristya22457d2013-12-07 14:03:06 +00002665 MagickBooleanType
2666 status;
2667
dirk5a2575f2016-04-15 09:24:26 +02002668 assert(device != (MagickCLDevice) NULL);
cristya22457d2013-12-07 14:03:06 +00002669 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002670 assert(exception->signature == MagickCoreSignature);
Cristy0a6f7282020-02-09 13:18:36 -05002671 (void) exception;
dirk5a2575f2016-04-15 09:24:26 +02002672 status=MagickTrue;
2673 if (severity != 0)
2674 {
2675 if (device->type == CL_DEVICE_TYPE_CPU)
2676 {
cristya22457d2013-12-07 14:03:06 +00002677 /* Workaround for Intel OpenCL CPU runtime bug */
2678 /* Turn off OpenCL when a problem is detected! */
Elliott Hughes5d41fba2021-04-12 16:36:42 -07002679 if (strncmp(device->platform_name,"Intel",5) == 0)
dirk5a2575f2016-04-15 09:24:26 +02002680 default_CLEnv->enabled=MagickFalse;
cristya22457d2013-12-07 14:03:06 +00002681 }
2682 }
2683
2684#ifdef OPENCLLOG_ENABLED
2685 {
2686 va_list
2687 operands;
2688 va_start(operands,format);
dirk5a2575f2016-04-15 09:24:26 +02002689 status=ThrowMagickExceptionList(exception,module,function,line,severity,tag,
2690 format,operands);
cristya22457d2013-12-07 14:03:06 +00002691 va_end(operands);
2692 }
2693#else
2694 magick_unreferenced(module);
2695 magick_unreferenced(function);
2696 magick_unreferenced(line);
2697 magick_unreferenced(tag);
2698 magick_unreferenced(format);
2699#endif
2700
2701 return(status);
2702}
2703
dirk5a2575f2016-04-15 09:24:26 +02002704/*
2705%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2706% %
2707% %
2708% %
dirk21dc0312016-06-13 22:30:26 +02002709+ R e c o r d P r o f i l e D a t a %
dirk5a2575f2016-04-15 09:24:26 +02002710% %
2711% %
2712% %
2713%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2714%
2715% RecordProfileData() records profile data.
2716%
2717% The format of the RecordProfileData method is:
2718%
2719% void RecordProfileData(MagickCLDevice device,ProfiledKernels kernel,
2720% cl_event event)
2721%
2722% A description of each parameter follows:
2723%
2724% o device: the OpenCL device that did the operation.
2725%
dirk5a2575f2016-04-15 09:24:26 +02002726% o event: the event that contains the profiling data.
2727%
2728*/
2729
dirkf0ae4ef2016-06-21 21:47:46 +02002730MagickPrivate MagickBooleanType RecordProfileData(MagickCLDevice device,
dirk7d42b3c2016-04-18 23:12:54 +02002731 cl_kernel kernel,cl_event event)
Cristy1dd96da2015-10-06 07:52:01 -04002732{
dirk7d42b3c2016-04-18 23:12:54 +02002733 char
2734 *name;
2735
dirk5a2575f2016-04-15 09:24:26 +02002736 cl_int
2737 status;
cristy0c832c62014-03-07 22:21:04 +00002738
dirk5a2575f2016-04-15 09:24:26 +02002739 cl_ulong
2740 elapsed,
2741 end,
2742 start;
cristy0c832c62014-03-07 22:21:04 +00002743
dirk7d42b3c2016-04-18 23:12:54 +02002744 KernelProfileRecord
2745 profile_record;
2746
2747 size_t
2748 i,
2749 length;
2750
2751 if (device->profile_kernels == MagickFalse)
dirkf0ae4ef2016-06-21 21:47:46 +02002752 return(MagickFalse);
2753 status=openCL_library->clWaitForEvents(1,&event);
2754 if (status != CL_SUCCESS)
2755 return(MagickFalse);
dirk7d42b3c2016-04-18 23:12:54 +02002756 status=openCL_library->clGetKernelInfo(kernel,CL_KERNEL_FUNCTION_NAME,0,NULL,
2757 &length);
2758 if (status != CL_SUCCESS)
dirkf0ae4ef2016-06-21 21:47:46 +02002759 return(MagickTrue);
dirk7d42b3c2016-04-18 23:12:54 +02002760 name=AcquireQuantumMemory(length,sizeof(*name));
dirkf0ae4ef2016-06-21 21:47:46 +02002761 if (name == (char *) NULL)
2762 return(MagickTrue);
2763 start=end=elapsed=0;
dirk66acef52016-06-21 00:02:08 +02002764 status=openCL_library->clGetKernelInfo(kernel,CL_KERNEL_FUNCTION_NAME,length,
2765 name,(size_t *) NULL);
dirk66acef52016-06-21 00:02:08 +02002766 status|=openCL_library->clGetEventProfilingInfo(event,
dirk5a2575f2016-04-15 09:24:26 +02002767 CL_PROFILING_COMMAND_START,sizeof(cl_ulong),&start,NULL);
dirk66acef52016-06-21 00:02:08 +02002768 status|=openCL_library->clGetEventProfilingInfo(event,
dirk5a2575f2016-04-15 09:24:26 +02002769 CL_PROFILING_COMMAND_END,sizeof(cl_ulong),&end,NULL);
2770 if (status != CL_SUCCESS)
dirk7d42b3c2016-04-18 23:12:54 +02002771 {
2772 name=DestroyString(name);
dirkf0ae4ef2016-06-21 21:47:46 +02002773 return(MagickTrue);
dirk7d42b3c2016-04-18 23:12:54 +02002774 }
Cristycab28072018-07-29 18:55:33 -04002775 start/=1000; /* usecs */
2776 end/=1000;
dirk5a2575f2016-04-15 09:24:26 +02002777 elapsed=end-start;
2778 LockSemaphoreInfo(device->lock);
dirk7d42b3c2016-04-18 23:12:54 +02002779 i=0;
2780 profile_record=(KernelProfileRecord) NULL;
2781 if (device->profile_records != (KernelProfileRecord *) NULL)
2782 {
dirkf0ae4ef2016-06-21 21:47:46 +02002783 while (device->profile_records[i] != (KernelProfileRecord) NULL)
dirk7d42b3c2016-04-18 23:12:54 +02002784 {
dirk83b804e2016-04-21 22:49:23 +02002785 if (LocaleCompare(device->profile_records[i]->kernel_name,name) == 0)
dirk7d42b3c2016-04-18 23:12:54 +02002786 {
2787 profile_record=device->profile_records[i];
2788 break;
2789 }
2790 i++;
2791 }
2792 }
dirkf0ae4ef2016-06-21 21:47:46 +02002793 if (profile_record != (KernelProfileRecord) NULL)
2794 name=DestroyString(name);
2795 else
dirk7d42b3c2016-04-18 23:12:54 +02002796 {
Cristy22434f82018-03-24 12:02:52 -04002797 profile_record=AcquireCriticalMemory(sizeof(*profile_record));
Cristy81bfff22018-03-10 07:58:31 -05002798 (void) memset(profile_record,0,sizeof(*profile_record));
dirkf0ae4ef2016-06-21 21:47:46 +02002799 profile_record->kernel_name=name;
Cristy566eaf12020-11-15 17:46:43 +00002800 device->profile_records=ResizeQuantumMemory(device->profile_records,(i+2),
dirk66acef52016-06-21 00:02:08 +02002801 sizeof(*device->profile_records));
Cristy22434f82018-03-24 12:02:52 -04002802 if (device->profile_records == (KernelProfileRecord *) NULL)
2803 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
dirk7d42b3c2016-04-18 23:12:54 +02002804 device->profile_records[i]=profile_record;
2805 device->profile_records[i+1]=(KernelProfileRecord) NULL;
2806 }
2807 if ((elapsed < profile_record->min) || (profile_record->count == 0))
2808 profile_record->min=elapsed;
2809 if (elapsed > profile_record->max)
2810 profile_record->max=elapsed;
2811 profile_record->total+=elapsed;
2812 profile_record->count+=1;
dirk5a2575f2016-04-15 09:24:26 +02002813 UnlockSemaphoreInfo(device->lock);
dirkf0ae4ef2016-06-21 21:47:46 +02002814 return(MagickTrue);
cristyf034abb2013-11-24 14:16:14 +00002815}
2816
2817/*
dirk5a2575f2016-04-15 09:24:26 +02002818%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2819% %
2820% %
2821% %
dirk66acef52016-06-21 00:02:08 +02002822+ R e l e a s e O p e n C L C o m m a n d Q u e u e %
2823% %
2824% %
2825% %
2826%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2827%
2828% ReleaseOpenCLCommandQueue() releases the OpenCL command queue
2829%
2830% The format of the ReleaseOpenCLCommandQueue method is:
2831%
2832% void ReleaseOpenCLCommandQueue(MagickCLDevice device,
2833% cl_command_queue queue)
2834%
2835% A description of each parameter follows:
2836%
2837% o device: the OpenCL device.
2838%
2839% o queue: the OpenCL queue to be released.
2840*/
2841
2842MagickPrivate void ReleaseOpenCLCommandQueue(MagickCLDevice device,
2843 cl_command_queue queue)
2844{
2845 if (queue == (cl_command_queue) NULL)
2846 return;
2847
2848 assert(device != (MagickCLDevice) NULL);
2849 LockSemaphoreInfo(device->lock);
2850 if ((device->profile_kernels != MagickFalse) ||
2851 (device->command_queues_index >= MAGICKCORE_OPENCL_COMMAND_QUEUES-1))
2852 {
2853 UnlockSemaphoreInfo(device->lock);
2854 openCL_library->clFinish(queue);
2855 (void) openCL_library->clReleaseCommandQueue(queue);
2856 }
2857 else
2858 {
2859 openCL_library->clFlush(queue);
2860 device->command_queues[++device->command_queues_index]=queue;
2861 UnlockSemaphoreInfo(device->lock);
2862 }
2863}
2864
2865/*
2866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2867% %
2868% %
2869% %
dirk21dc0312016-06-13 22:30:26 +02002870+ R e l e a s e M a g i c k C L D e v i c e %
dirkc5a1b3f2016-04-24 23:04:34 +02002871% %
2872% %
2873% %
2874%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2875%
2876% ReleaseOpenCLDevice() returns the OpenCL device to the environment
2877%
2878% The format of the ReleaseOpenCLDevice method is:
2879%
dirk21dc0312016-06-13 22:30:26 +02002880% void ReleaseOpenCLDevice(MagickCLDevice device)
dirkc5a1b3f2016-04-24 23:04:34 +02002881%
2882% A description of each parameter follows:
2883%
dirkc5a1b3f2016-04-24 23:04:34 +02002884% o device: the OpenCL device to be released.
2885%
2886*/
2887
dirk21dc0312016-06-13 22:30:26 +02002888MagickPrivate void ReleaseOpenCLDevice(MagickCLDevice device)
dirkc5a1b3f2016-04-24 23:04:34 +02002889{
dirk21dc0312016-06-13 22:30:26 +02002890 assert(device != (MagickCLDevice) NULL);
2891 LockSemaphoreInfo(openCL_lock);
dirkc5a1b3f2016-04-24 23:04:34 +02002892 device->requested--;
dirk21dc0312016-06-13 22:30:26 +02002893 UnlockSemaphoreInfo(openCL_lock);
2894}
2895
2896/*
2897%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2898% %
2899% %
2900% %
2901+ R e l i n q u i s h M a g i c k C L C a c h e I n f o %
2902% %
2903% %
2904% %
2905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2906%
2907% RelinquishMagickCLCacheInfo() frees memory acquired with
2908% AcquireMagickCLCacheInfo()
2909%
2910% The format of the RelinquishMagickCLCacheInfo method is:
2911%
2912% MagickCLCacheInfo RelinquishMagickCLCacheInfo(MagickCLCacheInfo info,
2913% const MagickBooleanType relinquish_pixels)
2914%
2915% A description of each parameter follows:
2916%
2917% o info: the OpenCL cache info.
2918%
2919% o relinquish_pixels: the pixels will be relinquish when set to true.
2920%
2921*/
dirk5fb23952016-06-19 12:35:35 +02002922
2923static void CL_API_CALL DestroyMagickCLCacheInfoAndPixels(
2924 cl_event magick_unused(event),
2925 cl_int magick_unused(event_command_exec_status),void *user_data)
dirk21dc0312016-06-13 22:30:26 +02002926{
2927 MagickCLCacheInfo
2928 info;
2929
Dirk Lemstra3df73bb2017-02-27 22:03:20 +01002930 Quantum
2931 *pixels;
2932
Dirk Lemstracf250862017-10-15 09:29:32 +02002933 ssize_t
2934 i;
2935
dirk21dc0312016-06-13 22:30:26 +02002936 magick_unreferenced(event);
2937 magick_unreferenced(event_command_exec_status);
2938 info=(MagickCLCacheInfo) user_data;
Dirk Lemstracf250862017-10-15 09:29:32 +02002939 for (i=(ssize_t)info->event_count-1; i >= 0; i--)
2940 {
2941 cl_int
2942 event_status;
Cristy9099bbb2018-05-26 06:19:13 -04002943
Dirk Lemstracf250862017-10-15 09:29:32 +02002944 cl_uint
2945 status;
2946
2947 status=openCL_library->clGetEventInfo(info->events[i],
Cristy9099bbb2018-05-26 06:19:13 -04002948 CL_EVENT_COMMAND_EXECUTION_STATUS,sizeof(event_status),&event_status,
Dirk Lemstracf250862017-10-15 09:29:32 +02002949 NULL);
Dirk Lemstra41a3cc92018-10-15 22:25:19 +02002950 if ((status == CL_SUCCESS) && (event_status > CL_COMPLETE))
Dirk Lemstracf250862017-10-15 09:29:32 +02002951 {
2952 openCL_library->clSetEventCallback(info->events[i],CL_COMPLETE,
2953 &DestroyMagickCLCacheInfoAndPixels,info);
2954 return;
2955 }
2956 }
Dirk Lemstra3df73bb2017-02-27 22:03:20 +01002957 pixels=info->pixels;
Dirk Lemstra45dc6cd2018-04-10 21:58:33 +02002958 RelinquishMagickResource(MemoryResource,info->length);
dirk21dc0312016-06-13 22:30:26 +02002959 DestroyMagickCLCacheInfo(info);
Dirk Lemstra3df73bb2017-02-27 22:03:20 +01002960 (void) RelinquishAlignedMemory(pixels);
dirk21dc0312016-06-13 22:30:26 +02002961}
2962
2963MagickPrivate MagickCLCacheInfo RelinquishMagickCLCacheInfo(
2964 MagickCLCacheInfo info,const MagickBooleanType relinquish_pixels)
2965{
2966 if (info == (MagickCLCacheInfo) NULL)
2967 return((MagickCLCacheInfo) NULL);
dirk5fb23952016-06-19 12:35:35 +02002968 if (relinquish_pixels != MagickFalse)
Dirk Lemstracf250862017-10-15 09:29:32 +02002969 DestroyMagickCLCacheInfoAndPixels((cl_event) NULL,0,info);
dirk21dc0312016-06-13 22:30:26 +02002970 else
2971 DestroyMagickCLCacheInfo(info);
2972 return((MagickCLCacheInfo) NULL);
dirkc5a1b3f2016-04-24 23:04:34 +02002973}
2974
2975/*
2976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2977% %
2978% %
2979% %
dirk5a2575f2016-04-15 09:24:26 +02002980% R e l i n q u i s h M a g i c k C L D e v i c e %
2981% %
2982% %
2983% %
2984%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2985%
2986% RelinquishMagickCLDevice() releases the OpenCL device
2987%
2988% The format of the RelinquishMagickCLDevice method is:
2989%
2990% MagickCLDevice RelinquishMagickCLDevice(MagickCLDevice device)
2991%
2992% A description of each parameter follows:
2993%
2994% o device: the OpenCL device to be released.
2995%
Cristy1dd96da2015-10-06 07:52:01 -04002996*/
dirk5a2575f2016-04-15 09:24:26 +02002997
2998static MagickCLDevice RelinquishMagickCLDevice(MagickCLDevice device)
cristyf034abb2013-11-24 14:16:14 +00002999{
dirk5a2575f2016-04-15 09:24:26 +02003000 if (device == (MagickCLDevice) NULL)
3001 return((MagickCLDevice) NULL);
3002
dirk8f012e52016-06-03 22:28:50 +02003003 device->platform_name=RelinquishMagickMemory(device->platform_name);
dirk9ca8c472016-09-20 21:40:52 +02003004 device->vendor_name=RelinquishMagickMemory(device->vendor_name);
dirk5a2575f2016-04-15 09:24:26 +02003005 device->name=RelinquishMagickMemory(device->name);
3006 device->version=RelinquishMagickMemory(device->version);
3007 if (device->program != (cl_program) NULL)
3008 (void) openCL_library->clReleaseProgram(device->program);
3009 while (device->command_queues_index >= 0)
3010 (void) openCL_library->clReleaseCommandQueue(
3011 device->command_queues[device->command_queues_index--]);
dirk5a2575f2016-04-15 09:24:26 +02003012 RelinquishSemaphoreInfo(&device->lock);
3013 return((MagickCLDevice) RelinquishMagickMemory(device));
cristyf034abb2013-11-24 14:16:14 +00003014}
3015
dirk5a2575f2016-04-15 09:24:26 +02003016/*
3017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3018% %
3019% %
3020% %
3021% R e l i n q u i s h M a g i c k C L E n v %
3022% %
3023% %
3024% %
3025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3026%
3027% RelinquishMagickCLEnv() releases the OpenCL environment
3028%
3029% The format of the RelinquishMagickCLEnv method is:
3030%
3031% MagickCLEnv RelinquishMagickCLEnv(MagickCLEnv device)
3032%
3033% A description of each parameter follows:
3034%
3035% o clEnv: the OpenCL environment to be released.
3036%
3037*/
cristyf034abb2013-11-24 14:16:14 +00003038
dirk5a2575f2016-04-15 09:24:26 +02003039static MagickCLEnv RelinquishMagickCLEnv(MagickCLEnv clEnv)
3040{
3041 if (clEnv == (MagickCLEnv) NULL)
3042 return((MagickCLEnv) NULL);
3043
3044 RelinquishSemaphoreInfo(&clEnv->lock);
3045 RelinquishMagickCLDevices(clEnv);
dirk8f012e52016-06-03 22:28:50 +02003046 if (clEnv->contexts != (cl_context *) NULL)
3047 {
3048 ssize_t
3049 i;
3050
3051 for (i=0; i < clEnv->number_contexts; i++)
Cristy316977d2018-05-26 06:21:22 -04003052 if (clEnv->contexts[i] != (cl_context) NULL)
Cristy9099bbb2018-05-26 06:19:13 -04003053 (void) openCL_library->clReleaseContext(clEnv->contexts[i]);
dirk8f012e52016-06-03 22:28:50 +02003054 clEnv->contexts=(cl_context *) RelinquishMagickMemory(clEnv->contexts);
3055 }
dirk5a2575f2016-04-15 09:24:26 +02003056 return((MagickCLEnv) RelinquishMagickMemory(clEnv));
Cristy1dd96da2015-10-06 07:52:01 -04003057}
cristyf034abb2013-11-24 14:16:14 +00003058
dirk5a2575f2016-04-15 09:24:26 +02003059/*
3060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3061% %
3062% %
3063% %
dirk21dc0312016-06-13 22:30:26 +02003064+ R e q u e s t O p e n C L D e v i c e %
dirkc5a1b3f2016-04-24 23:04:34 +02003065% %
3066% %
3067% %
3068%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3069%
3070% RequestOpenCLDevice() returns one of the enabled OpenCL devices.
3071%
3072% The format of the RequestOpenCLDevice method is:
3073%
3074% MagickCLDevice RequestOpenCLDevice(MagickCLEnv clEnv)
3075%
3076% A description of each parameter follows:
3077%
3078% o clEnv: the OpenCL environment.
3079*/
3080
3081MagickPrivate MagickCLDevice RequestOpenCLDevice(MagickCLEnv clEnv)
3082{
3083 MagickCLDevice
3084 device;
3085
3086 double
3087 score,
3088 best_score;
3089
3090 size_t
3091 i;
3092
3093 if (clEnv == (MagickCLEnv) NULL)
3094 return((MagickCLDevice) NULL);
3095
3096 if (clEnv->number_devices == 1)
3097 {
3098 if (clEnv->devices[0]->enabled)
3099 return(clEnv->devices[0]);
3100 else
3101 return((MagickCLDevice) NULL);
3102 }
3103
3104 device=(MagickCLDevice) NULL;
3105 best_score=0.0;
dirk21dc0312016-06-13 22:30:26 +02003106 LockSemaphoreInfo(openCL_lock);
dirkc5a1b3f2016-04-24 23:04:34 +02003107 for (i = 0; i < clEnv->number_devices; i++)
3108 {
3109 if (clEnv->devices[i]->enabled == MagickFalse)
3110 continue;
3111
3112 score=clEnv->devices[i]->score+(clEnv->devices[i]->score*
3113 clEnv->devices[i]->requested);
3114 if ((device == (MagickCLDevice) NULL) || (score < best_score))
3115 {
3116 device=clEnv->devices[i];
3117 best_score=score;
3118 }
3119 }
3120 if (device != (MagickCLDevice)NULL)
3121 device->requested++;
dirk21dc0312016-06-13 22:30:26 +02003122 UnlockSemaphoreInfo(openCL_lock);
dirkc5a1b3f2016-04-24 23:04:34 +02003123
3124 return(device);
3125}
3126
3127/*
3128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3129% %
3130% %
3131% %
dirk5a2575f2016-04-15 09:24:26 +02003132% S e t O p e n C L D e v i c e E n a b l e d %
3133% %
3134% %
3135% %
3136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3137%
3138% SetOpenCLDeviceEnabled() can be used to enable or disabled the device.
3139%
3140% The format of the SetOpenCLDeviceEnabled method is:
3141%
dirk7d42b3c2016-04-18 23:12:54 +02003142% void SetOpenCLDeviceEnabled(MagickCLDevice device,
dirk5a2575f2016-04-15 09:24:26 +02003143% MagickBooleanType value)
3144%
3145% A description of each parameter follows:
3146%
3147% o device: the OpenCL device.
3148%
3149% o value: determines if the device should be enabled or disabled.
3150*/
cristyf034abb2013-11-24 14:16:14 +00003151
dirk7d42b3c2016-04-18 23:12:54 +02003152MagickExport void SetOpenCLDeviceEnabled(MagickCLDevice device,
dirk5a2575f2016-04-15 09:24:26 +02003153 const MagickBooleanType value)
3154{
3155 if (device == (MagickCLDevice) NULL)
3156 return;
3157 device->enabled=value;
cristyf034abb2013-11-24 14:16:14 +00003158}
3159
dirk5a2575f2016-04-15 09:24:26 +02003160/*
3161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3162% %
3163% %
3164% %
dirk7d42b3c2016-04-18 23:12:54 +02003165% S e t O p e n C L K e r n e l P r o f i l e E n a b l e d %
3166% %
3167% %
3168% %
3169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3170%
3171% SetOpenCLKernelProfileEnabled() can be used to enable or disabled the
3172% kernel profiling of a device.
3173%
3174% The format of the SetOpenCLKernelProfileEnabled method is:
3175%
3176% void SetOpenCLKernelProfileEnabled(MagickCLDevice device,
3177% MagickBooleanType value)
3178%
3179% A description of each parameter follows:
3180%
3181% o device: the OpenCL device.
3182%
3183% o value: determines if kernel profiling for the device should be enabled
3184% or disabled.
3185*/
3186
3187MagickExport void SetOpenCLKernelProfileEnabled(MagickCLDevice device,
3188 const MagickBooleanType value)
3189{
3190 if (device == (MagickCLDevice) NULL)
3191 return;
3192 device->profile_kernels=value;
3193}
3194
3195/*
3196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3197% %
3198% %
3199% %
dirk5a2575f2016-04-15 09:24:26 +02003200% S e t O p e n C L E n a b l e d %
3201% %
3202% %
3203% %
3204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3205%
3206% SetOpenCLEnabled() can be used to enable or disable OpenCL acceleration.
3207%
3208% The format of the SetOpenCLEnabled method is:
3209%
3210% void SetOpenCLEnabled(MagickBooleanType)
3211%
3212% A description of each parameter follows:
3213%
3214% o value: specify true to enable OpenCL acceleration
3215*/
cristyf034abb2013-11-24 14:16:14 +00003216
dirk5a2575f2016-04-15 09:24:26 +02003217MagickExport MagickBooleanType SetOpenCLEnabled(const MagickBooleanType value)
3218{
3219 MagickCLEnv
3220 clEnv;
3221
3222 clEnv=GetCurrentOpenCLEnv();
3223 if (clEnv == (MagickCLEnv) NULL)
3224 return(MagickFalse);
3225 clEnv->enabled=value;
3226 return(clEnv->enabled);
cristyf034abb2013-11-24 14:16:14 +00003227}
3228
dirk5a2575f2016-04-15 09:24:26 +02003229#else
cristyf034abb2013-11-24 14:16:14 +00003230
dirk5a2575f2016-04-15 09:24:26 +02003231MagickExport double GetOpenCLDeviceBenchmarkScore(
3232 const MagickCLDevice magick_unused(device))
3233{
3234 magick_unreferenced(device);
3235 return(0.0);
cristyf034abb2013-11-24 14:16:14 +00003236}
3237
dirk5a2575f2016-04-15 09:24:26 +02003238MagickExport MagickBooleanType GetOpenCLDeviceEnabled(
3239 const MagickCLDevice magick_unused(device))
cristyf034abb2013-11-24 14:16:14 +00003240{
dirk5a2575f2016-04-15 09:24:26 +02003241 magick_unreferenced(device);
cristya22457d2013-12-07 14:03:06 +00003242 return(MagickFalse);
3243}
cristy0c832c62014-03-07 22:21:04 +00003244
dirk5a2575f2016-04-15 09:24:26 +02003245MagickExport const char *GetOpenCLDeviceName(
3246 const MagickCLDevice magick_unused(device))
cristy0c832c62014-03-07 22:21:04 +00003247{
dirk5a2575f2016-04-15 09:24:26 +02003248 magick_unreferenced(device);
3249 return((const char *) NULL);
cristy0c832c62014-03-07 22:21:04 +00003250}
3251
dirk7d42b3c2016-04-18 23:12:54 +02003252MagickExport MagickCLDevice *GetOpenCLDevices(size_t *length,
3253 ExceptionInfo *magick_unused(exception))
cristy0c832c62014-03-07 22:21:04 +00003254{
dirk7d42b3c2016-04-18 23:12:54 +02003255 magick_unreferenced(exception);
dirk5a2575f2016-04-15 09:24:26 +02003256 if (length != (size_t *) NULL)
3257 *length=0;
3258 return((MagickCLDevice *) NULL);
cristy0c832c62014-03-07 22:21:04 +00003259}
3260
dirk5a2575f2016-04-15 09:24:26 +02003261MagickExport MagickCLDeviceType GetOpenCLDeviceType(
3262 const MagickCLDevice magick_unused(device))
cristy0c832c62014-03-07 22:21:04 +00003263{
dirk5a2575f2016-04-15 09:24:26 +02003264 magick_unreferenced(device);
3265 return(UndefinedCLDeviceType);
cristy0c832c62014-03-07 22:21:04 +00003266}
3267
dirk7d42b3c2016-04-18 23:12:54 +02003268MagickExport const KernelProfileRecord *GetOpenCLKernelProfileRecords(
dirk669ff272016-04-21 22:16:57 +02003269 const MagickCLDevice magick_unused(device),size_t *length)
dirk7d42b3c2016-04-18 23:12:54 +02003270{
dirk669ff272016-04-21 22:16:57 +02003271 magick_unreferenced(device);
dirk7d42b3c2016-04-18 23:12:54 +02003272 if (length != (size_t *) NULL)
3273 *length=0;
dirk3c60ad62016-04-18 23:35:20 +02003274 return((const KernelProfileRecord *) NULL);
dirk7d42b3c2016-04-18 23:12:54 +02003275}
3276
dirk5a2575f2016-04-15 09:24:26 +02003277MagickExport const char *GetOpenCLDeviceVersion(
3278 const MagickCLDevice magick_unused(device))
cristy0c832c62014-03-07 22:21:04 +00003279{
dirk5a2575f2016-04-15 09:24:26 +02003280 magick_unreferenced(device);
3281 return((const char *) NULL);
cristy0c832c62014-03-07 22:21:04 +00003282}
3283
dirk5a2575f2016-04-15 09:24:26 +02003284MagickExport MagickBooleanType GetOpenCLEnabled(void)
dirk99731742015-11-14 22:54:38 +01003285{
dirk5a2575f2016-04-15 09:24:26 +02003286 return(MagickFalse);
3287}
3288
3289MagickExport void SetOpenCLDeviceEnabled(
dirk7d42b3c2016-04-18 23:12:54 +02003290 MagickCLDevice magick_unused(device),
dirk5a2575f2016-04-15 09:24:26 +02003291 const MagickBooleanType magick_unused(value))
3292{
3293 magick_unreferenced(device);
3294 magick_unreferenced(value);
3295}
3296
3297MagickExport MagickBooleanType SetOpenCLEnabled(
3298 const MagickBooleanType magick_unused(value))
3299{
3300 magick_unreferenced(value);
3301 return(MagickFalse);
3302}
3303
dirk3c60ad62016-04-18 23:35:20 +02003304MagickExport void SetOpenCLKernelProfileEnabled(
dirk7d42b3c2016-04-18 23:12:54 +02003305 MagickCLDevice magick_unused(device),
3306 const MagickBooleanType magick_unused(value))
3307{
3308 magick_unreferenced(device);
3309 magick_unreferenced(value);
3310}
Cristycca91aa2017-09-30 09:34:33 -04003311#endif