Cycles: Fix missing kernel re-compilation after recent changes
[blender.git] / intern / cycles / device / opencl / opencl_util.cpp
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifdef WITH_OPENCL
18
19 #include "device/opencl/opencl.h"
20
21 #include "util/util_logging.h"
22 #include "util/util_md5.h"
23 #include "util/util_path.h"
24 #include "util/util_time.h"
25
26 using std::cerr;
27 using std::endl;
28
29 CCL_NAMESPACE_BEGIN
30
31 OpenCLCache::Slot::ProgramEntry::ProgramEntry()
32  : program(NULL),
33    mutex(NULL)
34 {
35 }
36
37 OpenCLCache::Slot::ProgramEntry::ProgramEntry(const ProgramEntry& rhs)
38  : program(rhs.program),
39    mutex(NULL)
40 {
41 }
42
43 OpenCLCache::Slot::ProgramEntry::~ProgramEntry()
44 {
45         delete mutex;
46 }
47
48 OpenCLCache::Slot::Slot()
49  : context_mutex(NULL),
50    context(NULL)
51 {
52 }
53
54 OpenCLCache::Slot::Slot(const Slot& rhs)
55  : context_mutex(NULL),
56    context(NULL),
57    programs(rhs.programs)
58 {
59 }
60
61 OpenCLCache::Slot::~Slot()
62 {
63         delete context_mutex;
64 }
65
66 OpenCLCache& OpenCLCache::global_instance()
67 {
68         static OpenCLCache instance;
69         return instance;
70 }
71
72 cl_context OpenCLCache::get_context(cl_platform_id platform,
73                                     cl_device_id device,
74                                     thread_scoped_lock& slot_locker)
75 {
76         assert(platform != NULL);
77
78         OpenCLCache& self = global_instance();
79
80         thread_scoped_lock cache_lock(self.cache_lock);
81
82         pair<CacheMap::iterator,bool> ins = self.cache.insert(
83                 CacheMap::value_type(PlatformDevicePair(platform, device), Slot()));
84
85         Slot &slot = ins.first->second;
86
87         /* create slot lock only while holding cache lock */
88         if(!slot.context_mutex)
89                 slot.context_mutex = new thread_mutex;
90
91         /* need to unlock cache before locking slot, to allow store to complete */
92         cache_lock.unlock();
93
94         /* lock the slot */
95         slot_locker = thread_scoped_lock(*slot.context_mutex);
96
97         /* If the thing isn't cached */
98         if(slot.context == NULL) {
99                 /* return with the caller's lock holder holding the slot lock */
100                 return NULL;
101         }
102
103         /* the item was already cached, release the slot lock */
104         slot_locker.unlock();
105
106         cl_int ciErr = clRetainContext(slot.context);
107         assert(ciErr == CL_SUCCESS);
108         (void)ciErr;
109
110         return slot.context;
111 }
112
113 cl_program OpenCLCache::get_program(cl_platform_id platform,
114                                     cl_device_id device,
115                                     ustring key,
116                                     thread_scoped_lock& slot_locker)
117 {
118         assert(platform != NULL);
119
120         OpenCLCache& self = global_instance();
121
122         thread_scoped_lock cache_lock(self.cache_lock);
123
124         pair<CacheMap::iterator,bool> ins = self.cache.insert(
125                 CacheMap::value_type(PlatformDevicePair(platform, device), Slot()));
126
127         Slot &slot = ins.first->second;
128
129         pair<Slot::EntryMap::iterator,bool> ins2 = slot.programs.insert(
130                 Slot::EntryMap::value_type(key, Slot::ProgramEntry()));
131
132         Slot::ProgramEntry &entry = ins2.first->second;
133
134         /* create slot lock only while holding cache lock */
135         if(!entry.mutex)
136                 entry.mutex = new thread_mutex;
137
138         /* need to unlock cache before locking slot, to allow store to complete */
139         cache_lock.unlock();
140
141         /* lock the slot */
142         slot_locker = thread_scoped_lock(*entry.mutex);
143
144         /* If the thing isn't cached */
145         if(entry.program == NULL) {
146                 /* return with the caller's lock holder holding the slot lock */
147                 return NULL;
148         }
149
150         /* the item was already cached, release the slot lock */
151         slot_locker.unlock();
152
153         cl_int ciErr = clRetainProgram(entry.program);
154         assert(ciErr == CL_SUCCESS);
155         (void)ciErr;
156
157         return entry.program;
158 }
159
160 void OpenCLCache::store_context(cl_platform_id platform,
161                                 cl_device_id device,
162                                 cl_context context,
163                                 thread_scoped_lock& slot_locker)
164 {
165         assert(platform != NULL);
166         assert(device != NULL);
167         assert(context != NULL);
168
169         OpenCLCache &self = global_instance();
170
171         thread_scoped_lock cache_lock(self.cache_lock);
172         CacheMap::iterator i = self.cache.find(PlatformDevicePair(platform, device));
173         cache_lock.unlock();
174
175         Slot &slot = i->second;
176
177         /* sanity check */
178         assert(i != self.cache.end());
179         assert(slot.context == NULL);
180
181         slot.context = context;
182
183         /* unlock the slot */
184         slot_locker.unlock();
185
186         /* increment reference count in OpenCL.
187          * The caller is going to release the object when done with it. */
188         cl_int ciErr = clRetainContext(context);
189         assert(ciErr == CL_SUCCESS);
190         (void)ciErr;
191 }
192
193 void OpenCLCache::store_program(cl_platform_id platform,
194                                 cl_device_id device,
195                                 cl_program program,
196                                 ustring key,
197                                 thread_scoped_lock& slot_locker)
198 {
199         assert(platform != NULL);
200         assert(device != NULL);
201         assert(program != NULL);
202
203         OpenCLCache &self = global_instance();
204
205         thread_scoped_lock cache_lock(self.cache_lock);
206
207         CacheMap::iterator i = self.cache.find(PlatformDevicePair(platform, device));
208         assert(i != self.cache.end());
209         Slot &slot = i->second;
210
211         Slot::EntryMap::iterator i2 = slot.programs.find(key);
212         assert(i2 != slot.programs.end());
213         Slot::ProgramEntry &entry = i2->second;
214
215         assert(entry.program == NULL);
216
217         cache_lock.unlock();
218
219         entry.program = program;
220
221         /* unlock the slot */
222         slot_locker.unlock();
223
224         /* Increment reference count in OpenCL.
225          * The caller is going to release the object when done with it.
226          */
227         cl_int ciErr = clRetainProgram(program);
228         assert(ciErr == CL_SUCCESS);
229         (void)ciErr;
230 }
231
232 string OpenCLCache::get_kernel_md5()
233 {
234         OpenCLCache &self = global_instance();
235         thread_scoped_lock lock(self.kernel_md5_lock);
236
237         if(self.kernel_md5.empty()) {
238                 self.kernel_md5 = path_files_md5_hash(path_get("source/kernel"));
239         }
240         return self.kernel_md5;
241 }
242
243 OpenCLDeviceBase::OpenCLProgram::OpenCLProgram(OpenCLDeviceBase *device,
244                                                string program_name,
245                                                string kernel_file,
246                                                string kernel_build_options,
247                                                bool use_stdout)
248  : device(device),
249    program_name(program_name),
250    kernel_file(kernel_file),
251    kernel_build_options(kernel_build_options),
252    use_stdout(use_stdout)
253 {
254         loaded = false;
255         program = NULL;
256 }
257
258 OpenCLDeviceBase::OpenCLProgram::~OpenCLProgram()
259 {
260         release();
261 }
262
263 void OpenCLDeviceBase::OpenCLProgram::release()
264 {
265         for(map<ustring, cl_kernel>::iterator kernel = kernels.begin(); kernel != kernels.end(); ++kernel) {
266                 if(kernel->second) {
267                         clReleaseKernel(kernel->second);
268                         kernel->second = NULL;
269                 }
270         }
271         if(program) {
272                 clReleaseProgram(program);
273                 program = NULL;
274         }
275 }
276
277 void OpenCLDeviceBase::OpenCLProgram::add_log(string msg, bool debug)
278 {
279         if(!use_stdout) {
280                 log += msg + "\n";
281         }
282         else if(!debug) {
283                 printf("%s\n", msg.c_str());
284         }
285         else {
286                 VLOG(2) << msg;
287         }
288 }
289
290 void OpenCLDeviceBase::OpenCLProgram::add_error(string msg)
291 {
292         if(use_stdout) {
293                 fprintf(stderr, "%s\n", msg.c_str());
294         }
295         if(error_msg == "") {
296                 error_msg += "\n";
297         }
298         error_msg += msg;
299 }
300
301 void OpenCLDeviceBase::OpenCLProgram::add_kernel(ustring name)
302 {
303         if(!kernels.count(name)) {
304                 kernels[name] = NULL;
305         }
306 }
307
308 bool OpenCLDeviceBase::OpenCLProgram::build_kernel(const string *debug_src)
309 {
310         string build_options;
311         build_options = device->kernel_build_options(debug_src) + kernel_build_options;
312
313         VLOG(1) << "Build options passed to clBuildProgram: '"
314                 << build_options << "'.";
315         cl_int ciErr = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL);
316
317         /* show warnings even if build is successful */
318         size_t ret_val_size = 0;
319
320         clGetProgramBuildInfo(program, device->cdDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);
321
322         if(ciErr != CL_SUCCESS) {
323                 add_error(string("OpenCL build failed with error ") + clewErrorString(ciErr) + ", errors in console.");
324         }
325
326         if(ret_val_size > 1) {
327                 vector<char> build_log(ret_val_size + 1);
328                 clGetProgramBuildInfo(program, device->cdDevice, CL_PROGRAM_BUILD_LOG, ret_val_size, &build_log[0], NULL);
329
330                 build_log[ret_val_size] = '\0';
331                 /* Skip meaningless empty output from the NVidia compiler. */
332                 if(!(ret_val_size == 2 && build_log[0] == '\n')) {
333                         add_log(string("OpenCL program ") + program_name + " build output: " + string(&build_log[0]), ciErr == CL_SUCCESS);
334                 }
335         }
336
337         return (ciErr == CL_SUCCESS);
338 }
339
340 bool OpenCLDeviceBase::OpenCLProgram::compile_kernel(const string *debug_src)
341 {
342         string source = "#include \"kernel/kernels/opencl/" + kernel_file + "\"\n";
343         /* We compile kernels consisting of many files. unfortunately OpenCL
344          * kernel caches do not seem to recognize changes in included files.
345          * so we force recompile on changes by adding the md5 hash of all files.
346          */
347         source = path_source_replace_includes(source, path_get("source"));
348         source += "\n// " + util_md5_string(source) + "\n";
349
350         if(debug_src) {
351                 path_write_text(*debug_src, source);
352         }
353
354         size_t source_len = source.size();
355         const char *source_str = source.c_str();
356         cl_int ciErr;
357
358         program = clCreateProgramWithSource(device->cxContext,
359                                             1,
360                                             &source_str,
361                                             &source_len,
362                                             &ciErr);
363
364         if(ciErr != CL_SUCCESS) {
365                 add_error(string("OpenCL program creation failed: ") + clewErrorString(ciErr));
366                 return false;
367         }
368
369         double starttime = time_dt();
370         add_log(string("Compiling OpenCL program ") + program_name.c_str(), false);
371         add_log(string("Build flags: ") + kernel_build_options, true);
372
373         if(!build_kernel(debug_src))
374                 return false;
375
376         add_log(string("Kernel compilation of ") + program_name + " finished in " + string_printf("%.2lfs.\n", time_dt() - starttime), false);
377
378         return true;
379 }
380
381 bool OpenCLDeviceBase::OpenCLProgram::load_binary(const string& clbin,
382                                                   const string *debug_src)
383 {
384         /* read binary into memory */
385         vector<uint8_t> binary;
386
387         if(!path_read_binary(clbin, binary)) {
388                 add_error(string_printf("OpenCL failed to read cached binary %s.", clbin.c_str()));
389                 return false;
390         }
391
392         /* create program */
393         cl_int status, ciErr;
394         size_t size = binary.size();
395         const uint8_t *bytes = &binary[0];
396
397         program = clCreateProgramWithBinary(device->cxContext, 1, &device->cdDevice,
398                 &size, &bytes, &status, &ciErr);
399
400         if(status != CL_SUCCESS || ciErr != CL_SUCCESS) {
401                 add_error(string("OpenCL failed create program from cached binary ") + clbin + ": "
402                                  + clewErrorString(status) + " " + clewErrorString(ciErr));
403                 return false;
404         }
405
406         if(!build_kernel(debug_src))
407                 return false;
408
409         return true;
410 }
411
412 bool OpenCLDeviceBase::OpenCLProgram::save_binary(const string& clbin)
413 {
414         size_t size = 0;
415         clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL);
416
417         if(!size)
418                 return false;
419
420         vector<uint8_t> binary(size);
421         uint8_t *bytes = &binary[0];
422
423         clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t*), &bytes, NULL);
424
425         return path_write_binary(clbin, binary);
426 }
427
428 void OpenCLDeviceBase::OpenCLProgram::load()
429 {
430         assert(device);
431
432         loaded = false;
433
434         string device_md5 = device->device_md5_hash(kernel_build_options);
435
436         /* Try to use cached kernel. */
437         thread_scoped_lock cache_locker;
438         ustring cache_key(program_name + device_md5);
439         program = device->load_cached_kernel(cache_key,
440                                              cache_locker);
441
442         if(!program) {
443                 add_log(string("OpenCL program ") + program_name + " not found in cache.", true);
444
445                 /* need to create source to get md5 */
446                 string source = "#include \"kernel/kernels/opencl/" + kernel_file + "\"\n";
447                 source = path_source_replace_includes(source, path_get("source"));
448
449                 string basename = "cycles_kernel_" + program_name + "_" + device_md5 + "_" + util_md5_string(source);
450                 basename = path_cache_get(path_join("kernels", basename));
451                 string clbin = basename + ".clbin";
452
453                 /* path to preprocessed source for debugging */
454                 string clsrc, *debug_src = NULL;
455
456                 if(OpenCLInfo::use_debug()) {
457                         clsrc = basename + ".cl";
458                         debug_src = &clsrc;
459                 }
460
461                 /* If binary kernel exists already, try use it. */
462                 if(path_exists(clbin) && load_binary(clbin)) {
463                         /* Kernel loaded from binary, nothing to do. */
464                         add_log(string("Loaded program from ") + clbin + ".", true);
465                 }
466                 else {
467                         add_log(string("Kernel file ") + clbin + " either doesn't exist or failed to be loaded by driver.", true);
468
469                         /* If does not exist or loading binary failed, compile kernel. */
470                         if(!compile_kernel(debug_src)) {
471                                 return;
472                         }
473
474                         /* Save binary for reuse. */
475                         if(!save_binary(clbin)) {
476                                 add_log(string("Saving compiled OpenCL kernel to ") + clbin + " failed!", true);
477                         }
478                 }
479
480                 /* Cache the program. */
481                 device->store_cached_kernel(program,
482                                             cache_key,
483                                             cache_locker);
484         }
485         else {
486                 add_log(string("Found cached OpenCL program ") + program_name + ".", true);
487         }
488
489         for(map<ustring, cl_kernel>::iterator kernel = kernels.begin(); kernel != kernels.end(); ++kernel) {
490                 assert(kernel->second == NULL);
491                 cl_int ciErr;
492                 string name = "kernel_ocl_" + kernel->first.string();
493                 kernel->second = clCreateKernel(program, name.c_str(), &ciErr);
494                 if(device->opencl_error(ciErr)) {
495                         add_error(string("Error getting kernel ") + name + " from program " + program_name + ": " + clewErrorString(ciErr));
496                         return;
497                 }
498         }
499
500         loaded = true;
501 }
502
503 void OpenCLDeviceBase::OpenCLProgram::report_error()
504 {
505         /* If loaded is true, there was no error. */
506         if(loaded) return;
507         /* if use_stdout is true, the error was already reported. */
508         if(use_stdout) return;
509
510         cerr << error_msg << endl;
511         if(!compile_output.empty()) {
512                 cerr << "OpenCL kernel build output for " << program_name << ":" << endl;
513                 cerr << compile_output << endl;
514         }
515 }
516
517 cl_kernel OpenCLDeviceBase::OpenCLProgram::operator()()
518 {
519         assert(kernels.size() == 1);
520         return kernels.begin()->second;
521 }
522
523 cl_kernel OpenCLDeviceBase::OpenCLProgram::operator()(ustring name)
524 {
525         assert(kernels.count(name));
526         return kernels[name];
527 }
528
529 cl_device_type OpenCLInfo::device_type()
530 {
531         switch(DebugFlags().opencl.device_type)
532         {
533                 case DebugFlags::OpenCL::DEVICE_NONE:
534                         return 0;
535                 case DebugFlags::OpenCL::DEVICE_ALL:
536                         return CL_DEVICE_TYPE_ALL;
537                 case DebugFlags::OpenCL::DEVICE_DEFAULT:
538                         return CL_DEVICE_TYPE_DEFAULT;
539                 case DebugFlags::OpenCL::DEVICE_CPU:
540                         return CL_DEVICE_TYPE_CPU;
541                 case DebugFlags::OpenCL::DEVICE_GPU:
542                         return CL_DEVICE_TYPE_GPU;
543                 case DebugFlags::OpenCL::DEVICE_ACCELERATOR:
544                         return CL_DEVICE_TYPE_ACCELERATOR;
545                 default:
546                         return CL_DEVICE_TYPE_ALL;
547         }
548 }
549
550 bool OpenCLInfo::use_debug()
551 {
552         return DebugFlags().opencl.debug;
553 }
554
555 bool OpenCLInfo::use_single_program()
556 {
557         return DebugFlags().opencl.single_program;
558 }
559
560 bool OpenCLInfo::kernel_use_advanced_shading(const string& platform)
561 {
562         /* keep this in sync with kernel_types.h! */
563         if(platform == "NVIDIA CUDA")
564                 return true;
565         else if(platform == "Apple")
566                 return true;
567         else if(platform == "AMD Accelerated Parallel Processing")
568                 return true;
569         else if(platform == "Intel(R) OpenCL")
570                 return true;
571         /* Make sure officially unsupported OpenCL platforms
572          * does not set up to use advanced shading.
573          */
574         return false;
575 }
576
577 bool OpenCLInfo::kernel_use_split(const string& platform_name,
578                                   const cl_device_type device_type)
579 {
580         if(DebugFlags().opencl.kernel_type == DebugFlags::OpenCL::KERNEL_SPLIT) {
581                 VLOG(1) << "Forcing split kernel to use.";
582                 return true;
583         }
584         if(DebugFlags().opencl.kernel_type == DebugFlags::OpenCL::KERNEL_MEGA) {
585                 VLOG(1) << "Forcing mega kernel to use.";
586                 return false;
587         }
588         /* TODO(sergey): Replace string lookups with more enum-like API,
589          * similar to device/vendor checks blender's gpu.
590          */
591         if(platform_name == "AMD Accelerated Parallel Processing" &&
592            device_type == CL_DEVICE_TYPE_GPU)
593         {
594                 return true;
595         }
596         return false;
597 }
598
599 bool OpenCLInfo::device_supported(const string& platform_name,
600                                   const cl_device_id device_id)
601 {
602         cl_device_type device_type;
603         if(!get_device_type(device_id, &device_type)) {
604                 return false;
605         }
606         string device_name;
607         if(!get_device_name(device_id, &device_name)) {
608                 return false;
609         }
610         /* It is possible tyo have Iris GPU on AMD/Apple OpenCL framework
611          * (aka, it will not be on Intel framework). This isn't supported
612          * and needs an explicit blacklist.
613          */
614         if(strstr(device_name.c_str(), "Iris")) {
615                 return false;
616         }
617         if(platform_name == "AMD Accelerated Parallel Processing" &&
618            device_type == CL_DEVICE_TYPE_GPU)
619         {
620                 return true;
621         }
622         if(platform_name == "Apple" && device_type == CL_DEVICE_TYPE_GPU) {
623                 return true;
624         }
625         return false;
626 }
627
628 bool OpenCLInfo::platform_version_check(cl_platform_id platform,
629                                         string *error)
630 {
631         const int req_major = 1, req_minor = 1;
632         int major, minor;
633         char version[256];
634         clGetPlatformInfo(platform,
635                           CL_PLATFORM_VERSION,
636                           sizeof(version),
637                           &version,
638                           NULL);
639         if(sscanf(version, "OpenCL %d.%d", &major, &minor) < 2) {
640                 if(error != NULL) {
641                         *error = string_printf("OpenCL: failed to parse platform version string (%s).", version);
642                 }
643                 return false;
644         }
645         if(!((major == req_major && minor >= req_minor) || (major > req_major))) {
646                 if(error != NULL) {
647                         *error = string_printf("OpenCL: platform version 1.1 or later required, found %d.%d", major, minor);
648                 }
649                 return false;
650         }
651         if(error != NULL) {
652                 *error = "";
653         }
654         return true;
655 }
656
657 bool OpenCLInfo::device_version_check(cl_device_id device,
658                                       string *error)
659 {
660         const int req_major = 1, req_minor = 1;
661         int major, minor;
662         char version[256];
663         clGetDeviceInfo(device,
664                         CL_DEVICE_OPENCL_C_VERSION,
665                         sizeof(version),
666                         &version,
667                         NULL);
668         if(sscanf(version, "OpenCL C %d.%d", &major, &minor) < 2) {
669                 if(error != NULL) {
670                         *error = string_printf("OpenCL: failed to parse OpenCL C version string (%s).", version);
671                 }
672                 return false;
673         }
674         if(!((major == req_major && minor >= req_minor) || (major > req_major))) {
675                 if(error != NULL) {
676                         *error = string_printf("OpenCL: C version 1.1 or later required, found %d.%d", major, minor);
677                 }
678                 return false;
679         }
680         if(error != NULL) {
681                 *error = "";
682         }
683         return true;
684 }
685
686 string OpenCLInfo::get_hardware_id(string platform_name, cl_device_id device_id)
687 {
688         if(platform_name == "AMD Accelerated Parallel Processing" || platform_name == "Apple") {
689                 /* Use cl_amd_device_topology extension. */
690                 cl_char topology[24];
691                 if(clGetDeviceInfo(device_id, 0x4037, sizeof(topology), topology, NULL) == CL_SUCCESS && topology[0] == 1) {
692                         return string_printf("%02x:%02x.%01x",
693                                              (unsigned int)topology[21],
694                                              (unsigned int)topology[22],
695                                              (unsigned int)topology[23]);
696                 }
697         }
698         else if(platform_name == "NVIDIA CUDA") {
699                 /* Use two undocumented options of the cl_nv_device_attribute_query extension. */
700                 cl_int bus_id, slot_id;
701                 if(clGetDeviceInfo(device_id, 0x4008, sizeof(cl_int), &bus_id,  NULL) == CL_SUCCESS &&
702                    clGetDeviceInfo(device_id, 0x4009, sizeof(cl_int), &slot_id, NULL) == CL_SUCCESS) {
703                         return string_printf("%02x:%02x.%01x",
704                                              (unsigned int)(bus_id),
705                                              (unsigned int)(slot_id >> 3),
706                                              (unsigned int)(slot_id & 0x7));
707                 }
708         }
709         /* No general way to get a hardware ID from OpenCL => give up. */
710         return "";
711 }
712
713 void OpenCLInfo::get_usable_devices(vector<OpenCLPlatformDevice> *usable_devices,
714                                     bool force_all)
715 {
716         const bool force_all_platforms = force_all ||
717                 (DebugFlags().opencl.kernel_type != DebugFlags::OpenCL::KERNEL_DEFAULT);
718         const cl_device_type device_type = OpenCLInfo::device_type();
719         static bool first_time = true;
720 #define FIRST_VLOG(severity) if(first_time) VLOG(severity)
721
722         usable_devices->clear();
723
724         if(device_type == 0) {
725                 FIRST_VLOG(2) << "OpenCL devices are forced to be disabled.";
726                 first_time = false;
727                 return;
728         }
729
730         cl_int error;
731         vector<cl_device_id> device_ids;
732         vector<cl_platform_id> platform_ids;
733
734         /* Get platforms. */
735         if(!get_platforms(&platform_ids, &error)) {
736                 FIRST_VLOG(2) << "Error fetching platforms:"
737                               << string(clewErrorString(error));
738                 first_time = false;
739                 return;
740         }
741         if(platform_ids.size() == 0) {
742                 FIRST_VLOG(2) << "No OpenCL platforms were found.";
743                 first_time = false;
744                 return;
745         }
746         /* Devices are numbered consecutively across platforms. */
747         for(int platform = 0; platform < platform_ids.size(); platform++) {
748                 cl_platform_id platform_id = platform_ids[platform];
749                 string platform_name;
750                 if(!get_platform_name(platform_id, &platform_name)) {
751                         FIRST_VLOG(2) << "Failed to get platform name, ignoring.";
752                         continue;
753                 }
754                 FIRST_VLOG(2) << "Enumerating devices for platform "
755                               << platform_name << ".";
756                 if(!platform_version_check(platform_id)) {
757                         FIRST_VLOG(2) << "Ignoring platform " << platform_name
758                                       << " due to too old compiler version.";
759                         continue;
760                 }
761                 if(!get_platform_devices(platform_id,
762                                          device_type,
763                                          &device_ids,
764                                          &error))
765                 {
766                         FIRST_VLOG(2) << "Ignoring platform " << platform_name
767                                       << ", failed to fetch of devices: "
768                                       << string(clewErrorString(error));
769                         continue;
770                 }
771                 if(device_ids.size() == 0) {
772                         FIRST_VLOG(2) << "Ignoring platform " << platform_name
773                                       << ", it has no devices.";
774                         continue;
775                 }
776                 for(int num = 0; num < device_ids.size(); num++) {
777                         const cl_device_id device_id = device_ids[num];
778                         string device_name;
779                         if(!get_device_name(device_id, &device_name, &error)) {
780                                 FIRST_VLOG(2) << "Failed to fetch device name: "
781                                               << string(clewErrorString(error))
782                                               << ", ignoring.";
783                                 continue;
784                         }
785                         if(!device_version_check(device_id)) {
786                                 FIRST_VLOG(2) << "Ignoring device " << device_name
787                                               << " due to old compiler version.";
788                                 continue;
789                         }
790                         if(force_all_platforms ||
791                            device_supported(platform_name, device_id))
792                         {
793                                 cl_device_type device_type;
794                                 if(!get_device_type(device_id, &device_type, &error)) {
795                                         FIRST_VLOG(2) << "Ignoring device " << device_name
796                                                       << ", failed to fetch device type:"
797                                                       << string(clewErrorString(error));
798                                         continue;
799                                 }
800                                 string readable_device_name =
801                                         get_readable_device_name(device_id);
802                                 if(readable_device_name != device_name) {
803                                         FIRST_VLOG(2) << "Using more readable device name: "
804                                                       << readable_device_name;
805                                 }
806                                 FIRST_VLOG(2) << "Adding new device "
807                                               << readable_device_name << ".";
808                                 string hardware_id = get_hardware_id(platform_name, device_id);
809                                 usable_devices->push_back(OpenCLPlatformDevice(
810                                         platform_id,
811                                         platform_name,
812                                         device_id,
813                                         device_type,
814                                         readable_device_name,
815                                         hardware_id));
816                         }
817                         else {
818                                 FIRST_VLOG(2) << "Ignoring device " << device_name
819                                               << ", not officially supported yet.";
820                         }
821                 }
822         }
823         first_time = false;
824 }
825
826 bool OpenCLInfo::get_platforms(vector<cl_platform_id> *platform_ids,
827                                cl_int *error)
828 {
829         /* Reset from possible previous state. */
830         platform_ids->resize(0);
831         cl_uint num_platforms;
832         if(!get_num_platforms(&num_platforms, error)) {
833                 return false;
834         }
835         /* Get actual platforms. */
836         cl_int err;
837         platform_ids->resize(num_platforms);
838         if((err = clGetPlatformIDs(num_platforms,
839                                    &platform_ids->at(0),
840                                    NULL)) != CL_SUCCESS) {
841                 if(error != NULL) {
842                         *error = err;
843                 }
844                 return false;
845         }
846         if(error != NULL) {
847                 *error = CL_SUCCESS;
848         }
849         return true;
850 }
851
852 vector<cl_platform_id> OpenCLInfo::get_platforms()
853 {
854         vector<cl_platform_id> platform_ids;
855         get_platforms(&platform_ids);
856         return platform_ids;
857 }
858
859 bool OpenCLInfo::get_num_platforms(cl_uint *num_platforms, cl_int *error)
860 {
861         cl_int err;
862         if((err = clGetPlatformIDs(0, NULL, num_platforms)) != CL_SUCCESS) {
863                 if(error != NULL) {
864                         *error = err;
865                 }
866                 *num_platforms = 0;
867                 return false;
868         }
869         if(error != NULL) {
870                 *error = CL_SUCCESS;
871         }
872         return true;
873 }
874
875 cl_uint OpenCLInfo::get_num_platforms()
876 {
877         cl_uint num_platforms;
878         if(!get_num_platforms(&num_platforms)) {
879                 return 0;
880         }
881         return num_platforms;
882 }
883
884 bool OpenCLInfo::get_platform_name(cl_platform_id platform_id,
885                                    string *platform_name)
886 {
887         char buffer[256];
888         if(clGetPlatformInfo(platform_id,
889                              CL_PLATFORM_NAME,
890                              sizeof(buffer),
891                              &buffer,
892                              NULL) != CL_SUCCESS)
893         {
894                 *platform_name = "";
895                 return false;
896         }
897         *platform_name = buffer;
898         return true;
899 }
900
901 string OpenCLInfo::get_platform_name(cl_platform_id platform_id)
902 {
903         string platform_name;
904         if (!get_platform_name(platform_id, &platform_name)) {
905                 return "";
906         }
907         return platform_name;
908 }
909
910 bool OpenCLInfo::get_num_platform_devices(cl_platform_id platform_id,
911                                           cl_device_type device_type,
912                                           cl_uint *num_devices,
913                                           cl_int *error)
914 {
915         cl_int err;
916         if((err = clGetDeviceIDs(platform_id,
917                                  device_type,
918                                  0,
919                                  NULL,
920                                  num_devices)) != CL_SUCCESS)
921         {
922                 if(error != NULL) {
923                         *error = err;
924                 }
925                 *num_devices = 0;
926                 return false;
927         }
928         if(error != NULL) {
929                 *error = CL_SUCCESS;
930         }
931         return true;
932 }
933
934 cl_uint OpenCLInfo::get_num_platform_devices(cl_platform_id platform_id,
935                                              cl_device_type device_type)
936 {
937         cl_uint num_devices;
938         if(!get_num_platform_devices(platform_id,
939                                      device_type,
940                                      &num_devices))
941         {
942                 return 0;
943         }
944         return num_devices;
945 }
946
947 bool OpenCLInfo::get_platform_devices(cl_platform_id platform_id,
948                                       cl_device_type device_type,
949                                       vector<cl_device_id> *device_ids,
950                                       cl_int* error)
951 {
952         /* Reset from possible previous state. */
953         device_ids->resize(0);
954         /* Get number of devices to pre-allocate memory. */
955         cl_uint num_devices;
956         if(!get_num_platform_devices(platform_id,
957                                      device_type,
958                                      &num_devices,
959                                      error))
960         {
961                 return false;
962         }
963         /* Get actual device list. */
964         device_ids->resize(num_devices);
965         cl_int err;
966         if((err = clGetDeviceIDs(platform_id,
967                                  device_type,
968                                  num_devices,
969                                  &device_ids->at(0),
970                                  NULL)) != CL_SUCCESS)
971         {
972                 if(error != NULL) {
973                         *error = err;
974                 }
975                 return false;
976         }
977         if(error != NULL) {
978                 *error = CL_SUCCESS;
979         }
980         return true;
981 }
982
983 vector<cl_device_id> OpenCLInfo::get_platform_devices(cl_platform_id platform_id,
984                                                       cl_device_type device_type)
985 {
986         vector<cl_device_id> devices;
987         get_platform_devices(platform_id, device_type, &devices);
988         return devices;
989 }
990
991 bool OpenCLInfo::get_device_name(cl_device_id device_id,
992                                  string *device_name,
993                                  cl_int* error)
994 {
995         char buffer[1024];
996         cl_int err;
997         if((err = clGetDeviceInfo(device_id,
998                                   CL_DEVICE_NAME,
999                                   sizeof(buffer),
1000                                   &buffer,
1001                                   NULL)) != CL_SUCCESS)
1002         {
1003                 if(error != NULL) {
1004                         *error = err;
1005                 }
1006                 *device_name = "";
1007                 return false;
1008         }
1009         if(error != NULL) {
1010                 *error = CL_SUCCESS;
1011         }
1012         *device_name = buffer;
1013         return true;
1014 }
1015
1016 string OpenCLInfo::get_device_name(cl_device_id device_id)
1017 {
1018         string device_name;
1019         if(!get_device_name(device_id, &device_name)) {
1020                 return "";
1021         }
1022         return device_name;
1023 }
1024
1025 bool OpenCLInfo::get_device_type(cl_device_id device_id,
1026                                  cl_device_type *device_type,
1027                                  cl_int* error)
1028 {
1029         cl_int err;
1030         if((err = clGetDeviceInfo(device_id,
1031                                   CL_DEVICE_TYPE,
1032                                   sizeof(cl_device_type),
1033                                   device_type,
1034                                   NULL)) != CL_SUCCESS)
1035         {
1036                 if(error != NULL) {
1037                         *error = err;
1038                 }
1039                 *device_type = 0;
1040                 return false;
1041         }
1042         if(error != NULL) {
1043                 *error = CL_SUCCESS;
1044         }
1045         return true;
1046 }
1047
1048 cl_device_type OpenCLInfo::get_device_type(cl_device_id device_id)
1049 {
1050         cl_device_type device_type;
1051         if(!get_device_type(device_id, &device_type)) {
1052                 return 0;
1053         }
1054         return device_type;
1055 }
1056
1057 string OpenCLInfo::get_readable_device_name(cl_device_id device_id)
1058 {
1059         char board_name[1024];
1060         if(clGetDeviceInfo(device_id,
1061                            CL_DEVICE_BOARD_NAME_AMD,
1062                            sizeof(board_name),
1063                            &board_name,
1064                            NULL) == CL_SUCCESS)
1065         {
1066                 return board_name;
1067         }
1068         /* Fallback to standard device name API. */
1069         return get_device_name(device_id);
1070 }
1071
1072 CCL_NAMESPACE_END
1073
1074 #endif