Bump version to 5.0-14
[LibreOffice.git] / opencl / inc / opencl_device_selection.h
blob263b8c006b2fc1d5477d9ca1d09b203b8fec132f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
11 #define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
13 #ifdef _MSC_VER
14 #define _CRT_SECURE_NO_WARNINGS
15 #endif
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
21 #include <clew/clew.h>
23 #define DS_DEVICE_NAME_LENGTH 256
25 enum ds_status
27 DS_SUCCESS = 0
28 ,DS_INVALID_PROFILE = 1000
29 ,DS_MEMORY_ERROR
30 , DS_INVALID_PERF_EVALUATOR_TYPE
31 , DS_INVALID_PERF_EVALUATOR
32 , DS_PERF_EVALUATOR_ERROR
33 , DS_FILE_ERROR
34 , DS_UNKNOWN_DEVICE_TYPE
35 , DS_PROFILE_FILE_ERROR
36 , DS_SCORE_SERIALIZER_ERROR
37 , DS_SCORE_DESERIALIZER_ERROR
40 // device type
41 enum ds_device_type
43 DS_DEVICE_NATIVE_CPU = 0
44 ,DS_DEVICE_OPENCL_DEVICE
48 struct ds_device
50 ds_device_type type;
51 cl_device_id oclDeviceID;
52 char* oclPlatformVendor;
53 char* oclDeviceName;
54 char* oclDriverVersion;
55 void* score; // a pointer to the score data, the content/format is application defined
58 struct ds_profile
60 unsigned int numDevices;
61 ds_device* devices;
62 const char* version;
65 // deallocate memory used by score
66 typedef ds_status(* ds_score_release)(void* score);
67 inline ds_status releaseDSProfile(ds_profile* profile, ds_score_release sr)
69 ds_status status = DS_SUCCESS;
70 if (profile != NULL)
72 if (profile->devices != NULL && sr != NULL)
74 unsigned int i;
75 for (i = 0; i < profile->numDevices; i++)
77 free(profile->devices[i].oclPlatformVendor);
78 free(profile->devices[i].oclDeviceName);
79 free(profile->devices[i].oclDriverVersion);
80 status = sr(profile->devices[i].score);
81 if (status != DS_SUCCESS) break;
83 free(profile->devices);
85 free(profile);
87 return status;
91 inline ds_status initDSProfile(ds_profile** p, const char* version)
93 int numDevices;
94 cl_uint numPlatforms;
95 cl_platform_id* platforms = NULL;
96 cl_device_id* devices = NULL;
97 ds_status status = DS_SUCCESS;
98 ds_profile* profile = NULL;
99 unsigned int next;
100 unsigned int i;
102 if (p == NULL) return DS_INVALID_PROFILE;
104 profile = static_cast<ds_profile*>(malloc(sizeof(ds_profile)));
105 if (profile == NULL) return DS_MEMORY_ERROR;
107 memset(profile, 0, sizeof(ds_profile));
109 clGetPlatformIDs(0, NULL, &numPlatforms);
110 if (numPlatforms != 0)
112 platforms = static_cast<cl_platform_id*>(malloc(numPlatforms * sizeof(cl_platform_id)));
113 if (platforms == NULL)
115 status = DS_MEMORY_ERROR;
116 goto cleanup;
118 clGetPlatformIDs(numPlatforms, platforms, NULL);
121 numDevices = 0;
122 for (i = 0; i < (unsigned int)numPlatforms; i++)
124 cl_uint num = 0;
125 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, NULL, &num);
126 if (err != CL_SUCCESS)
128 /* we want to catch at least the case when the call returns
129 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
130 * don't set num to 0 in this case; but in fact this is a good
131 * thing to do for _any_ error returned by the call
133 num = 0;
135 numDevices += num;
137 if (numDevices != 0)
139 devices = static_cast<cl_device_id*>(malloc(numDevices * sizeof(cl_device_id)));
140 if (devices == NULL)
142 status = DS_MEMORY_ERROR;
143 goto cleanup;
147 profile->numDevices = numDevices + 1; // +1 to numDevices to include the native CPU
148 profile->devices = static_cast<ds_device*>(malloc(profile->numDevices * sizeof(ds_device)));
149 if (profile->devices == NULL)
151 profile->numDevices = 0;
152 status = DS_MEMORY_ERROR;
153 goto cleanup;
155 memset(profile->devices, 0, profile->numDevices * sizeof(ds_device));
157 next = 0;
158 for (i = 0; i < (unsigned int)numPlatforms; i++)
160 cl_uint num = 0;
161 unsigned j;
162 char vendor[256];
163 if (clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor, NULL) != CL_SUCCESS)
164 vendor[0] = '\0';
165 cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices, &num);
166 if (err != CL_SUCCESS)
168 /* we want to catch at least the case when the call returns
169 * CL_DEVICE_NOT_FOUND (i.e. no devices), because some platforms
170 * don't set num to 0 in this case; but in fact this is a good
171 * thing to do for _any_ error returned by the call
173 num = 0;
175 for (j = 0; j < num; j++, next++)
177 char buffer[DS_DEVICE_NAME_LENGTH];
178 size_t length;
180 profile->devices[next].type = DS_DEVICE_OPENCL_DEVICE;
181 profile->devices[next].oclDeviceID = devices[j];
183 profile->devices[next].oclPlatformVendor = strdup(vendor);
185 clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_NAME
186 , DS_DEVICE_NAME_LENGTH, &buffer, NULL);
187 length = strlen(buffer);
188 profile->devices[next].oclDeviceName = static_cast<char*>(malloc(length + 1));
189 memcpy(profile->devices[next].oclDeviceName, buffer, length + 1);
191 clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DRIVER_VERSION
192 , DS_DEVICE_NAME_LENGTH, &buffer, NULL);
193 length = strlen(buffer);
194 profile->devices[next].oclDriverVersion = static_cast<char*>(malloc(length + 1));
195 memcpy(profile->devices[next].oclDriverVersion, buffer, length + 1);
198 profile->devices[next].type = DS_DEVICE_NATIVE_CPU;
199 profile->version = version;
201 cleanup:
202 if (platforms) free(platforms);
203 if (devices) free(devices);
204 if (status == DS_SUCCESS)
206 *p = profile;
208 else
210 if (profile)
212 if (profile->devices) free(profile->devices);
213 free(profile);
216 return status;
219 // Pointer to a function that calculates the score of a device (ex: device->score)
220 // update the data size of score. The encoding and the format of the score data
221 // is implementation defined. The function should return DS_SUCCESS if there's no error to be reported.
222 typedef ds_status(* ds_perf_evaluator)(ds_device* device, void* data);
224 typedef enum {
225 DS_EVALUATE_ALL
226 , DS_EVALUATE_NEW_ONLY
227 } ds_evaluation_type;
229 inline ds_status profileDevices(ds_profile* profile, const ds_evaluation_type type,
230 ds_perf_evaluator evaluator, void* evaluatorData, unsigned int* numUpdates)
232 ds_status status = DS_SUCCESS;
233 unsigned int i;
234 unsigned int updates = 0;
236 if (profile == NULL)
238 return DS_INVALID_PROFILE;
240 if (evaluator == NULL)
242 return DS_INVALID_PERF_EVALUATOR;
245 for (i = 0; i < profile->numDevices; i++)
247 ds_status evaluatorStatus;
249 switch (type)
251 case DS_EVALUATE_NEW_ONLY:
252 if (profile->devices[i].score != NULL) break;
253 // else fall through
254 case DS_EVALUATE_ALL:
255 evaluatorStatus = evaluator(profile->devices + i, evaluatorData);
256 if (evaluatorStatus != DS_SUCCESS)
258 status = evaluatorStatus;
259 return status;
261 updates++;
262 break;
263 default:
264 return DS_INVALID_PERF_EVALUATOR_TYPE;
265 break;
268 if (numUpdates) *numUpdates = updates;
269 return status;
273 #define DS_TAG_VERSION "<version>"
274 #define DS_TAG_VERSION_END "</version>"
275 #define DS_TAG_DEVICE "<device>"
276 #define DS_TAG_DEVICE_END "</device>"
277 #define DS_TAG_SCORE "<score>"
278 #define DS_TAG_SCORE_END "</score>"
279 #define DS_TAG_DEVICE_TYPE "<type>"
280 #define DS_TAG_DEVICE_TYPE_END "</type>"
281 #define DS_TAG_DEVICE_NAME "<name>"
282 #define DS_TAG_DEVICE_NAME_END "</name>"
283 #define DS_TAG_DEVICE_DRIVER_VERSION "<driver>"
284 #define DS_TAG_DEVICE_DRIVER_VERSION_END "</driver>"
286 #define DS_DEVICE_NATIVE_CPU_STRING "native_cpu"
288 typedef ds_status(* ds_score_serializer)(ds_device* device, void** serializedScore, unsigned int* serializedScoreSize);
289 inline ds_status writeProfileToFile(ds_profile* profile, ds_score_serializer serializer, const char* file)
291 ds_status status = DS_SUCCESS;
292 FILE* profileFile = NULL;
295 if (profile == NULL) return DS_INVALID_PROFILE;
297 profileFile = fopen(file, "wb");
298 if (profileFile == NULL)
300 status = DS_FILE_ERROR;
302 else
304 unsigned int i;
306 // write version string
307 fwrite(DS_TAG_VERSION, sizeof(char), strlen(DS_TAG_VERSION), profileFile);
308 fwrite(profile->version, sizeof(char), strlen(profile->version), profileFile);
309 fwrite(DS_TAG_VERSION_END, sizeof(char), strlen(DS_TAG_VERSION_END), profileFile);
310 fwrite("\n", sizeof(char), 1, profileFile);
312 for (i = 0; i < profile->numDevices && status == DS_SUCCESS; i++)
314 void* serializedScore;
315 unsigned int serializedScoreSize;
317 fwrite(DS_TAG_DEVICE, sizeof(char), strlen(DS_TAG_DEVICE), profileFile);
319 fwrite(DS_TAG_DEVICE_TYPE, sizeof(char), strlen(DS_TAG_DEVICE_TYPE), profileFile);
320 fwrite(&profile->devices[i].type, sizeof(ds_device_type), 1, profileFile);
321 fwrite(DS_TAG_DEVICE_TYPE_END, sizeof(char), strlen(DS_TAG_DEVICE_TYPE_END), profileFile);
323 switch (profile->devices[i].type)
325 case DS_DEVICE_NATIVE_CPU:
327 // There's no need to emit a device name for the native CPU device.
329 fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
330 fwrite(DS_DEVICE_NATIVE_CPU_STRING,sizeof(char),strlen(DS_DEVICE_NATIVE_CPU_STRING), profileFile);
331 fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
334 break;
335 case DS_DEVICE_OPENCL_DEVICE:
337 fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
338 fwrite(profile->devices[i].oclDeviceName, sizeof(char), strlen(profile->devices[i].oclDeviceName), profileFile);
339 fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
341 fwrite(DS_TAG_DEVICE_DRIVER_VERSION, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION), profileFile);
342 fwrite(profile->devices[i].oclDriverVersion, sizeof(char), strlen(profile->devices[i].oclDriverVersion), profileFile);
343 fwrite(DS_TAG_DEVICE_DRIVER_VERSION_END, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION_END), profileFile);
345 break;
346 default:
347 break;
350 fwrite(DS_TAG_SCORE, sizeof(char), strlen(DS_TAG_SCORE), profileFile);
351 status = serializer(profile->devices + i, &serializedScore, &serializedScoreSize);
352 if (status == DS_SUCCESS && serializedScore != NULL && serializedScoreSize > 0)
354 fwrite(serializedScore, sizeof(char), serializedScoreSize, profileFile);
355 free(serializedScore);
357 fwrite(DS_TAG_SCORE_END, sizeof(char), strlen(DS_TAG_SCORE_END), profileFile);
358 fwrite(DS_TAG_DEVICE_END, sizeof(char), strlen(DS_TAG_DEVICE_END), profileFile);
359 fwrite("\n", sizeof(char), 1, profileFile);
361 fclose(profileFile);
363 return status;
367 inline ds_status readProFile(const char* fileName, char** content, size_t* contentSize)
369 FILE* input = NULL;
370 size_t size = 0;
371 char* binary = NULL;
372 long pos = -1;
374 *contentSize = 0;
375 *content = NULL;
377 input = fopen(fileName, "rb");
378 if (input == NULL)
380 return DS_FILE_ERROR;
383 fseek(input, 0L, SEEK_END);
384 pos = ftell(input);
385 if (pos < 0)
387 fclose(input);
388 return DS_FILE_ERROR;
391 size = pos;
392 rewind(input);
393 binary = static_cast<char*>(malloc(size));
394 if (binary == NULL)
396 fclose(input);
397 return DS_FILE_ERROR;
399 size_t bytesRead = fread(binary, sizeof(char), size, input);
400 (void) bytesRead; // avoid warning
401 fclose(input);
403 *contentSize = size;
404 *content = binary;
405 return DS_SUCCESS;
409 inline const char* findString(const char* contentStart, const char* contentEnd, const char* string)
411 size_t stringLength;
412 const char* currentPosition;
413 const char* found;
414 found = NULL;
415 stringLength = strlen(string);
416 currentPosition = contentStart;
417 for (currentPosition = contentStart; currentPosition < contentEnd; currentPosition++)
419 if (*currentPosition == string[0])
421 if (currentPosition + stringLength < contentEnd)
423 if (strncmp(currentPosition, string, stringLength) == 0)
425 found = currentPosition;
426 break;
431 return found;
435 typedef ds_status(* ds_score_deserializer)(ds_device* device, const unsigned char* serializedScore, unsigned int serializedScoreSize);
436 inline ds_status readProfileFromFile(ds_profile* profile, ds_score_deserializer deserializer, const char* file)
439 ds_status status = DS_SUCCESS;
440 char* contentStart = NULL;
441 const char* contentEnd = NULL;
442 size_t contentSize;
444 if (profile == NULL) return DS_INVALID_PROFILE;
446 status = readProFile(file, &contentStart, &contentSize);
447 if (status == DS_SUCCESS)
449 const char* currentPosition;
450 const char* dataStart;
451 const char* dataEnd;
452 size_t versionStringLength;
454 contentEnd = contentStart + contentSize;
455 currentPosition = contentStart;
458 // parse the version string
459 dataStart = findString(currentPosition, contentEnd, DS_TAG_VERSION);
460 if (dataStart == NULL)
462 status = DS_PROFILE_FILE_ERROR;
463 goto cleanup;
465 dataStart += strlen(DS_TAG_VERSION);
467 dataEnd = findString(dataStart, contentEnd, DS_TAG_VERSION_END);
468 if (dataEnd == NULL)
470 status = DS_PROFILE_FILE_ERROR;
471 goto cleanup;
474 versionStringLength = strlen(profile->version);
475 if (versionStringLength != static_cast<size_t>(dataEnd - dataStart)
476 || strncmp(profile->version, dataStart, versionStringLength) != 0)
478 // version mismatch
479 status = DS_PROFILE_FILE_ERROR;
480 goto cleanup;
482 currentPosition = dataEnd + strlen(DS_TAG_VERSION_END);
484 // parse the device information
485 while (true)
487 unsigned int i;
489 const char* deviceTypeStart;
490 const char* deviceTypeEnd;
491 ds_device_type deviceType;
493 const char* deviceNameStart;
494 const char* deviceNameEnd;
496 const char* deviceScoreStart;
497 const char* deviceScoreEnd;
499 const char* deviceDriverStart;
500 const char* deviceDriverEnd;
502 dataStart = findString(currentPosition, contentEnd, DS_TAG_DEVICE);
503 if (dataStart == NULL)
505 // nothing useful remain, quit...
506 break;
508 dataStart += strlen(DS_TAG_DEVICE);
509 dataEnd = findString(dataStart, contentEnd, DS_TAG_DEVICE_END);
510 if (dataEnd == NULL)
512 status = DS_PROFILE_FILE_ERROR;
513 goto cleanup;
516 // parse the device type
517 deviceTypeStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_TYPE);
518 if (deviceTypeStart == NULL)
520 status = DS_PROFILE_FILE_ERROR;
521 goto cleanup;
523 deviceTypeStart += strlen(DS_TAG_DEVICE_TYPE);
524 deviceTypeEnd = findString(deviceTypeStart, contentEnd, DS_TAG_DEVICE_TYPE_END);
525 if (deviceTypeEnd == NULL)
527 status = DS_PROFILE_FILE_ERROR;
528 goto cleanup;
530 memcpy(&deviceType, deviceTypeStart, sizeof(ds_device_type));
533 // parse the device name
534 if (deviceType == DS_DEVICE_OPENCL_DEVICE)
537 deviceNameStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_NAME);
538 if (deviceNameStart == NULL)
540 status = DS_PROFILE_FILE_ERROR;
541 goto cleanup;
543 deviceNameStart += strlen(DS_TAG_DEVICE_NAME);
544 deviceNameEnd = findString(deviceNameStart, contentEnd, DS_TAG_DEVICE_NAME_END);
545 if (deviceNameEnd == NULL)
547 status = DS_PROFILE_FILE_ERROR;
548 goto cleanup;
552 deviceDriverStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION);
553 if (deviceDriverStart == NULL)
555 status = DS_PROFILE_FILE_ERROR;
556 goto cleanup;
558 deviceDriverStart += strlen(DS_TAG_DEVICE_DRIVER_VERSION);
559 deviceDriverEnd = findString(deviceDriverStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION_END);
560 if (deviceDriverEnd == NULL)
562 status = DS_PROFILE_FILE_ERROR;
563 goto cleanup;
567 // check if this device is on the system
568 for (i = 0; i < profile->numDevices; i++)
570 if (profile->devices[i].type == DS_DEVICE_OPENCL_DEVICE)
572 size_t actualDeviceNameLength;
573 size_t driverVersionLength;
575 actualDeviceNameLength = strlen(profile->devices[i].oclDeviceName);
576 driverVersionLength = strlen(profile->devices[i].oclDriverVersion);
577 if (actualDeviceNameLength == static_cast<size_t>(deviceNameEnd - deviceNameStart)
578 && driverVersionLength == static_cast<size_t>(deviceDriverEnd - deviceDriverStart)
579 && strncmp(profile->devices[i].oclDeviceName, deviceNameStart, actualDeviceNameLength) == 0
580 && strncmp(profile->devices[i].oclDriverVersion, deviceDriverStart, driverVersionLength) == 0)
583 deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
584 if (deviceScoreStart == NULL)
586 status = DS_PROFILE_FILE_ERROR;
587 goto cleanup;
589 deviceScoreStart += strlen(DS_TAG_SCORE);
590 deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
591 status = deserializer(profile->devices + i, reinterpret_cast<const unsigned char*>(deviceScoreStart), deviceScoreEnd - deviceScoreStart);
592 if (status != DS_SUCCESS)
594 goto cleanup;
601 else if (deviceType == DS_DEVICE_NATIVE_CPU)
603 for (i = 0; i < profile->numDevices; i++)
605 if (profile->devices[i].type == DS_DEVICE_NATIVE_CPU)
607 deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
608 if (deviceScoreStart == NULL)
610 status = DS_PROFILE_FILE_ERROR;
611 goto cleanup;
613 deviceScoreStart += strlen(DS_TAG_SCORE);
614 deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
615 status = deserializer(profile->devices + i, reinterpret_cast<const unsigned char*>(deviceScoreStart), deviceScoreEnd - deviceScoreStart);
616 if (status != DS_SUCCESS)
618 goto cleanup;
624 // skip over the current one to find the next device
625 currentPosition = dataEnd + strlen(DS_TAG_DEVICE_END);
628 cleanup:
629 if (contentStart != NULL) free(contentStart);
630 if (status != DS_SUCCESS)
631 return status;
633 // Check that all the devices present had valid cached scores. If
634 // not, return DS_INVALID_PROFILE and let the caller re-evaluate
635 // scores for present devices, and write a new profile file.
636 for (unsigned int i = 0; i < profile->numDevices; i++)
637 if (profile->devices[i].score == NULL)
638 return DS_INVALID_PROFILE;
640 return DS_SUCCESS;
643 inline ds_status getNumDeviceWithEmptyScore(ds_profile* profile, unsigned int* num)
645 unsigned int i;
646 if (profile == NULL || num == NULL) return DS_MEMORY_ERROR;
647 *num = 0;
648 for (i = 0; i < profile->numDevices; i++)
650 if (profile->devices[i].score == NULL)
652 (*num)++;
655 return DS_SUCCESS;
658 #endif
660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */