2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % OOO PPPP EEEEE N N CCCC L %
8 % O O PPPP EEE N N N C L %
10 % OOO P EEEEE N N CCCC LLLLL %
13 % MagickCore OpenCL Methods %
20 % Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/color.h"
47 #include "MagickCore/compare.h"
48 #include "MagickCore/constitute.h"
49 #include "MagickCore/distort.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/effect.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/fx.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/geometry.h"
57 #include "MagickCore/image.h"
58 #include "MagickCore/image-private.h"
59 #include "MagickCore/layer.h"
60 #include "MagickCore/mime-private.h"
61 #include "MagickCore/memory_.h"
62 #include "MagickCore/monitor.h"
63 #include "MagickCore/montage.h"
64 #include "MagickCore/morphology.h"
65 #include "MagickCore/nt-base.h"
66 #include "MagickCore/nt-base-private.h"
67 #include "MagickCore/opencl.h"
68 #include "MagickCore/opencl-private.h"
69 #include "MagickCore/option.h"
70 #include "MagickCore/policy.h"
71 #include "MagickCore/property.h"
72 #include "MagickCore/quantize.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/random_.h"
75 #include "MagickCore/random-private.h"
76 #include "MagickCore/resample.h"
77 #include "MagickCore/resource_.h"
78 #include "MagickCore/splay-tree.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/statistic.h"
81 #include "MagickCore/string_.h"
82 #include "MagickCore/token.h"
83 #include "MagickCore/utility.h"
85 #ifdef MAGICKCORE_CLPERFMARKER
86 #include "CLPerfMarker.h"
90 #if defined(MAGICKCORE_OPENCL_SUPPORT)
92 #ifdef MAGICKCORE_HAVE_OPENCL_CL_H
93 #define MAGICKCORE_OPENCL_MACOSX 1
97 #define NUM_CL_RAND_GENERATORS 1024 /* number of random number generators running in parallel */
101 * Dynamic library loading functions
104 #ifdef MAGICKCORE_WINDOWS_SUPPORT
109 // dynamically load a library. returns NULL on failure
110 void *OsLibraryLoad(const char *libraryName)
112 #ifdef MAGICKCORE_WINDOWS_SUPPORT
113 return (void *)LoadLibraryA(libraryName);
115 return (void *)dlopen(libraryName, RTLD_NOW);
119 // get a function pointer from a loaded library. returns NULL on failure.
120 void *OsLibraryGetFunctionAddress(void *library, const char *functionName)
122 #ifdef MAGICKCORE_WINDOWS_SUPPORT
123 if (!library || !functionName)
127 return (void *) GetProcAddress( (HMODULE)library, functionName);
129 if (!library || !functionName)
133 return (void *)dlsym(library, functionName);
138 void OsLibraryUnload(void *library)
140 #ifdef MAGICKCORE_WINDOWS_SUPPORT
141 FreeLibrary( (HMODULE)library);
149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 + A c q u i r e M a g i c k O p e n C L E n v %
157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159 % AcquireMagickOpenCLEnv() allocates the MagickCLEnv structure
163 MagickExport MagickCLEnv AcquireMagickOpenCLEnv()
166 clEnv = (MagickCLEnv) AcquireMagickMemory(sizeof(struct _MagickCLEnv));
169 memset(clEnv, 0, sizeof(struct _MagickCLEnv));
170 ActivateSemaphoreInfo(&clEnv->lock);
177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181 + R e l i n q u i s h M a g i c k O p e n C L E n v %
185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 % RelinquishMagickOpenCLEnv() destroy the MagickCLEnv structure
189 % The format of the RelinquishMagickOpenCLEnv method is:
191 % MagickBooleanType RelinquishMagickOpenCLEnv(MagickCLEnv clEnv)
193 % A description of each parameter follows:
195 % o clEnv: MagickCLEnv structure to destroy
199 MagickExport MagickBooleanType RelinquishMagickOpenCLEnv(MagickCLEnv clEnv)
201 if (clEnv != (MagickCLEnv)NULL)
203 DestroySemaphoreInfo(&clEnv->lock);
204 RelinquishMagickMemory(clEnv);
212 * Default OpenCL environment
214 MagickCLEnv defaultCLEnv;
215 SemaphoreInfo* defaultCLEnvLock;
220 MagickLibrary * OpenCLLib;
221 SemaphoreInfo* OpenCLLibLock;
224 static MagickBooleanType bindOpenCLFunctions(void* library)
226 #ifdef MAGICKCORE_OPENCL_MACOSX
227 #define BIND(X) OpenCLLib->X= &X;
230 if ((OpenCLLib->X=(MAGICKpfn_##X)OsLibraryGetFunctionAddress(library,#X)) == NULL)\
234 BIND(clGetPlatformIDs);
235 BIND(clGetPlatformInfo);
237 BIND(clGetDeviceIDs);
238 BIND(clGetDeviceInfo);
240 BIND(clCreateContext);
242 BIND(clCreateBuffer);
243 BIND(clReleaseMemObject);
245 BIND(clCreateProgramWithSource);
246 BIND(clCreateProgramWithBinary);
247 BIND(clBuildProgram);
248 BIND(clGetProgramInfo);
249 BIND(clGetProgramBuildInfo);
251 BIND(clCreateKernel);
252 BIND(clReleaseKernel);
253 BIND(clSetKernelArg);
258 BIND(clEnqueueNDRangeKernel);
259 BIND(clEnqueueReadBuffer);
260 BIND(clEnqueueMapBuffer);
261 BIND(clEnqueueUnmapMemObject);
263 BIND(clCreateCommandQueue);
264 BIND(clReleaseCommandQueue);
269 MagickLibrary * GetOpenCLLib()
271 if (OpenCLLib == NULL)
273 if (OpenCLLibLock == NULL)
275 ActivateSemaphoreInfo(&OpenCLLibLock);
278 LockSemaphoreInfo(OpenCLLibLock);
280 OpenCLLib = (MagickLibrary *) AcquireMagickMemory (sizeof (MagickLibrary));
282 if (OpenCLLib != NULL)
284 MagickBooleanType status = MagickFalse;
285 void * library = NULL;
287 #ifdef MAGICKCORE_OPENCL_MACOSX
288 status = bindOpenCLFunctions(library);
291 memset(OpenCLLib, 0, sizeof(MagickLibrary));
292 #ifdef MAGICKCORE_WINDOWS_SUPPORT
293 library = OsLibraryLoad("OpenCL.dll");
295 library = OsLibraryLoad("libOpenCL.so");
298 status = bindOpenCLFunctions(library);
300 if (status==MagickTrue)
301 OpenCLLib->base=library;
303 OpenCLLib=(MagickLibrary *)RelinquishMagickMemory(OpenCLLib);
307 UnlockSemaphoreInfo(OpenCLLibLock);
316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
320 + G e t D e f a u l t O p e n C L E n v %
324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326 % GetDefaultOpenCLEnv() returns the default OpenCL env
328 % The format of the GetDefaultOpenCLEnv method is:
330 % MagickCLEnv GetDefaultOpenCLEnv()
332 % A description of each parameter follows:
334 % o exception: return any errors or warnings.
338 MagickExport MagickCLEnv GetDefaultOpenCLEnv()
340 if (defaultCLEnv == NULL)
342 if (defaultCLEnvLock == NULL)
344 ActivateSemaphoreInfo(&defaultCLEnvLock);
346 LockSemaphoreInfo(defaultCLEnvLock);
347 defaultCLEnv = AcquireMagickOpenCLEnv();
348 UnlockSemaphoreInfo(defaultCLEnvLock);
353 static void LockDefaultOpenCLEnv() {
354 if (defaultCLEnvLock == NULL)
356 ActivateSemaphoreInfo(&defaultCLEnvLock);
358 LockSemaphoreInfo(defaultCLEnvLock);
361 static void UnlockDefaultOpenCLEnv() {
362 if (defaultCLEnvLock == NULL)
364 ActivateSemaphoreInfo(&defaultCLEnvLock);
367 UnlockSemaphoreInfo(defaultCLEnvLock);
372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
376 + S e t D e f a u l t O p e n C L E n v %
380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
382 % SetDefaultOpenCLEnv() sets the new OpenCL environment as default
383 % and returns the old OpenCL environment
385 % The format of the SetDefaultOpenCLEnv() method is:
387 % MagickCLEnv SetDefaultOpenCLEnv(MagickCLEnv clEnv)
389 % A description of each parameter follows:
391 % o clEnv: the new default OpenCL environment.
394 MagickExport MagickCLEnv SetDefaultOpenCLEnv(MagickCLEnv clEnv)
397 LockDefaultOpenCLEnv();
398 oldEnv = defaultCLEnv;
399 defaultCLEnv = clEnv;
400 UnlockDefaultOpenCLEnv();
407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411 + S e t M a g i c k O p e n C L E n v P a r a m %
415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
417 % SetMagickOpenCLEnvParam() sets the parameters in the OpenCL environment
419 % The format of the SetMagickOpenCLEnvParam() method is:
421 % MagickBooleanType SetMagickOpenCLEnvParam(MagickCLEnv clEnv,
422 % MagickOpenCLEnvParam param, size_t dataSize, void* data,
423 % ExceptionInfo* exception)
425 % A description of each parameter follows:
427 % o clEnv: the OpenCL environment.
429 % o param: the parameter to be set.
431 % o dataSize: the data size of the parameter value.
433 % o data: the pointer to the new parameter value
435 % o exception: return any errors or warnings
439 static MagickBooleanType SetMagickOpenCLEnvParamInternal(MagickCLEnv clEnv, MagickOpenCLEnvParam param
440 , size_t dataSize, void* data, ExceptionInfo* exception)
442 MagickBooleanType status = MagickFalse;
450 case MAGICK_OPENCL_ENV_PARAM_DEVICE:
451 if (dataSize != sizeof(clEnv->device))
453 clEnv->device = *((cl_device_id*)data);
454 clEnv->OpenCLInitialized = MagickFalse;
458 case MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED:
459 if (dataSize != sizeof(clEnv->OpenCLDisabled))
461 clEnv->OpenCLDisabled = *((MagickBooleanType*)data);
462 clEnv->OpenCLInitialized = MagickFalse;
466 case MAGICK_OPENCL_ENV_PARAM_OPENCL_INITIALIZED:
467 (void) ThrowMagickException(exception, GetMagickModule(), ModuleWarning, "SetMagickOpenCLEnvParm cannot modify the OpenCL initialization state.", "'%s'", ".");
470 case MAGICK_OPENCL_ENV_PARAM_PROGRAM_CACHE_DISABLED:
471 if (dataSize != sizeof(clEnv->disableProgramCache))
473 clEnv->disableProgramCache = *((MagickBooleanType*)data);
474 clEnv->OpenCLInitialized = MagickFalse;
478 case MAGICK_OPENCL_ENV_PARAM_REGENERATE_PROFILE:
479 if (dataSize != sizeof(clEnv->regenerateProfile))
481 clEnv->regenerateProfile = *((MagickBooleanType*)data);
482 clEnv->OpenCLInitialized = MagickFalse;
495 MagickBooleanType SetMagickOpenCLEnvParam(MagickCLEnv clEnv, MagickOpenCLEnvParam param
496 , size_t dataSize, void* data, ExceptionInfo* exception) {
497 MagickBooleanType status = MagickFalse;
499 LockSemaphoreInfo(clEnv->lock);
500 status = SetMagickOpenCLEnvParamInternal(clEnv,param,dataSize,data,exception);
501 UnlockSemaphoreInfo(clEnv->lock);
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 + G e t M a g i c k O p e n C L E n v P a r a m %
515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
517 % GetMagickOpenCLEnvParam() gets the parameters in the OpenCL environment
519 % The format of the GetMagickOpenCLEnvParam() method is:
521 % MagickBooleanType GetMagickOpenCLEnvParam(MagickCLEnv clEnv,
522 % MagickOpenCLEnvParam param, size_t dataSize, void* data,
523 % ExceptionInfo* exception)
525 % A description of each parameter follows:
527 % o clEnv: the OpenCL environment.
529 % o param: the parameter to be returned.
531 % o dataSize: the data size of the parameter value.
533 % o data: the location where the returned parameter value will be stored
535 % o exception: return any errors or warnings
540 MagickBooleanType GetMagickOpenCLEnvParam(MagickCLEnv clEnv, MagickOpenCLEnvParam param
541 , size_t dataSize, void* data, ExceptionInfo* exception)
546 magick_unreferenced(exception);
548 status = MagickFalse;
556 case MAGICK_OPENCL_ENV_PARAM_DEVICE:
557 if (dataSize != sizeof(cl_device_id))
559 *((cl_device_id*)data) = clEnv->device;
563 case MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED:
564 if (dataSize != sizeof(clEnv->OpenCLDisabled))
566 *((MagickBooleanType*)data) = clEnv->OpenCLDisabled;
570 case MAGICK_OPENCL_ENV_PARAM_OPENCL_INITIALIZED:
571 if (dataSize != sizeof(clEnv->OpenCLDisabled))
573 *((MagickBooleanType*)data) = clEnv->OpenCLInitialized;
577 case MAGICK_OPENCL_ENV_PARAM_PROGRAM_CACHE_DISABLED:
578 if (dataSize != sizeof(clEnv->disableProgramCache))
580 *((MagickBooleanType*)data) = clEnv->disableProgramCache;
584 case MAGICK_OPENCL_ENV_PARAM_REGENERATE_PROFILE:
585 if (dataSize != sizeof(clEnv->regenerateProfile))
587 *((MagickBooleanType*)data) = clEnv->regenerateProfile;
601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
605 + G e t O p e n C L C o n t e x t %
609 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611 % GetOpenCLContext() returns the OpenCL context
613 % The format of the GetOpenCLContext() method is:
615 % cl_context GetOpenCLContext(MagickCLEnv clEnv)
617 % A description of each parameter follows:
619 % o clEnv: OpenCL environment
624 cl_context GetOpenCLContext(MagickCLEnv clEnv) {
628 return clEnv->context;
631 static char* getBinaryCLProgramName(MagickCLEnv clEnv, MagickOpenCLProgram prog, unsigned int signature)
635 char path[MaxTextExtent];
636 char deviceName[MaxTextExtent];
637 const char* prefix = "magick_opencl";
638 clEnv->library->clGetDeviceInfo(clEnv->device, CL_DEVICE_NAME, MaxTextExtent, deviceName, NULL);
640 /* strip out illegal characters for file names */
643 if ( *ptr == ' ' || *ptr == '\\' || *ptr == '/' || *ptr == ':' || *ptr == '*'
644 || *ptr == '?' || *ptr == '"' || *ptr == '<' || *ptr == '>' || *ptr == '|')
650 (void) FormatLocaleString(path,MaxTextExtent,"%s%s%s_%s_%02d_%08x_%.20g.bin",
651 GetOpenCLCachedFilesDirectory(),DirectorySeparator,prefix,deviceName,
652 (unsigned int) prog,signature,(double) sizeof(char*)*8);
653 name = (char*)AcquireMagickMemory(strlen(path)+1);
654 CopyMagickString(name,path,strlen(path)+1);
658 static MagickBooleanType saveBinaryCLProgram(MagickCLEnv clEnv, MagickOpenCLProgram prog, unsigned int signature, ExceptionInfo* exception)
660 MagickBooleanType saveSuccessful;
662 size_t binaryProgramSize;
663 unsigned char* binaryProgram;
664 char* binaryFileName;
667 #ifdef MAGICKCORE_CLPERFMARKER
668 clBeginPerfMarkerAMD(__FUNCTION__,"");
671 binaryProgram = NULL;
672 binaryFileName = NULL;
674 saveSuccessful = MagickFalse;
676 clStatus = clEnv->library->clGetProgramInfo(clEnv->programs[prog], CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &binaryProgramSize, NULL);
677 if (clStatus != CL_SUCCESS)
679 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError, "clGetProgramInfo failed.", "'%s'", ".");
683 binaryProgram = (unsigned char*) AcquireMagickMemory(binaryProgramSize);
684 clStatus = clEnv->library->clGetProgramInfo(clEnv->programs[prog], CL_PROGRAM_BINARIES, sizeof(char*), &binaryProgram, NULL);
685 if (clStatus != CL_SUCCESS)
687 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError, "clGetProgramInfo failed.", "'%s'", ".");
691 binaryFileName = getBinaryCLProgramName(clEnv, prog, signature);
692 fileHandle = fopen(binaryFileName, "wb");
693 if (fileHandle != NULL)
695 fwrite(binaryProgram, sizeof(char), binaryProgramSize, fileHandle);
696 saveSuccessful = MagickTrue;
700 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
701 "Saving binary kernel failed.", "'%s'", ".");
705 if (fileHandle != NULL)
707 if (binaryProgram != NULL)
708 RelinquishMagickMemory(binaryProgram);
709 if (binaryFileName != NULL)
710 free(binaryFileName);
712 #ifdef MAGICKCORE_CLPERFMARKER
713 clEndPerfMarkerAMD();
716 return saveSuccessful;
719 static MagickBooleanType loadBinaryCLProgram(MagickCLEnv clEnv, MagickOpenCLProgram prog, unsigned int signature)
721 MagickBooleanType loadSuccessful;
722 unsigned char* binaryProgram;
723 char* binaryFileName;
726 #ifdef MAGICKCORE_CLPERFMARKER
727 clBeginPerfMarkerAMD(__FUNCTION__,"");
730 binaryProgram = NULL;
731 binaryFileName = NULL;
733 loadSuccessful = MagickFalse;
735 binaryFileName = getBinaryCLProgramName(clEnv, prog, signature);
736 fileHandle = fopen(binaryFileName, "rb");
737 if (fileHandle != NULL)
742 cl_int clBinaryStatus;
746 b_error |= fseek( fileHandle, 0, SEEK_END ) < 0;
747 b_error |= ( length = ftell( fileHandle ) ) <= 0;
748 b_error |= fseek( fileHandle, 0, SEEK_SET ) < 0;
752 binaryProgram = (unsigned char*)AcquireMagickMemory(length);
753 if (binaryProgram == NULL)
756 memset(binaryProgram, 0, length);
757 b_error |= fread(binaryProgram, 1, length, fileHandle) != length;
759 clEnv->programs[prog] = clEnv->library->clCreateProgramWithBinary(clEnv->context, 1, &clEnv->device, &length, (const unsigned char**)&binaryProgram, &clBinaryStatus, &clStatus);
760 if (clStatus != CL_SUCCESS
761 || clBinaryStatus != CL_SUCCESS)
764 loadSuccessful = MagickTrue;
768 if (fileHandle != NULL)
770 if (binaryFileName != NULL)
771 free(binaryFileName);
772 if (binaryProgram != NULL)
773 RelinquishMagickMemory(binaryProgram);
775 #ifdef MAGICKCORE_CLPERFMARKER
776 clEndPerfMarkerAMD();
779 return loadSuccessful;
782 static unsigned int stringSignature(const char* string)
784 unsigned int stringLength;
786 unsigned int signature;
790 const unsigned int* u;
793 #ifdef MAGICKCORE_CLPERFMARKER
794 clBeginPerfMarkerAMD(__FUNCTION__,"");
797 stringLength = strlen(string);
798 signature = stringLength;
799 n = stringLength/sizeof(unsigned int);
801 for (i = 0; i < n; i++)
805 if (n * sizeof(unsigned int) != stringLength)
808 j = n * sizeof(unsigned int);
809 for (i = 0; i < 4; i++,j++)
811 if (j < stringLength)
820 #ifdef MAGICKCORE_CLPERFMARKER
821 clEndPerfMarkerAMD();
827 /* OpenCL kernels for accelerate.c */
828 extern const char *accelerateKernels, *accelerateKernels2;
830 static MagickBooleanType CompileOpenCLKernels(MagickCLEnv clEnv, ExceptionInfo* exception)
832 MagickBooleanType status = MagickFalse;
835 char* accelerateKernelsBuffer = NULL;
837 /* The index of the program strings in this array has to match the value of the enum MagickOpenCLProgram */
838 const char* MagickOpenCLProgramStrings[MAGICK_OPENCL_NUM_PROGRAMS];
840 char options[MaxTextExtent];
841 unsigned int optionsSignature;
843 #ifdef MAGICKCORE_CLPERFMARKER
844 clBeginPerfMarkerAMD(__FUNCTION__,"");
847 /* Get additional options */
848 (void) FormatLocaleString(options, MaxTextExtent, CLOptions, (float)QuantumRange,
849 (float)QuantumScale, (float)CLCharQuantumScale, (float)MagickEpsilon, (float)MagickPI, (unsigned int)MaxMap, (unsigned int)MAGICKCORE_QUANTUM_DEPTH);
852 if (getenv("MAGICK_OCL_DEF"))
855 strcat(options,getenv("MAGICK_OCL_DEF"));
860 if (getenv("MAGICK_OCL_BUILD"))
861 printf("options: %s\n", options);
864 optionsSignature = stringSignature(options);
866 /* get all the OpenCL program strings here */
867 accelerateKernelsBuffer = (char*) AcquireMagickMemory(strlen(accelerateKernels)+strlen(accelerateKernels2)+1);
868 sprintf(accelerateKernelsBuffer,"%s%s",accelerateKernels,accelerateKernels2);
869 MagickOpenCLProgramStrings[MAGICK_OPENCL_ACCELERATE] = accelerateKernelsBuffer;
871 for (i = 0; i < MAGICK_OPENCL_NUM_PROGRAMS; i++)
873 MagickBooleanType loadSuccessful = MagickFalse;
874 unsigned int programSignature = stringSignature(MagickOpenCLProgramStrings[i]) ^ optionsSignature;
876 /* try to load the binary first */
877 if (clEnv->disableProgramCache != MagickTrue
878 && !getenv("MAGICK_OCL_REC"))
879 loadSuccessful = loadBinaryCLProgram(clEnv, (MagickOpenCLProgram)i, programSignature);
881 if (loadSuccessful == MagickFalse)
883 /* Binary CL program unavailable, compile the program from source */
884 size_t programLength = strlen(MagickOpenCLProgramStrings[i]);
885 clEnv->programs[i] = clEnv->library->clCreateProgramWithSource(clEnv->context, 1, &(MagickOpenCLProgramStrings[i]), &programLength, &clStatus);
886 if (clStatus!=CL_SUCCESS)
888 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
889 "clCreateProgramWithSource failed.", "(%d)", (int)clStatus);
895 clStatus = clEnv->library->clBuildProgram(clEnv->programs[i], 1, &clEnv->device, options, NULL, NULL);
896 if (clStatus!=CL_SUCCESS)
898 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
899 "clBuildProgram failed.", "(%d)", (int)clStatus);
901 if (loadSuccessful == MagickFalse)
903 char path[MaxTextExtent];
906 /* dump the source into a file */
907 (void) FormatLocaleString(path,MaxTextExtent,"%s%s%s"
908 ,GetOpenCLCachedFilesDirectory()
909 ,DirectorySeparator,"magick_badcl.cl");
910 fileHandle = fopen(path, "wb");
911 if (fileHandle != NULL)
913 fwrite(MagickOpenCLProgramStrings[i], sizeof(char), strlen(MagickOpenCLProgramStrings[i]), fileHandle);
917 /* dump the build log */
921 clEnv->library->clGetProgramBuildInfo(clEnv->programs[i], clEnv->device, CL_PROGRAM_BUILD_LOG, 0, NULL, &logSize);
922 log = (char*)AcquireMagickMemory(logSize);
923 clEnv->library->clGetProgramBuildInfo(clEnv->programs[i], clEnv->device, CL_PROGRAM_BUILD_LOG, logSize, log, &logSize);
925 (void) FormatLocaleString(path,MaxTextExtent,"%s%s%s"
926 ,GetOpenCLCachedFilesDirectory()
927 ,DirectorySeparator,"magick_badcl_build.log");
928 fileHandle = fopen(path, "wb");
929 if (fileHandle != NULL)
931 const char* buildOptionsTitle = "build options: ";
932 fwrite(buildOptionsTitle, sizeof(char), strlen(buildOptionsTitle), fileHandle);
933 fwrite(options, sizeof(char), strlen(options), fileHandle);
934 fwrite("\n",sizeof(char), 1, fileHandle);
935 fwrite(log, sizeof(char), logSize, fileHandle);
938 RelinquishMagickMemory(log);
944 if (loadSuccessful == MagickFalse)
946 /* Save the binary to a file to avoid re-compilation of the kernels in the future */
947 saveBinaryCLProgram(clEnv, (MagickOpenCLProgram)i, programSignature, exception);
955 if (accelerateKernelsBuffer!=NULL) RelinquishMagickMemory(accelerateKernelsBuffer);
957 #ifdef MAGICKCORE_CLPERFMARKER
958 clEndPerfMarkerAMD();
964 static MagickBooleanType InitOpenCLPlatformDevice(MagickCLEnv clEnv, ExceptionInfo* exception) {
967 cl_uint numPlatforms = 0;
968 cl_platform_id *platforms = NULL;
969 char* MAGICK_OCL_DEVICE = NULL;
970 MagickBooleanType OpenCLAvailable = MagickFalse;
972 #ifdef MAGICKCORE_CLPERFMARKER
973 clBeginPerfMarkerAMD(__FUNCTION__,"");
976 /* check if there's an environment variable overriding the device selection */
977 MAGICK_OCL_DEVICE = getenv("MAGICK_OCL_DEVICE");
978 if (MAGICK_OCL_DEVICE != NULL)
980 if (strcmp(MAGICK_OCL_DEVICE, "CPU") == 0)
982 clEnv->deviceType = CL_DEVICE_TYPE_CPU;
984 else if (strcmp(MAGICK_OCL_DEVICE, "GPU") == 0)
986 clEnv->deviceType = CL_DEVICE_TYPE_GPU;
988 else if (strcmp(MAGICK_OCL_DEVICE, "OFF") == 0)
990 /* OpenCL disabled */
994 else if (clEnv->deviceType == 0) {
995 clEnv->deviceType = CL_DEVICE_TYPE_ALL;
998 if (clEnv->device != NULL)
1000 status = clEnv->library->clGetDeviceInfo(clEnv->device, CL_DEVICE_PLATFORM, sizeof(cl_platform_id), &clEnv->platform, NULL);
1001 if (status != CL_SUCCESS) {
1002 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
1003 "Failed to get OpenCL platform from the selected device.", "(%d)", status);
1007 else if (clEnv->platform != NULL)
1010 platforms = (cl_platform_id *) AcquireMagickMemory(numPlatforms * sizeof(cl_platform_id));
1011 if (platforms == (cl_platform_id *) NULL)
1013 (void) ThrowMagickException(exception, GetMagickModule(), ResourceLimitError,
1014 "AcquireMagickMemory failed.",".");
1017 platforms[0] = clEnv->platform;
1021 clEnv->device = NULL;
1023 /* Get the number of OpenCL platforms available */
1024 status = clEnv->library->clGetPlatformIDs(0, NULL, &numPlatforms);
1025 if (status != CL_SUCCESS)
1027 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
1028 "clGetplatformIDs failed.", "(%d)", status);
1032 /* No OpenCL available, just leave */
1033 if (numPlatforms == 0) {
1037 platforms = (cl_platform_id *) AcquireMagickMemory(numPlatforms * sizeof(cl_platform_id));
1038 if (platforms == (cl_platform_id *) NULL)
1040 (void) ThrowMagickException(exception, GetMagickModule(), ResourceLimitError,
1041 "AcquireMagickMemory failed.",".");
1045 status = clEnv->library->clGetPlatformIDs(numPlatforms, platforms, NULL);
1046 if (status != CL_SUCCESS)
1048 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
1049 "clGetPlatformIDs failed.", "(%d)", status);
1054 /* Device selection */
1055 clEnv->device = NULL;
1056 for (j = 0; j < 2; j++)
1059 cl_device_type deviceType;
1060 if (clEnv->deviceType == CL_DEVICE_TYPE_ALL)
1063 deviceType = CL_DEVICE_TYPE_GPU;
1065 deviceType = CL_DEVICE_TYPE_CPU;
1072 deviceType = clEnv->deviceType;
1074 for (i = 0; i < numPlatforms; i++)
1076 char version[MaxTextExtent];
1078 status = clEnv->library->clGetPlatformInfo(clEnv->platform, CL_PLATFORM_VERSION, MaxTextExtent, version, NULL);
1079 if (status != CL_SUCCESS)
1081 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
1082 "clGetPlatformInfo failed.", "(%d)", status);
1085 if (strncmp(version,"OpenCL 1.0 ",11) == 0)
1087 status = clEnv->library->clGetDeviceIDs(platforms[i], deviceType, 1, &(clEnv->device), &numDevices);
1088 if (status != CL_SUCCESS)
1090 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
1091 "clGetDeviceIDs failed.", "(%d)", status);
1094 if (clEnv->device != NULL)
1096 clEnv->platform = platforms[i];
1103 if (platforms!=NULL)
1104 RelinquishMagickMemory(platforms);
1106 OpenCLAvailable = (clEnv->platform!=NULL
1107 && clEnv->device!=NULL)?MagickTrue:MagickFalse;
1109 #ifdef MAGICKCORE_CLPERFMARKER
1110 clEndPerfMarkerAMD();
1113 return OpenCLAvailable;
1116 static MagickBooleanType EnableOpenCLInternal(MagickCLEnv clEnv) {
1117 if (clEnv->OpenCLInitialized != MagickFalse
1118 && clEnv->platform != NULL
1119 && clEnv->device != NULL) {
1120 clEnv->OpenCLDisabled = MagickFalse;
1123 clEnv->OpenCLDisabled = MagickTrue;
1128 static MagickBooleanType autoSelectDevice(MagickCLEnv clEnv, ExceptionInfo* exception);
1130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1134 + I n i t O p e n C L E n v %
1138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1140 % InitOpenCLEnv() initialize the OpenCL environment
1142 % The format of the RelinquishMagickOpenCLEnv method is:
1144 % MagickBooleanType InitOpenCLEnv(MagickCLEnv clEnv, ExceptionInfo* exception)
1146 % A description of each parameter follows:
1148 % o clEnv: OpenCL environment structure
1150 % o exception: return any errors or warnings.
1155 MagickBooleanType InitOpenCLEnvInternal(MagickCLEnv clEnv, ExceptionInfo* exception) {
1156 MagickBooleanType status = MagickTrue;
1158 cl_context_properties cps[3];
1160 #ifdef MAGICKCORE_CLPERFMARKER
1162 int status = clInitializePerfMarkerAMD();
1163 if (status == AP_SUCCESS) {
1164 //printf("PerfMarker successfully initialized\n");
1168 clEnv->OpenCLInitialized = MagickTrue;
1170 /* check and init the global lib */
1171 OpenCLLib=GetOpenCLLib();
1174 clEnv->library=OpenCLLib;
1178 /* turn off opencl */
1179 MagickBooleanType flag;
1181 SetMagickOpenCLEnvParamInternal(clEnv, MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED
1182 , sizeof(MagickBooleanType), &flag, exception);
1185 if (clEnv->OpenCLDisabled != MagickFalse)
1188 clEnv->OpenCLDisabled = MagickTrue;
1189 /* setup the OpenCL platform and device */
1190 status = InitOpenCLPlatformDevice(clEnv, exception);
1191 if (status == MagickFalse) {
1192 /* No OpenCL device available */
1196 /* create an OpenCL context */
1197 cps[0] = CL_CONTEXT_PLATFORM;
1198 cps[1] = (cl_context_properties)clEnv->platform;
1200 clEnv->context = clEnv->library->clCreateContext(cps, 1, &(clEnv->device), NULL, NULL, &clStatus);
1201 if (clStatus != CL_SUCCESS)
1203 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
1204 "clCreateContext failed.", "(%d)", clStatus);
1205 status = MagickFalse;
1209 status = CompileOpenCLKernels(clEnv, exception);
1210 if (status == MagickFalse) {
1211 (void) ThrowMagickException(exception, GetMagickModule(), DelegateWarning,
1212 "clCreateCommandQueue failed.", "(%d)", status);
1214 status = MagickFalse;
1218 status = EnableOpenCLInternal(clEnv);
1226 MagickBooleanType InitOpenCLEnv(MagickCLEnv clEnv, ExceptionInfo* exception) {
1227 MagickBooleanType status = MagickFalse;
1232 #ifdef MAGICKCORE_CLPERFMARKER
1233 clBeginPerfMarkerAMD(__FUNCTION__,"");
1236 LockSemaphoreInfo(clEnv->lock);
1237 if (clEnv->OpenCLInitialized == MagickFalse) {
1238 if (clEnv->device==NULL
1239 && clEnv->OpenCLDisabled == MagickFalse)
1240 status = autoSelectDevice(clEnv, exception);
1242 status = InitOpenCLEnvInternal(clEnv, exception);
1244 UnlockSemaphoreInfo(clEnv->lock);
1246 #ifdef MAGICKCORE_CLPERFMARKER
1247 clEndPerfMarkerAMD();
1254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1258 + 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 %
1262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1264 % AcquireOpenCLCommandQueue() acquires an OpenCL command queue
1266 % The format of the AcquireOpenCLCommandQueue method is:
1268 % cl_command_queue AcquireOpenCLCommandQueue(MagickCLEnv clEnv)
1270 % A description of each parameter follows:
1272 % o clEnv: the OpenCL environment.
1277 cl_command_queue AcquireOpenCLCommandQueue(MagickCLEnv clEnv)
1280 return clEnv->library->clCreateCommandQueue(clEnv->context, clEnv->device, 0, NULL);
1287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 + R e l i n q u i s h O p e n C L C o m m a n d Q u e u e %
1295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1297 % RelinquishOpenCLCommandQueue() releases the OpenCL command queue
1299 % The format of the RelinquishOpenCLCommandQueue method is:
1301 % MagickBooleanType RelinquishOpenCLCommandQueue(MagickCLEnv clEnv,
1302 % cl_command_queue queue)
1304 % A description of each parameter follows:
1306 % o clEnv: the OpenCL environment.
1308 % o queue: the OpenCL queue to be released.
1313 MagickBooleanType RelinquishOpenCLCommandQueue(MagickCLEnv clEnv, cl_command_queue queue)
1317 return ((clEnv->library->clReleaseCommandQueue(queue) == CL_SUCCESS) ? MagickTrue:MagickFalse);
1326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1330 + A c q u i r e O p e n C L K e r n e l %
1334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1336 % AcquireOpenCLKernel() acquires an OpenCL kernel
1338 % The format of the AcquireOpenCLKernel method is:
1340 % cl_kernel AcquireOpenCLKernel(MagickCLEnv clEnv,
1341 % MagickOpenCLProgram program, const char* kernelName)
1343 % A description of each parameter follows:
1345 % o clEnv: the OpenCL environment.
1347 % o program: the OpenCL program module that the kernel belongs to.
1349 % o kernelName: the name of the kernel
1354 cl_kernel AcquireOpenCLKernel(MagickCLEnv clEnv, MagickOpenCLProgram program, const char* kernelName)
1357 cl_kernel kernel = NULL;
1358 if (clEnv != NULL && kernelName!=NULL)
1360 kernel = clEnv->library->clCreateKernel(clEnv->programs[program], kernelName, &clStatus);
1367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1371 + R e l i n q u i s h O p e n C L K e r n e l %
1375 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1377 % RelinquishOpenCLKernel() releases an OpenCL kernel
1379 % The format of the RelinquishOpenCLKernel method is:
1381 % MagickBooleanType RelinquishOpenCLKernel(MagickCLEnv clEnv,
1384 % A description of each parameter follows:
1386 % o clEnv: the OpenCL environment.
1388 % o kernel: the OpenCL kernel object to be released.
1394 MagickBooleanType RelinquishOpenCLKernel(MagickCLEnv clEnv, cl_kernel kernel)
1396 MagickBooleanType status = MagickFalse;
1397 if (clEnv != NULL && kernel != NULL)
1399 status = ((clEnv->library->clReleaseKernel(kernel) == CL_SUCCESS)?MagickTrue:MagickFalse);
1405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1409 + G e t O p e n C L D e v i c e L o c a l M e m o r y S i z e %
1413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1415 % GetOpenCLDeviceLocalMemorySize() returns local memory size of the device
1417 % The format of the GetOpenCLDeviceLocalMemorySize method is:
1419 % unsigned long GetOpenCLDeviceLocalMemorySize(MagickCLEnv clEnv)
1421 % A description of each parameter follows:
1423 % o clEnv: the OpenCL environment.
1429 unsigned long GetOpenCLDeviceLocalMemorySize(MagickCLEnv clEnv)
1431 cl_ulong localMemorySize;
1432 clEnv->library->clGetDeviceInfo(clEnv->device, CL_DEVICE_LOCAL_MEM_SIZE, sizeof(cl_ulong), &localMemorySize, NULL);
1433 return (unsigned long)localMemorySize;
1437 unsigned long GetOpenCLDeviceMaxMemAllocSize(MagickCLEnv clEnv)
1439 cl_ulong maxMemAllocSize;
1440 clEnv->library->clGetDeviceInfo(clEnv->device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &maxMemAllocSize, NULL);
1441 return (unsigned long)maxMemAllocSize;
1446 Beginning of the OpenCL device selection infrastructure
1452 ,DS_INVALID_PROFILE = 1000
1454 ,DS_INVALID_PERF_EVALUATOR_TYPE
1455 ,DS_INVALID_PERF_EVALUATOR
1456 ,DS_PERF_EVALUATOR_ERROR
1458 ,DS_UNKNOWN_DEVICE_TYPE
1459 ,DS_PROFILE_FILE_ERROR
1460 ,DS_SCORE_SERIALIZER_ERROR
1461 ,DS_SCORE_DESERIALIZER_ERROR
1466 DS_DEVICE_NATIVE_CPU = 0
1467 ,DS_DEVICE_OPENCL_DEVICE
1472 ds_device_type type;
1473 cl_device_id oclDeviceID;
1474 char* oclDeviceName;
1475 char* oclDriverVersion;
1476 cl_uint oclMaxClockFrequency;
1477 cl_uint oclMaxComputeUnits;
1478 void* score; /* a pointer to the score data, the content/format is application defined */
1482 unsigned int numDevices;
1484 const char* version;
1487 /* deallocate memory used by score */
1488 typedef ds_status (*ds_score_release)(void* score);
1490 static ds_status releaseDeviceResource(ds_device* device, ds_score_release sr) {
1491 ds_status status = DS_SUCCESS;
1493 if (device->oclDeviceName) free(device->oclDeviceName);
1494 if (device->oclDriverVersion) free(device->oclDriverVersion);
1495 if (device->score) status = sr(device->score);
1500 static ds_status releaseDSProfile(ds_profile* profile, ds_score_release sr) {
1501 ds_status status = DS_SUCCESS;
1502 if (profile!=NULL) {
1503 if (profile->devices!=NULL && sr!=NULL) {
1505 for (i = 0; i < profile->numDevices; i++) {
1506 status = releaseDeviceResource(profile->devices+i,sr);
1507 if (status != DS_SUCCESS)
1510 free(profile->devices);
1518 static ds_status initDSProfile(ds_profile** p, const char* version) {
1520 cl_uint numPlatforms = 0;
1521 cl_platform_id* platforms = NULL;
1522 cl_device_id* devices = NULL;
1523 ds_status status = DS_SUCCESS;
1524 ds_profile* profile = NULL;
1525 unsigned int next = 0;
1529 return DS_INVALID_PROFILE;
1531 profile = (ds_profile*)malloc(sizeof(ds_profile));
1532 if (profile == NULL)
1533 return DS_MEMORY_ERROR;
1535 memset(profile, 0, sizeof(ds_profile));
1537 OpenCLLib->clGetPlatformIDs(0, NULL, &numPlatforms);
1538 if (numPlatforms > 0) {
1539 platforms = (cl_platform_id*)malloc(numPlatforms*sizeof(cl_platform_id));
1540 if (platforms == NULL) {
1541 status = DS_MEMORY_ERROR;
1544 OpenCLLib->clGetPlatformIDs(numPlatforms, platforms, NULL);
1545 for (i = 0; i < (unsigned int)numPlatforms; i++) {
1547 if (OpenCLLib->clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_CPU | CL_DEVICE_TYPE_GPU, 0, NULL, &num) == CL_SUCCESS)
1552 profile->numDevices = numDevices+1; /* +1 to numDevices to include the native CPU */
1554 profile->devices = (ds_device*)malloc(profile->numDevices*sizeof(ds_device));
1555 if (profile->devices == NULL) {
1556 profile->numDevices = 0;
1557 status = DS_MEMORY_ERROR;
1560 memset(profile->devices, 0, profile->numDevices*sizeof(ds_device));
1562 if (numDevices > 0) {
1563 devices = (cl_device_id*)malloc(numDevices*sizeof(cl_device_id));
1564 if (devices == NULL) {
1565 status = DS_MEMORY_ERROR;
1568 for (i = 0; i < (unsigned int)numPlatforms; i++) {
1572 for (d = 0; d < 2; d++) {
1574 cl_device_type deviceType;
1577 deviceType = CL_DEVICE_TYPE_GPU;
1580 deviceType = CL_DEVICE_TYPE_CPU;
1586 if (OpenCLLib->clGetDeviceIDs(platforms[i], deviceType, numDevices, devices, &num) != CL_SUCCESS)
1588 for (j = 0; j < num; j++, next++) {
1591 profile->devices[next].type = DS_DEVICE_OPENCL_DEVICE;
1592 profile->devices[next].oclDeviceID = devices[j];
1594 OpenCLLib->clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_NAME
1595 , 0, NULL, &length);
1596 profile->devices[next].oclDeviceName = (char*)malloc(sizeof(char)*length);
1597 OpenCLLib->clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_NAME
1598 , length, profile->devices[next].oclDeviceName, NULL);
1600 OpenCLLib->clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DRIVER_VERSION
1601 , 0, NULL, &length);
1602 profile->devices[next].oclDriverVersion = (char*)malloc(sizeof(char)*length);
1603 OpenCLLib->clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DRIVER_VERSION
1604 , length, profile->devices[next].oclDriverVersion, NULL);
1606 OpenCLLib->clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_MAX_CLOCK_FREQUENCY
1607 , sizeof(cl_uint), &profile->devices[next].oclMaxClockFrequency, NULL);
1609 OpenCLLib->clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_MAX_COMPUTE_UNITS
1610 , sizeof(cl_uint), &profile->devices[next].oclMaxComputeUnits, NULL);
1616 profile->devices[next].type = DS_DEVICE_NATIVE_CPU;
1617 profile->version = version;
1620 if (platforms) free(platforms);
1621 if (devices) free(devices);
1622 if (status == DS_SUCCESS) {
1627 if (profile->devices)
1628 free(profile->devices);
1635 /* Pointer to a function that calculates the score of a device (ex: device->score)
1636 update the data size of score. The encoding and the format of the score data
1637 is implementation defined. The function should return DS_SUCCESS if there's no error to be reported.
1639 typedef ds_status (*ds_perf_evaluator)(ds_device* device, void* data);
1643 ,DS_EVALUATE_NEW_ONLY
1644 } ds_evaluation_type;
1646 static ds_status profileDevices(ds_profile* profile, const ds_evaluation_type type
1647 ,ds_perf_evaluator evaluator, void* evaluatorData, unsigned int* numUpdates) {
1648 ds_status status = DS_SUCCESS;
1650 unsigned int updates = 0;
1652 if (profile == NULL) {
1653 return DS_INVALID_PROFILE;
1655 if (evaluator == NULL) {
1656 return DS_INVALID_PERF_EVALUATOR;
1659 for (i = 0; i < profile->numDevices; i++) {
1660 ds_status evaluatorStatus;
1663 case DS_EVALUATE_NEW_ONLY:
1664 if (profile->devices[i].score != NULL)
1666 /* else fall through */
1667 case DS_EVALUATE_ALL:
1668 evaluatorStatus = evaluator(profile->devices+i,evaluatorData);
1669 if (evaluatorStatus != DS_SUCCESS) {
1670 status = evaluatorStatus;
1676 return DS_INVALID_PERF_EVALUATOR_TYPE;
1681 *numUpdates = updates;
1686 #define DS_TAG_VERSION "<version>"
1687 #define DS_TAG_VERSION_END "</version>"
1688 #define DS_TAG_DEVICE "<device>"
1689 #define DS_TAG_DEVICE_END "</device>"
1690 #define DS_TAG_SCORE "<score>"
1691 #define DS_TAG_SCORE_END "</score>"
1692 #define DS_TAG_DEVICE_TYPE "<type>"
1693 #define DS_TAG_DEVICE_TYPE_END "</type>"
1694 #define DS_TAG_DEVICE_NAME "<name>"
1695 #define DS_TAG_DEVICE_NAME_END "</name>"
1696 #define DS_TAG_DEVICE_DRIVER_VERSION "<driver>"
1697 #define DS_TAG_DEVICE_DRIVER_VERSION_END "</driver>"
1698 #define DS_TAG_DEVICE_MAX_COMPUTE_UNITS "<max cu>"
1699 #define DS_TAG_DEVICE_MAX_COMPUTE_UNITS_END "</max cu>"
1700 #define DS_TAG_DEVICE_MAX_CLOCK_FREQ "<max clock>"
1701 #define DS_TAG_DEVICE_MAX_CLOCK_FREQ_END "</max clock>"
1703 #define DS_DEVICE_NATIVE_CPU_STRING "native_cpu"
1707 typedef ds_status (*ds_score_serializer)(ds_device* device, void** serializedScore, unsigned int* serializedScoreSize);
1708 static ds_status writeProfileToFile(ds_profile* profile, ds_score_serializer serializer, const char* file) {
1709 ds_status status = DS_SUCCESS;
1710 FILE* profileFile = NULL;
1713 if (profile == NULL)
1714 return DS_INVALID_PROFILE;
1716 profileFile = fopen(file, "wb");
1717 if (profileFile==NULL) {
1718 status = DS_FILE_ERROR;
1723 /* write version string */
1724 fwrite(DS_TAG_VERSION, sizeof(char), strlen(DS_TAG_VERSION), profileFile);
1725 fwrite(profile->version, sizeof(char), strlen(profile->version), profileFile);
1726 fwrite(DS_TAG_VERSION_END, sizeof(char), strlen(DS_TAG_VERSION_END), profileFile);
1727 fwrite("\n", sizeof(char), 1, profileFile);
1729 for (i = 0; i < profile->numDevices && status == DS_SUCCESS; i++) {
1730 void* serializedScore;
1731 unsigned int serializedScoreSize;
1733 fwrite(DS_TAG_DEVICE, sizeof(char), strlen(DS_TAG_DEVICE), profileFile);
1735 fwrite(DS_TAG_DEVICE_TYPE, sizeof(char), strlen(DS_TAG_DEVICE_TYPE), profileFile);
1736 fwrite(&profile->devices[i].type,sizeof(ds_device_type),1, profileFile);
1737 fwrite(DS_TAG_DEVICE_TYPE_END, sizeof(char), strlen(DS_TAG_DEVICE_TYPE_END), profileFile);
1739 switch(profile->devices[i].type) {
1740 case DS_DEVICE_NATIVE_CPU:
1742 /* There's no need to emit a device name for the native CPU device. */
1744 fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
1745 fwrite(DS_DEVICE_NATIVE_CPU_STRING,sizeof(char),strlen(DS_DEVICE_NATIVE_CPU_STRING), profileFile);
1746 fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
1750 case DS_DEVICE_OPENCL_DEVICE:
1754 fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
1755 fwrite(profile->devices[i].oclDeviceName,sizeof(char),strlen(profile->devices[i].oclDeviceName), profileFile);
1756 fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
1758 fwrite(DS_TAG_DEVICE_DRIVER_VERSION, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION), profileFile);
1759 fwrite(profile->devices[i].oclDriverVersion,sizeof(char),strlen(profile->devices[i].oclDriverVersion), profileFile);
1760 fwrite(DS_TAG_DEVICE_DRIVER_VERSION_END, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION_END), profileFile);
1762 fwrite(DS_TAG_DEVICE_MAX_COMPUTE_UNITS, sizeof(char), strlen(DS_TAG_DEVICE_MAX_COMPUTE_UNITS), profileFile);
1763 sprintf(tmp,"%d",profile->devices[i].oclMaxComputeUnits);
1764 fwrite(tmp,sizeof(char),strlen(tmp), profileFile);
1765 fwrite(DS_TAG_DEVICE_MAX_COMPUTE_UNITS_END, sizeof(char), strlen(DS_TAG_DEVICE_MAX_COMPUTE_UNITS_END), profileFile);
1767 fwrite(DS_TAG_DEVICE_MAX_CLOCK_FREQ, sizeof(char), strlen(DS_TAG_DEVICE_MAX_CLOCK_FREQ), profileFile);
1768 sprintf(tmp,"%d",profile->devices[i].oclMaxClockFrequency);
1769 fwrite(tmp,sizeof(char),strlen(tmp), profileFile);
1770 fwrite(DS_TAG_DEVICE_MAX_CLOCK_FREQ_END, sizeof(char), strlen(DS_TAG_DEVICE_MAX_CLOCK_FREQ_END), profileFile);
1774 status = DS_UNKNOWN_DEVICE_TYPE;
1778 fwrite(DS_TAG_SCORE, sizeof(char), strlen(DS_TAG_SCORE), profileFile);
1779 status = serializer(profile->devices+i, &serializedScore, &serializedScoreSize);
1780 if (status == DS_SUCCESS && serializedScore!=NULL && serializedScoreSize > 0) {
1781 fwrite(serializedScore, sizeof(char), serializedScoreSize, profileFile);
1782 free(serializedScore);
1784 fwrite(DS_TAG_SCORE_END, sizeof(char), strlen(DS_TAG_SCORE_END), profileFile);
1785 fwrite(DS_TAG_DEVICE_END, sizeof(char), strlen(DS_TAG_DEVICE_END), profileFile);
1786 fwrite("\n",sizeof(char),1,profileFile);
1788 fclose(profileFile);
1794 static ds_status readProFile(const char* fileName, char** content, size_t* contentSize) {
1795 ds_status status = DS_SUCCESS;
1796 FILE * input = NULL;
1799 char* binary = NULL;
1804 input = fopen(fileName, "rb");
1806 return DS_FILE_ERROR;
1809 fseek(input, 0L, SEEK_END);
1810 size = ftell(input);
1812 binary = (char*)malloc(size);
1813 if(binary == NULL) {
1814 status = DS_FILE_ERROR;
1817 rsize = fread(binary, sizeof(char), size, input);
1820 status = DS_FILE_ERROR;
1823 *contentSize = size;
1827 if (input != NULL) fclose(input);
1828 if (status != DS_SUCCESS
1829 && binary != NULL) {
1838 static const char* findString(const char* contentStart, const char* contentEnd, const char* string) {
1839 size_t stringLength;
1840 const char* currentPosition;
1843 stringLength = strlen(string);
1844 currentPosition = contentStart;
1845 for(currentPosition = contentStart; currentPosition < contentEnd; currentPosition++) {
1846 if (*currentPosition == string[0]) {
1847 if (currentPosition+stringLength < contentEnd) {
1848 if (strncmp(currentPosition, string, stringLength) == 0) {
1849 found = currentPosition;
1859 typedef ds_status (*ds_score_deserializer)(ds_device* device, const unsigned char* serializedScore, unsigned int serializedScoreSize);
1860 static ds_status readProfileFromFile(ds_profile* profile, ds_score_deserializer deserializer, const char* file) {
1862 ds_status status = DS_SUCCESS;
1863 char* contentStart = NULL;
1864 const char* contentEnd = NULL;
1868 return DS_INVALID_PROFILE;
1870 status = readProFile(file, &contentStart, &contentSize);
1871 if (status == DS_SUCCESS) {
1872 const char* currentPosition;
1873 const char* dataStart;
1874 const char* dataEnd;
1875 size_t versionStringLength;
1877 contentEnd = contentStart + contentSize;
1878 currentPosition = contentStart;
1881 /* parse the version string */
1882 dataStart = findString(currentPosition, contentEnd, DS_TAG_VERSION);
1883 if (dataStart == NULL) {
1884 status = DS_PROFILE_FILE_ERROR;
1887 dataStart += strlen(DS_TAG_VERSION);
1889 dataEnd = findString(dataStart, contentEnd, DS_TAG_VERSION_END);
1890 if (dataEnd==NULL) {
1891 status = DS_PROFILE_FILE_ERROR;
1895 versionStringLength = strlen(profile->version);
1896 if (versionStringLength!=(size_t)(dataEnd-dataStart)
1897 || strncmp(profile->version, dataStart, versionStringLength)!=(int)0) {
1898 /* version mismatch */
1899 status = DS_PROFILE_FILE_ERROR;
1902 currentPosition = dataEnd+strlen(DS_TAG_VERSION_END);
1904 /* parse the device information */
1905 DisableMSCWarning(4127)
1910 const char* deviceTypeStart;
1911 const char* deviceTypeEnd;
1912 ds_device_type deviceType;
1914 const char* deviceNameStart;
1915 const char* deviceNameEnd;
1917 const char* deviceScoreStart;
1918 const char* deviceScoreEnd;
1920 const char* deviceDriverStart;
1921 const char* deviceDriverEnd;
1923 const char* tmpStart;
1927 cl_uint maxClockFrequency;
1928 cl_uint maxComputeUnits;
1930 dataStart = findString(currentPosition, contentEnd, DS_TAG_DEVICE);
1931 if (dataStart==NULL) {
1932 /* nothing useful remain, quit...*/
1935 dataStart+=strlen(DS_TAG_DEVICE);
1936 dataEnd = findString(dataStart, contentEnd, DS_TAG_DEVICE_END);
1937 if (dataEnd==NULL) {
1938 status = DS_PROFILE_FILE_ERROR;
1942 /* parse the device type */
1943 deviceTypeStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_TYPE);
1944 if (deviceTypeStart==NULL) {
1945 status = DS_PROFILE_FILE_ERROR;
1948 deviceTypeStart+=strlen(DS_TAG_DEVICE_TYPE);
1949 deviceTypeEnd = findString(deviceTypeStart, contentEnd, DS_TAG_DEVICE_TYPE_END);
1950 if (deviceTypeEnd==NULL) {
1951 status = DS_PROFILE_FILE_ERROR;
1954 memcpy(&deviceType, deviceTypeStart, sizeof(ds_device_type));
1957 /* parse the device name */
1958 if (deviceType == DS_DEVICE_OPENCL_DEVICE) {
1960 deviceNameStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_NAME);
1961 if (deviceNameStart==NULL) {
1962 status = DS_PROFILE_FILE_ERROR;
1965 deviceNameStart+=strlen(DS_TAG_DEVICE_NAME);
1966 deviceNameEnd = findString(deviceNameStart, contentEnd, DS_TAG_DEVICE_NAME_END);
1967 if (deviceNameEnd==NULL) {
1968 status = DS_PROFILE_FILE_ERROR;
1973 deviceDriverStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION);
1974 if (deviceDriverStart==NULL) {
1975 status = DS_PROFILE_FILE_ERROR;
1978 deviceDriverStart+=strlen(DS_TAG_DEVICE_DRIVER_VERSION);
1979 deviceDriverEnd = findString(deviceDriverStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION_END);
1980 if (deviceDriverEnd ==NULL) {
1981 status = DS_PROFILE_FILE_ERROR;
1986 tmpStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_MAX_COMPUTE_UNITS);
1987 if (tmpStart==NULL) {
1988 status = DS_PROFILE_FILE_ERROR;
1991 tmpStart+=strlen(DS_TAG_DEVICE_MAX_COMPUTE_UNITS);
1992 tmpEnd = findString(tmpStart, contentEnd, DS_TAG_DEVICE_MAX_COMPUTE_UNITS_END);
1993 if (tmpEnd ==NULL) {
1994 status = DS_PROFILE_FILE_ERROR;
1997 memcpy(tmp,tmpStart,tmpEnd-tmpStart);
1998 tmp[tmpEnd-tmpStart] = '\0';
1999 maxComputeUnits = atoi(tmp);
2002 tmpStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_MAX_CLOCK_FREQ);
2003 if (tmpStart==NULL) {
2004 status = DS_PROFILE_FILE_ERROR;
2007 tmpStart+=strlen(DS_TAG_DEVICE_MAX_CLOCK_FREQ);
2008 tmpEnd = findString(tmpStart, contentEnd, DS_TAG_DEVICE_MAX_CLOCK_FREQ_END);
2009 if (tmpEnd ==NULL) {
2010 status = DS_PROFILE_FILE_ERROR;
2013 memcpy(tmp,tmpStart,tmpEnd-tmpStart);
2014 tmp[tmpEnd-tmpStart] = '\0';
2015 maxClockFrequency = atoi(tmp);
2018 /* check if this device is on the system */
2019 for (i = 0; i < profile->numDevices; i++) {
2020 if (profile->devices[i].type == DS_DEVICE_OPENCL_DEVICE) {
2021 size_t actualDeviceNameLength;
2022 size_t driverVersionLength;
2024 actualDeviceNameLength = strlen(profile->devices[i].oclDeviceName);
2025 driverVersionLength = strlen(profile->devices[i].oclDriverVersion);
2026 if (actualDeviceNameLength == (size_t)(deviceNameEnd - deviceNameStart)
2027 && driverVersionLength == (size_t)(deviceDriverEnd - deviceDriverStart)
2028 && maxComputeUnits == profile->devices[i].oclMaxComputeUnits
2029 && maxClockFrequency == profile->devices[i].oclMaxClockFrequency
2030 && strncmp(profile->devices[i].oclDeviceName, deviceNameStart, actualDeviceNameLength)==(int)0
2031 && strncmp(profile->devices[i].oclDriverVersion, deviceDriverStart, driverVersionLength)==(int)0) {
2033 deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
2034 if (deviceNameStart==NULL) {
2035 status = DS_PROFILE_FILE_ERROR;
2038 deviceScoreStart+=strlen(DS_TAG_SCORE);
2039 deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
2040 status = deserializer(profile->devices+i, (const unsigned char*)deviceScoreStart, deviceScoreEnd-deviceScoreStart);
2041 if (status != DS_SUCCESS) {
2049 else if (deviceType == DS_DEVICE_NATIVE_CPU) {
2050 for (i = 0; i < profile->numDevices; i++) {
2051 if (profile->devices[i].type == DS_DEVICE_NATIVE_CPU) {
2052 deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
2053 if (deviceScoreStart==NULL) {
2054 status = DS_PROFILE_FILE_ERROR;
2057 deviceScoreStart+=strlen(DS_TAG_SCORE);
2058 deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
2059 status = deserializer(profile->devices+i, (const unsigned char*)deviceScoreStart, deviceScoreEnd-deviceScoreStart);
2060 if (status != DS_SUCCESS) {
2067 /* skip over the current one to find the next device */
2068 currentPosition = dataEnd+strlen(DS_TAG_DEVICE_END);
2072 if (contentStart!=NULL) free(contentStart);
2078 static ds_status getNumDeviceWithEmptyScore(ds_profile* profile, unsigned int* num) {
2080 if (profile == NULL || num==NULL)
2081 return DS_MEMORY_ERROR;
2083 for (i = 0; i < profile->numDevices; i++) {
2084 if (profile->devices[i].score == NULL) {
2093 End of the OpenCL device selection infrastructure
2097 typedef double AccelerateScoreType;
2099 static ds_status AcceleratePerfEvaluator(ds_device *device,
2100 void *magick_unused(data))
2102 #define ACCELERATE_PERF_DIMEN "2048x1536"
2104 #define ReturnStatus(status) \
2107 RelinquishMagickOpenCLEnv(clEnv); \
2108 if (oldClEnv!=NULL) \
2109 defaultCLEnv = oldClEnv; \
2123 magick_unreferenced(data);
2126 ReturnStatus(DS_PERF_EVALUATOR_ERROR);
2128 clEnv=AcquireMagickOpenCLEnv();
2129 exception=AcquireExceptionInfo();
2131 if (device->type == DS_DEVICE_NATIVE_CPU)
2134 MagickBooleanType flag=MagickTrue;
2135 SetMagickOpenCLEnvParamInternal(clEnv,
2136 MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED,sizeof(MagickBooleanType),
2139 else if (device->type == DS_DEVICE_OPENCL_DEVICE)
2142 SetMagickOpenCLEnvParamInternal(clEnv,MAGICK_OPENCL_ENV_PARAM_DEVICE,
2143 sizeof(cl_device_id),&device->oclDeviceID,exception);
2146 ReturnStatus(DS_PERF_EVALUATOR_ERROR);
2148 /* recompile the OpenCL kernels if it needs to */
2149 clEnv->disableProgramCache = defaultCLEnv->disableProgramCache;
2151 InitOpenCLEnvInternal(clEnv,exception);
2152 oldClEnv=defaultCLEnv;
2155 /* microbenchmark */
2166 imageInfo=AcquireImageInfo();
2167 CloneString(&imageInfo->size,ACCELERATE_PERF_DIMEN);
2168 CopyMagickString(imageInfo->filename,"xc:none",MaxTextExtent);
2169 inputImage=ReadImage(imageInfo,exception);
2171 initAccelerateTimer(&timer);
2173 for (i=0; i<=NUM_ITER; i++)
2181 startAccelerateTimer(&timer);
2183 #ifdef MAGICKCORE_CLPERFMARKER
2184 clBeginPerfMarkerAMD("PerfEvaluatorRegion","");
2187 bluredImage=BlurImage(inputImage,10.0f,3.5f,exception);
2188 unsharpedImage=UnsharpMaskImage(bluredImage,2.0f,2.0f,50.0f,10.0f,
2190 resizedImage=ResizeImage(unsharpedImage,640,480,LanczosFilter,1.0,
2193 #ifdef MAGICKCORE_CLPERFMARKER
2194 clEndPerfMarkerAMD();
2198 stopAccelerateTimer(&timer);
2201 DestroyImage(bluredImage);
2203 DestroyImage(unsharpedImage);
2205 DestroyImage(resizedImage);
2207 DestroyImage(inputImage);
2209 /* end of microbenchmark */
2211 if (device->score == NULL)
2212 device->score=malloc(sizeof(AccelerateScoreType));
2213 *(AccelerateScoreType*)device->score=readAccelerateTimer(&timer);
2215 ReturnStatus(DS_SUCCESS);
2218 ds_status AccelerateScoreSerializer(ds_device* device, void** serializedScore, unsigned int* serializedScoreSize) {
2221 /* generate a string from the score */
2222 char* s = (char*)malloc(sizeof(char)*256);
2223 sprintf(s,"%.4f",*((AccelerateScoreType*)device->score));
2224 *serializedScore = (void*)s;
2225 *serializedScoreSize = strlen(s);
2229 return DS_SCORE_SERIALIZER_ERROR;
2233 ds_status AccelerateScoreDeserializer(ds_device* device, const unsigned char* serializedScore, unsigned int serializedScoreSize) {
2235 /* convert the string back to an int */
2236 char* s = (char*)malloc(serializedScoreSize+1);
2237 memcpy(s, serializedScore, serializedScoreSize);
2238 s[serializedScoreSize] = (char)'\0';
2239 device->score = malloc(sizeof(AccelerateScoreType));
2240 *((AccelerateScoreType*)device->score) = (AccelerateScoreType)atof(s);
2245 return DS_SCORE_DESERIALIZER_ERROR;
2249 ds_status AccelerateScoreRelease(void* score) {
2257 #define IMAGEMAGICK_PROFILE_VERSION "ImageMagick Device Selection v0.9"
2258 #define IMAGEMAGICK_PROFILE_FILE "ImagemagickOpenCLDeviceProfile"
2259 static MagickBooleanType autoSelectDevice(MagickCLEnv clEnv, ExceptionInfo* exception) {
2261 MagickBooleanType mStatus = MagickFalse;
2263 ds_profile* profile;
2264 unsigned int numDeviceProfiled = 0;
2266 unsigned int bestDeviceIndex;
2267 AccelerateScoreType bestScore;
2268 char path[MaxTextExtent];
2269 MagickBooleanType flag;
2270 ds_evaluation_type profileType;
2272 LockDefaultOpenCLEnv();
2274 /* Initially, just set OpenCL to off */
2276 SetMagickOpenCLEnvParamInternal(clEnv, MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED
2277 , sizeof(MagickBooleanType), &flag, exception);
2279 /* check and init the global lib */
2280 OpenCLLib=GetOpenCLLib();
2281 if (OpenCLLib==NULL)
2283 mStatus=InitOpenCLEnvInternal(clEnv, exception);
2287 status = initDSProfile(&profile, IMAGEMAGICK_PROFILE_VERSION);
2288 if (status!=DS_SUCCESS) {
2289 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError, "Error when initializing the profile", "'%s'", ".");
2293 (void) FormatLocaleString(path,MaxTextExtent,"%s%s%s"
2294 ,GetOpenCLCachedFilesDirectory()
2295 ,DirectorySeparator,IMAGEMAGICK_PROFILE_FILE);
2297 if (clEnv->regenerateProfile != MagickFalse) {
2298 profileType = DS_EVALUATE_ALL;
2301 readProfileFromFile(profile, AccelerateScoreDeserializer, path);
2302 profileType = DS_EVALUATE_NEW_ONLY;
2304 status = profileDevices(profile, profileType, AcceleratePerfEvaluator, NULL, &numDeviceProfiled);
2306 if (status!=DS_SUCCESS) {
2307 (void) ThrowMagickException(exception, GetMagickModule(), ModuleFatalError, "Error when initializing the profile", "'%s'", ".");
2310 if (numDeviceProfiled > 0) {
2311 status = writeProfileToFile(profile, AccelerateScoreSerializer, path);
2312 if (status!=DS_SUCCESS) {
2313 (void) ThrowMagickException(exception, GetMagickModule(), ModuleWarning, "Error when saving the profile into a file", "'%s'", ".");
2317 /* pick the best device */
2318 bestDeviceIndex = 0;
2319 bestScore = *(AccelerateScoreType*)profile->devices[bestDeviceIndex].score;
2320 for (i = 1; i < profile->numDevices; i++) {
2321 AccelerateScoreType score = *(AccelerateScoreType*)profile->devices[i].score;
2322 if (score < bestScore) {
2323 bestDeviceIndex = i;
2328 /* set up clEnv with the best device */
2329 if (profile->devices[bestDeviceIndex].type == DS_DEVICE_NATIVE_CPU) {
2332 SetMagickOpenCLEnvParamInternal(clEnv, MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED
2333 , sizeof(MagickBooleanType), &flag, exception);
2335 else if (profile->devices[bestDeviceIndex].type == DS_DEVICE_OPENCL_DEVICE) {
2338 SetMagickOpenCLEnvParamInternal(clEnv, MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED
2339 , sizeof(MagickBooleanType), &flag, exception);
2340 SetMagickOpenCLEnvParamInternal(clEnv, MAGICK_OPENCL_ENV_PARAM_DEVICE
2341 , sizeof(cl_device_id), &profile->devices[bestDeviceIndex].oclDeviceID,exception);
2344 status = DS_PERF_EVALUATOR_ERROR;
2347 mStatus=InitOpenCLEnvInternal(clEnv, exception);
2349 status = releaseDSProfile(profile, AccelerateScoreRelease);
2350 if (status!=DS_SUCCESS) {
2351 (void) ThrowMagickException(exception, GetMagickModule(), ModuleWarning, "Error when releasing the profile", "'%s'", ".");
2356 UnlockDefaultOpenCLEnv();
2362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2366 + I n i t I m a g e M a g i c k O p e n C L %
2370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2372 % InitImageMagickOpenCL() provides a simplified interface to initialize
2373 % the OpenCL environtment in ImageMagick
2375 % The format of the InitImageMagickOpenCL() method is:
2377 % MagickBooleanType InitImageMagickOpenCL(ImageMagickOpenCLMode mode,
2378 % void* userSelectedDevice,
2379 % void* selectedDevice)
2381 % A description of each parameter follows:
2383 % o mode: OpenCL mode in ImageMagick, could be off,auto,user
2385 % o userSelectedDevice: when in user mode, a pointer to the selected
2388 % o selectedDevice: a pointer to cl_device_id where the selected
2389 % cl_device_id by ImageMagick could be returned
2391 % o exception: exception
2394 MagickExport MagickBooleanType InitImageMagickOpenCL(
2395 ImageMagickOpenCLMode mode,void *userSelectedDevice,void *selectedDevice,
2396 ExceptionInfo *exception)
2398 MagickBooleanType status = MagickFalse;
2399 MagickCLEnv clEnv = NULL;
2400 MagickBooleanType flag;
2402 clEnv = GetDefaultOpenCLEnv();
2406 case MAGICK_OPENCL_OFF:
2408 SetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED
2409 , sizeof(MagickBooleanType), &flag, exception);
2410 status = InitOpenCLEnv(clEnv, exception);
2413 *(cl_device_id*)selectedDevice = NULL;
2416 case MAGICK_OPENCL_DEVICE_SELECT_USER:
2418 if (userSelectedDevice == NULL)
2422 SetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED
2423 , sizeof(MagickBooleanType), &flag, exception);
2425 SetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_DEVICE
2426 , sizeof(cl_device_id), userSelectedDevice,exception);
2428 status = InitOpenCLEnv(clEnv, exception);
2429 if (selectedDevice) {
2430 GetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_DEVICE
2431 , sizeof(cl_device_id), selectedDevice, exception);
2435 case MAGICK_OPENCL_DEVICE_SELECT_AUTO_CLEAR_CACHE:
2437 SetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_PROGRAM_CACHE_DISABLED
2438 , sizeof(MagickBooleanType), &flag, exception);
2440 SetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_REGENERATE_PROFILE
2441 , sizeof(MagickBooleanType), &flag, exception);
2443 /* fall through here!! */
2444 case MAGICK_OPENCL_DEVICE_SELECT_AUTO:
2447 cl_device_id d = NULL;
2449 SetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_OPENCL_DISABLED
2450 , sizeof(MagickBooleanType), &flag, exception);
2451 SetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_DEVICE
2452 , sizeof(cl_device_id), &d,exception);
2453 status = InitOpenCLEnv(clEnv, exception);
2454 if (selectedDevice) {
2455 GetMagickOpenCLEnvParam(clEnv, MAGICK_OPENCL_ENV_PARAM_DEVICE
2456 , sizeof(cl_device_id), selectedDevice, exception);
2467 MagickBooleanType OpenCLThrowMagickException(ExceptionInfo *exception,
2468 const char *module,const char *function,const size_t line,
2469 const ExceptionType severity,const char *tag,const char *format,...) {
2475 status = MagickTrue;
2477 clEnv = GetDefaultOpenCLEnv();
2479 assert(exception != (ExceptionInfo *) NULL);
2480 assert(exception->signature == MagickSignature);
2483 cl_device_type dType;
2484 clEnv->library->clGetDeviceInfo(clEnv->device,CL_DEVICE_TYPE ,sizeof(cl_device_type),&dType,NULL);
2485 if (dType == CL_DEVICE_TYPE_CPU) {
2486 char buffer[MaxTextExtent];
2487 clEnv->library->clGetPlatformInfo(clEnv->platform, CL_PLATFORM_NAME, MaxTextExtent, buffer, NULL);
2489 /* Workaround for Intel OpenCL CPU runtime bug */
2490 /* Turn off OpenCL when a problem is detected! */
2491 if (strncmp(buffer, "Intel",5) == 0) {
2493 InitImageMagickOpenCL(MAGICK_OPENCL_OFF, NULL, NULL, exception);
2498 #ifdef OPENCLLOG_ENABLED
2502 va_start(operands,format);
2503 status=ThrowMagickExceptionList(exception,module,function,line,severity,tag, format,operands);
2507 magick_unreferenced(module);
2508 magick_unreferenced(function);
2509 magick_unreferenced(line);
2510 magick_unreferenced(tag);
2511 magick_unreferenced(format);
2517 MagickPrivate cl_mem GetAndLockRandSeedBuffer(MagickCLEnv clEnv)
2519 LockSemaphoreInfo(clEnv->lock);
2520 if (clEnv->seedsLock == NULL)
2522 ActivateSemaphoreInfo(&clEnv->seedsLock);
2524 LockSemaphoreInfo(clEnv->seedsLock);
2526 if (clEnv->seeds == NULL)
2529 clEnv->numGenerators = NUM_CL_RAND_GENERATORS;
2530 clEnv->seeds = clEnv->library->clCreateBuffer(clEnv->context, CL_MEM_READ_WRITE,
2531 clEnv->numGenerators*4*sizeof(unsigned int),
2533 if (clStatus != CL_SUCCESS)
2535 clEnv->seeds = NULL;
2540 cl_command_queue queue = NULL;
2541 unsigned int *seeds;
2543 queue = AcquireOpenCLCommandQueue(clEnv);
2544 seeds = (unsigned int*) clEnv->library->clEnqueueMapBuffer(queue, clEnv->seeds, CL_TRUE,
2546 clEnv->numGenerators*4
2547 *sizeof(unsigned int),
2548 0, NULL, NULL, &clStatus);
2549 if (clStatus!=CL_SUCCESS)
2551 clEnv->library->clReleaseMemObject(clEnv->seeds);
2555 for (i = 0; i < clEnv->numGenerators; i++) {
2556 RandomInfo* randomInfo = AcquireRandomInfo();
2557 const unsigned long* s = GetRandomInfoSeed(randomInfo);
2559 clEnv->randNormalize = GetRandomInfoNormalize(randomInfo);
2561 seeds[i*4] = (unsigned int) s[0];
2562 seeds[i*4+1] = (unsigned int) 0x50a7f451;
2563 seeds[i*4+2] = (unsigned int) 0x5365417e;
2564 seeds[i*4+3] = (unsigned int) 0xc3a4171a;
2566 randomInfo = DestroyRandomInfo(randomInfo);
2568 clStatus = clEnv->library->clEnqueueUnmapMemObject(queue, clEnv->seeds, seeds, 0,
2570 clEnv->library->clFinish(queue);
2573 RelinquishOpenCLCommandQueue(clEnv, queue);
2576 UnlockSemaphoreInfo(clEnv->lock);
2577 return clEnv->seeds;
2580 MagickPrivate void UnlockRandSeedBuffer(MagickCLEnv clEnv) {
2581 if (clEnv->seedsLock == NULL)
2583 ActivateSemaphoreInfo(&clEnv->seedsLock);
2586 UnlockSemaphoreInfo(clEnv->seedsLock);
2589 MagickPrivate unsigned int GetNumRandGenerators(MagickCLEnv clEnv)
2591 return clEnv->numGenerators;
2595 MagickPrivate float GetRandNormalize(MagickCLEnv clEnv)
2597 return clEnv->randNormalize;
2602 struct _MagickCLEnv {
2603 MagickBooleanType OpenCLInitialized; /* whether OpenCL environment is initialized. */
2606 MagickExport MagickCLEnv AcquireMagickOpenCLEnv()
2611 MagickExport MagickBooleanType RelinquishMagickOpenCLEnv(
2612 MagickCLEnv magick_unused(clEnv))
2614 magick_unreferenced(clEnv);
2620 * Return the OpenCL environment
2622 MagickExport MagickCLEnv GetDefaultOpenCLEnv(
2623 ExceptionInfo *magick_unused(exception))
2625 magick_unreferenced(exception);
2627 return (MagickCLEnv) NULL;
2630 MagickExport MagickCLEnv SetDefaultOpenCLEnv(
2631 MagickCLEnv magick_unused(clEnv))
2633 magick_unreferenced(clEnv);
2635 return (MagickCLEnv) NULL;
2638 MagickExport MagickBooleanType SetMagickOpenCLEnvParam(
2639 MagickCLEnv magick_unused(clEnv),MagickOpenCLEnvParam magick_unused(param),
2640 size_t magick_unused(dataSize),void *magick_unused(data),
2641 ExceptionInfo *magick_unused(exception))
2643 magick_unreferenced(clEnv);
2644 magick_unreferenced(param);
2645 magick_unreferenced(dataSize);
2646 magick_unreferenced(data);
2647 magick_unreferenced(exception);
2652 MagickExport MagickBooleanType GetMagickOpenCLEnvParam(
2653 MagickCLEnv magick_unused(clEnv),MagickOpenCLEnvParam magick_unused(param),
2654 size_t magick_unused(dataSize),void *magick_unused(data),
2655 ExceptionInfo *magick_unused(exception))
2657 magick_unreferenced(clEnv);
2658 magick_unreferenced(param);
2659 magick_unreferenced(dataSize);
2660 magick_unreferenced(data);
2661 magick_unreferenced(exception);
2666 MagickExport MagickBooleanType InitOpenCLEnv(MagickCLEnv magick_unused(clEnv),
2667 ExceptionInfo *magick_unused(exception))
2669 magick_unreferenced(clEnv);
2670 magick_unreferenced(exception);
2675 MagickPrivate cl_command_queue AcquireOpenCLCommandQueue(
2676 MagickCLEnv magick_unused(clEnv))
2678 magick_unreferenced(clEnv);
2680 return (cl_command_queue) NULL;
2683 MagickPrivate MagickBooleanType RelinquishCommandQueue(
2684 MagickCLEnv magick_unused(clEnv),cl_command_queue magick_unused(queue))
2686 magick_unreferenced(clEnv);
2687 magick_unreferenced(queue);
2692 MagickPrivate cl_kernel AcquireOpenCLKernel(
2693 MagickCLEnv magick_unused(clEnv),MagickOpenCLProgram magick_unused(program),
2694 const char *magick_unused(kernelName))
2696 magick_unreferenced(clEnv);
2697 magick_unreferenced(program);
2698 magick_unreferenced(kernelName);
2700 return (cl_kernel)NULL;
2703 MagickPrivate MagickBooleanType RelinquishOpenCLKernel(
2704 MagickCLEnv magick_unused(clEnv),cl_kernel magick_unused(kernel))
2706 magick_unreferenced(clEnv);
2707 magick_unreferenced(kernel);
2712 MagickPrivate unsigned long GetOpenCLDeviceLocalMemorySize(
2713 MagickCLEnv magick_unused(clEnv))
2715 magick_unreferenced(clEnv);
2720 MagickExport MagickBooleanType InitImageMagickOpenCL(
2721 ImageMagickOpenCLMode magick_unused(mode),
2722 void *magick_unused(userSelectedDevice),void *magick_unused(selectedDevice),
2723 ExceptionInfo *magick_unused(exception))
2725 magick_unreferenced(mode);
2726 magick_unreferenced(userSelectedDevice);
2727 magick_unreferenced(selectedDevice);
2728 magick_unreferenced(exception);
2734 MagickBooleanType OpenCLThrowMagickException(ExceptionInfo *exception,
2735 const char *module,const char *function,const size_t line,
2736 const ExceptionType severity,const char *tag,const char *format,...)
2738 magick_unreferenced(exception);
2739 magick_unreferenced(module);
2740 magick_unreferenced(function);
2741 magick_unreferenced(line);
2742 magick_unreferenced(severity);
2743 magick_unreferenced(tag);
2744 magick_unreferenced(format);
2745 return(MagickFalse);
2749 MagickPrivate cl_mem GetAndLockRandSeedBuffer(MagickCLEnv clEnv)
2751 magick_unreferenced(clEnv);
2756 MagickPrivate void UnlockRandSeedBuffer(MagickCLEnv clEnv)
2758 magick_unreferenced(clEnv);
2761 MagickPrivate unsigned int GetNumRandGenerators(MagickCLEnv clEnv)
2763 magick_unreferenced(clEnv);
2767 MagickPrivate float GetRandNormalize(MagickCLEnv clEnv)
2769 magick_unreferenced(clEnv);
2773 #endif /* MAGICKCORE_OPENCL_SUPPORT */
2775 char* openclCachedFilesDirectory;
2776 SemaphoreInfo* openclCachedFilesDirectoryLock;
2779 const char* GetOpenCLCachedFilesDirectory() {
2780 if (openclCachedFilesDirectory == NULL) {
2781 if (openclCachedFilesDirectoryLock == NULL)
2783 ActivateSemaphoreInfo(&openclCachedFilesDirectoryLock);
2785 LockSemaphoreInfo(openclCachedFilesDirectoryLock);
2786 if (openclCachedFilesDirectory == NULL) {
2787 char path[MaxTextExtent];
2790 struct stat attributes;
2791 MagickBooleanType status;
2795 home=GetEnvironmentValue("IMAGEMAGICK_OPENCL_CACHE_DIR");
2796 if (home == (char *) NULL)
2798 #ifdef MAGICKCORE_WINDOWS_SUPPORT
2799 home=GetEnvironmentValue("LOCALAPPDATA");
2800 if (home == (char *) NULL)
2801 home=GetEnvironmentValue("APPDATA");
2802 if (home == (char *) NULL)
2803 home=GetEnvironmentValue("USERPROFILE");
2805 home=GetEnvironmentValue("HOME");
2809 if (home != (char *) NULL)
2811 int mkdirStatus = 0;
2815 /* first check if $HOME/.config exists */
2816 (void) FormatLocaleString(path,MaxTextExtent,"%s%s.config",
2817 home,DirectorySeparator);
2818 status=GetPathAttributes(path,&attributes);
2819 if (status == MagickFalse)
2822 #ifdef MAGICKCORE_WINDOWS_SUPPORT
2823 mkdirStatus = mkdir(path);
2825 mkdirStatus = mkdir(path, 0777);
2829 /* first check if $HOME/.config/ImageMagick exists */
2832 (void) FormatLocaleString(path,MaxTextExtent,"%s%s.config%sImageMagick",
2833 home,DirectorySeparator,DirectorySeparator);
2835 status=GetPathAttributes(path,&attributes);
2836 if (status == MagickFalse)
2838 #ifdef MAGICKCORE_WINDOWS_SUPPORT
2839 mkdirStatus = mkdir(path);
2841 mkdirStatus = mkdir(path, 0777);
2848 temp = (char*)AcquireMagickMemory(strlen(path)+1);
2849 CopyMagickString(temp,path,strlen(path)+1);
2851 home=DestroyString(home);
2853 openclCachedFilesDirectory = temp;
2855 UnlockSemaphoreInfo(openclCachedFilesDirectoryLock);
2857 return openclCachedFilesDirectory;
2860 void startAccelerateTimer(AccelerateTimer* timer) {
2862 QueryPerformanceCounter((LARGE_INTEGER*)&timer->_start);
2867 gettimeofday(&s, 0);
2868 timer->_start = (long long)s.tv_sec * (long long)1.0E3 + (long long)s.tv_usec / (long long)1.0E3;
2872 void stopAccelerateTimer(AccelerateTimer* timer) {
2875 QueryPerformanceCounter((LARGE_INTEGER*)&(n));
2878 gettimeofday(&s, 0);
2879 n = (long long)s.tv_sec * (long long)1.0E3+ (long long)s.tv_usec / (long long)1.0E3;
2883 timer->_clocks += n;
2886 void resetAccelerateTimer(AccelerateTimer* timer) {
2892 void initAccelerateTimer(AccelerateTimer* timer) {
2894 QueryPerformanceFrequency((LARGE_INTEGER*)&timer->_freq);
2896 timer->_freq = (long long)1.0E3;
2898 resetAccelerateTimer(timer);
2901 double readAccelerateTimer(AccelerateTimer* timer) {
2902 return (double)timer->_clocks/(double)timer->_freq;
2906 /* create a function for OpenCL log */
2908 void OpenCLLog(const char* message) {
2910 #ifdef OPENCLLOG_ENABLED
2911 #define OPENCL_LOG_FILE "ImageMagickOpenCL.log"
2914 if (getenv("MAGICK_OCL_LOG"))
2917 char path[MaxTextExtent];
2918 unsigned long allocSize;
2922 clEnv = GetDefaultOpenCLEnv();
2924 /* dump the source into a file */
2925 (void) FormatLocaleString(path,MaxTextExtent,"%s%s%s"
2926 ,GetOpenCLCachedFilesDirectory()
2927 ,DirectorySeparator,OPENCL_LOG_FILE);
2930 log = fopen(path, "ab");
2931 fwrite(message, sizeof(char), strlen(message), log);
2932 fwrite("\n", sizeof(char), 1, log);
2934 if (clEnv->OpenCLInitialized && !clEnv->OpenCLDisabled)
2936 allocSize = GetOpenCLDeviceMaxMemAllocSize(clEnv);
2937 fprintf(log, "Devic Max Memory Alloc Size: %ld\n", allocSize);
2944 magick_unreferenced(message);