1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
11 #define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
14 #define _CRT_SECURE_NO_WARNINGS
21 #include <clew/clew.h>
23 #define DS_DEVICE_NAME_LENGTH 256
28 ,DS_INVALID_PROFILE
= 1000
30 , DS_INVALID_PERF_EVALUATOR_TYPE
31 , DS_INVALID_PERF_EVALUATOR
32 , DS_PERF_EVALUATOR_ERROR
34 , DS_UNKNOWN_DEVICE_TYPE
35 , DS_PROFILE_FILE_ERROR
36 , DS_SCORE_SERIALIZER_ERROR
37 , DS_SCORE_DESERIALIZER_ERROR
43 DS_DEVICE_NATIVE_CPU
= 0
44 ,DS_DEVICE_OPENCL_DEVICE
51 cl_device_id oclDeviceID
;
52 char* oclPlatformVendor
;
54 char* oclDriverVersion
;
55 void* score
; // a pointer to the score data, the content/format is application defined
60 unsigned int numDevices
;
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
;
72 if (profile
->devices
!= NULL
&& sr
!= NULL
)
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
);
91 inline ds_status
initDSProfile(ds_profile
** p
, const char* version
)
95 cl_platform_id
* platforms
= NULL
;
96 cl_device_id
* devices
= NULL
;
97 ds_status status
= DS_SUCCESS
;
98 ds_profile
* profile
= NULL
;
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
;
118 clGetPlatformIDs(numPlatforms
, platforms
, NULL
);
122 for (i
= 0; i
< (unsigned int)numPlatforms
; i
++)
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
139 devices
= static_cast<cl_device_id
*>(malloc(numDevices
* sizeof(cl_device_id
)));
142 status
= DS_MEMORY_ERROR
;
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
;
155 memset(profile
->devices
, 0, profile
->numDevices
* sizeof(ds_device
));
158 for (i
= 0; i
< (unsigned int)numPlatforms
; i
++)
163 if (clGetPlatformInfo(platforms
[i
], CL_PLATFORM_VENDOR
, sizeof(vendor
), vendor
, NULL
) != CL_SUCCESS
)
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
175 for (j
= 0; j
< num
; j
++, next
++)
177 char buffer
[DS_DEVICE_NAME_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
;
202 if (platforms
) free(platforms
);
203 if (devices
) free(devices
);
204 if (status
== DS_SUCCESS
)
212 if (profile
->devices
) free(profile
->devices
);
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
);
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
;
234 unsigned int updates
= 0;
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
;
251 case DS_EVALUATE_NEW_ONLY
:
252 if (profile
->devices
[i
].score
!= NULL
) break;
254 case DS_EVALUATE_ALL
:
255 evaluatorStatus
= evaluator(profile
->devices
+ i
, evaluatorData
);
256 if (evaluatorStatus
!= DS_SUCCESS
)
258 status
= evaluatorStatus
;
264 return DS_INVALID_PERF_EVALUATOR_TYPE
;
268 if (numUpdates
) *numUpdates
= updates
;
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
;
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);
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
);
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
);
367 inline ds_status
readProFile(const char* fileName
, char** content
, size_t* contentSize
)
377 input
= fopen(fileName
, "rb");
380 return DS_FILE_ERROR
;
383 fseek(input
, 0L, SEEK_END
);
388 return DS_FILE_ERROR
;
393 binary
= static_cast<char*>(malloc(size
));
397 return DS_FILE_ERROR
;
399 size_t bytesRead
= fread(binary
, sizeof(char), size
, input
);
400 (void) bytesRead
; // avoid warning
409 inline const char* findString(const char* contentStart
, const char* contentEnd
, const char* string
)
412 const char* currentPosition
;
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
;
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
;
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
;
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
;
465 dataStart
+= strlen(DS_TAG_VERSION
);
467 dataEnd
= findString(dataStart
, contentEnd
, DS_TAG_VERSION_END
);
470 status
= DS_PROFILE_FILE_ERROR
;
474 versionStringLength
= strlen(profile
->version
);
475 if (versionStringLength
!= static_cast<size_t>(dataEnd
- dataStart
)
476 || strncmp(profile
->version
, dataStart
, versionStringLength
) != 0)
479 status
= DS_PROFILE_FILE_ERROR
;
482 currentPosition
= dataEnd
+ strlen(DS_TAG_VERSION_END
);
484 // parse the device information
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...
508 dataStart
+= strlen(DS_TAG_DEVICE
);
509 dataEnd
= findString(dataStart
, contentEnd
, DS_TAG_DEVICE_END
);
512 status
= DS_PROFILE_FILE_ERROR
;
516 // parse the device type
517 deviceTypeStart
= findString(dataStart
, contentEnd
, DS_TAG_DEVICE_TYPE
);
518 if (deviceTypeStart
== NULL
)
520 status
= DS_PROFILE_FILE_ERROR
;
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
;
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
;
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
;
552 deviceDriverStart
= findString(dataStart
, contentEnd
, DS_TAG_DEVICE_DRIVER_VERSION
);
553 if (deviceDriverStart
== NULL
)
555 status
= DS_PROFILE_FILE_ERROR
;
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
;
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
;
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
)
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
;
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
)
624 // skip over the current one to find the next device
625 currentPosition
= dataEnd
+ strlen(DS_TAG_DEVICE_END
);
629 if (contentStart
!= NULL
) free(contentStart
);
630 if (status
!= DS_SUCCESS
)
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
;
643 inline ds_status
getNumDeviceWithEmptyScore(ds_profile
* profile
, unsigned int* num
)
646 if (profile
== NULL
|| num
== NULL
) return DS_MEMORY_ERROR
;
648 for (i
= 0; i
< profile
->numDevices
; i
++)
650 if (profile
->devices
[i
].score
== NULL
)
660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */