/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef SC_DEVICE_SELECTION_H #define SC_DEVICE_SELECTION_H #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include "clcc/clew.h" #define DS_DEVICE_NAME_LENGTH 256 enum ds_status { DS_SUCCESS = 0 ,DS_INVALID_PROFILE = 1000 ,DS_MEMORY_ERROR , DS_INVALID_PERF_EVALUATOR_TYPE , DS_INVALID_PERF_EVALUATOR , DS_PERF_EVALUATOR_ERROR , DS_FILE_ERROR , DS_UNKNOWN_DEVICE_TYPE , DS_PROFILE_FILE_ERROR , DS_SCORE_SERIALIZER_ERROR , DS_SCORE_DESERIALIZER_ERROR }; // device type enum ds_device_type { DS_DEVICE_NATIVE_CPU = 0 ,DS_DEVICE_OPENCL_DEVICE }; struct ds_device { ds_device_type type; cl_device_id oclDeviceID; char* oclDeviceName; char* oclDriverVersion; void* score; // a pointer to the score data, the content/format is application defined }; struct ds_profile { unsigned int numDevices; ds_device* devices; const char* version; }; // deallocate memory used by score typedef ds_status(* ds_score_release)(void* score); inline ds_status releaseDSProfile(ds_profile* profile, ds_score_release sr) { ds_status status = DS_SUCCESS; if (profile != NULL) { if (profile->devices != NULL && sr != NULL) { unsigned int i; for (i = 0; i < profile->numDevices; i++) { free(profile->devices[i].oclDeviceName); free(profile->devices[i].oclDriverVersion); status = sr(profile->devices[i].score); if (status != DS_SUCCESS) break; } free(profile->devices); } free(profile); } return status; } inline ds_status initDSProfile(ds_profile** p, const char* version) { int numDevices; cl_uint numPlatforms; cl_platform_id* platforms = NULL; cl_device_id* devices = NULL; ds_status status = DS_SUCCESS; ds_profile* profile = NULL; unsigned int next; unsigned int i; if (p == NULL) return DS_INVALID_PROFILE; profile = (ds_profile*)malloc(sizeof(ds_profile)); if (profile == NULL) return DS_MEMORY_ERROR; memset(profile, 0, sizeof(ds_profile)); clGetPlatformIDs(0, NULL, &numPlatforms); if (numPlatforms != 0) { platforms = (cl_platform_id*)malloc(numPlatforms * sizeof(cl_platform_id)); if (platforms == NULL) { status = DS_MEMORY_ERROR; goto cleanup; } clGetPlatformIDs(numPlatforms, platforms, NULL); } numDevices = 0; for (i = 0; i < (unsigned int)numPlatforms; i++) { cl_uint num; clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, NULL, &num); numDevices += num; } if (numDevices != 0) { devices = (cl_device_id*)malloc(numDevices * sizeof(cl_device_id)); if (devices == NULL) { status = DS_MEMORY_ERROR; goto cleanup; } } profile->numDevices = numDevices + 1; // +1 to numDevices to include the native CPU profile->devices = (ds_device*)malloc(profile->numDevices * sizeof(ds_device)); if (profile->devices == NULL) { profile->numDevices = 0; status = DS_MEMORY_ERROR; goto cleanup; } memset(profile->devices, 0, profile->numDevices * sizeof(ds_device)); next = 0; for (i = 0; i < (unsigned int)numPlatforms; i++) { cl_uint num; unsigned j; clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices, &num); for (j = 0; j < num; j++, next++) { char buffer[DS_DEVICE_NAME_LENGTH]; size_t length; profile->devices[next].type = DS_DEVICE_OPENCL_DEVICE; profile->devices[next].oclDeviceID = devices[j]; clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_NAME , DS_DEVICE_NAME_LENGTH, &buffer, NULL); length = strlen(buffer); profile->devices[next].oclDeviceName = (char*)malloc(length + 1); memcpy(profile->devices[next].oclDeviceName, buffer, length + 1); clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DRIVER_VERSION , DS_DEVICE_NAME_LENGTH, &buffer, NULL); length = strlen(buffer); profile->devices[next].oclDriverVersion = (char*)malloc(length + 1); memcpy(profile->devices[next].oclDriverVersion, buffer, length + 1); } } profile->devices[next].type = DS_DEVICE_NATIVE_CPU; profile->version = version; cleanup: if (platforms) free(platforms); if (devices) free(devices); if (status == DS_SUCCESS) { *p = profile; } else { if (profile) { if (profile->devices) free(profile->devices); free(profile); } } return status; } // Pointer to a function that calculates the score of a device (ex: device->score) // update the data size of score. The encoding and the format of the score data // is implementation defined. The function should return DS_SUCCESS if there's no error to be reported. typedef ds_status(* ds_perf_evaluator)(ds_device* device, void* data); typedef enum { DS_EVALUATE_ALL , DS_EVALUATE_NEW_ONLY } ds_evaluation_type; inline ds_status profileDevices(ds_profile* profile, const ds_evaluation_type type, ds_perf_evaluator evaluator, void* evaluatorData, unsigned int* numUpdates) { ds_status status = DS_SUCCESS; unsigned int i; unsigned int updates = 0; if (profile == NULL) { return DS_INVALID_PROFILE; } if (evaluator == NULL) { return DS_INVALID_PERF_EVALUATOR; } for (i = 0; i < profile->numDevices; i++) { ds_status evaluatorStatus; switch (type) { case DS_EVALUATE_NEW_ONLY: if (profile->devices[i].score != NULL) break; // else fall through case DS_EVALUATE_ALL: evaluatorStatus = evaluator(profile->devices + i, evaluatorData); if (evaluatorStatus != DS_SUCCESS) { status = evaluatorStatus; return status; } updates++; break; default: return DS_INVALID_PERF_EVALUATOR_TYPE; break; }; } if (numUpdates) *numUpdates = updates; return status; } #define DS_TAG_VERSION "" #define DS_TAG_VERSION_END "" #define DS_TAG_DEVICE "" #define DS_TAG_DEVICE_END "" #define DS_TAG_SCORE "" #define DS_TAG_SCORE_END "" #define DS_TAG_DEVICE_TYPE "" #define DS_TAG_DEVICE_TYPE_END "" #define DS_TAG_DEVICE_NAME "" #define DS_TAG_DEVICE_NAME_END "" #define DS_TAG_DEVICE_DRIVER_VERSION "" #define DS_TAG_DEVICE_DRIVER_VERSION_END "" #define DS_DEVICE_NATIVE_CPU_STRING "native_cpu" typedef ds_status(* ds_score_serializer)(ds_device* device, void** serializedScore, unsigned int* serializedScoreSize); inline ds_status writeProfileToFile(ds_profile* profile, ds_score_serializer serializer, const char* file) { ds_status status = DS_SUCCESS; FILE* profileFile = NULL; if (profile == NULL) return DS_INVALID_PROFILE; profileFile = fopen(file, "wb"); if (profileFile == NULL) { status = DS_FILE_ERROR; } else { unsigned int i; // write version string fwrite(DS_TAG_VERSION, sizeof(char), strlen(DS_TAG_VERSION), profileFile); fwrite(profile->version, sizeof(char), strlen(profile->version), profileFile); fwrite(DS_TAG_VERSION_END, sizeof(char), strlen(DS_TAG_VERSION_END), profileFile); fwrite("\n", sizeof(char), 1, profileFile); for (i = 0; i < profile->numDevices && status == DS_SUCCESS; i++) { void* serializedScore; unsigned int serializedScoreSize; fwrite(DS_TAG_DEVICE, sizeof(char), strlen(DS_TAG_DEVICE), profileFile); fwrite(DS_TAG_DEVICE_TYPE, sizeof(char), strlen(DS_TAG_DEVICE_TYPE), profileFile); fwrite(&profile->devices[i].type, sizeof(ds_device_type), 1, profileFile); fwrite(DS_TAG_DEVICE_TYPE_END, sizeof(char), strlen(DS_TAG_DEVICE_TYPE_END), profileFile); switch (profile->devices[i].type) { case DS_DEVICE_NATIVE_CPU: { // There's no need to emit a device name for the native CPU device. /* fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile); fwrite(DS_DEVICE_NATIVE_CPU_STRING,sizeof(char),strlen(DS_DEVICE_NATIVE_CPU_STRING), profileFile); fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile); */ } break; case DS_DEVICE_OPENCL_DEVICE: { fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile); fwrite(profile->devices[i].oclDeviceName, sizeof(char), strlen(profile->devices[i].oclDeviceName), profileFile); fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile); fwrite(DS_TAG_DEVICE_DRIVER_VERSION, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION), profileFile); fwrite(profile->devices[i].oclDriverVersion, sizeof(char), strlen(profile->devices[i].oclDriverVersion), profileFile); fwrite(DS_TAG_DEVICE_DRIVER_VERSION_END, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION_END), profileFile); } break; default: status = DS_UNKNOWN_DEVICE_TYPE; break; }; fwrite(DS_TAG_SCORE, sizeof(char), strlen(DS_TAG_SCORE), profileFile); status = serializer(profile->devices + i, &serializedScore, &serializedScoreSize); if (status == DS_SUCCESS && serializedScore != NULL && serializedScoreSize > 0) { fwrite(serializedScore, sizeof(char), serializedScoreSize, profileFile); free(serializedScore); } fwrite(DS_TAG_SCORE_END, sizeof(char), strlen(DS_TAG_SCORE_END), profileFile); fwrite(DS_TAG_DEVICE_END, sizeof(char), strlen(DS_TAG_DEVICE_END), profileFile); fwrite("\n", sizeof(char), 1, profileFile); } fclose(profileFile); } return status; } inline ds_status readProFile(const char* fileName, char** content, size_t* contentSize) { FILE* input = NULL; size_t size = 0; char* binary = NULL; *contentSize = 0; *content = NULL; input = fopen(fileName, "rb"); if (input == NULL) { return DS_FILE_ERROR; } fseek(input, 0L, SEEK_END); size = ftell(input); rewind(input); binary = (char*)malloc(size); if (binary == NULL) { return DS_FILE_ERROR; } fread(binary, sizeof(char), size, input); fclose(input); *contentSize = size; *content = binary; return DS_SUCCESS; } inline const char* findString(const char* contentStart, const char* contentEnd, const char* string) { size_t stringLength; const char* currentPosition; const char* found; found = NULL; stringLength = strlen(string); currentPosition = contentStart; for (currentPosition = contentStart; currentPosition < contentEnd; currentPosition++) { if (*currentPosition == string[0]) { if (currentPosition + stringLength < contentEnd) { if (strncmp(currentPosition, string, stringLength) == 0) { found = currentPosition; break; } } } } return found; } typedef ds_status(* ds_score_deserializer)(ds_device* device, const unsigned char* serializedScore, unsigned int serializedScoreSize); inline ds_status readProfileFromFile(ds_profile* profile, ds_score_deserializer deserializer, const char* file) { ds_status status = DS_SUCCESS; char* contentStart = NULL; const char* contentEnd = NULL; size_t contentSize; if (profile == NULL) return DS_INVALID_PROFILE; status = readProFile(file, &contentStart, &contentSize); if (status == DS_SUCCESS) { const char* currentPosition; const char* dataStart; const char* dataEnd; size_t versionStringLength; contentEnd = contentStart + contentSize; currentPosition = contentStart; // parse the version string dataStart = findString(currentPosition, contentEnd, DS_TAG_VERSION); if (dataStart == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } dataStart += strlen(DS_TAG_VERSION); dataEnd = findString(dataStart, contentEnd, DS_TAG_VERSION_END); if (dataEnd == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } versionStringLength = strlen(profile->version); if (versionStringLength != static_cast(dataEnd - dataStart) || strncmp(profile->version, dataStart, versionStringLength) != 0) { // version mismatch status = DS_PROFILE_FILE_ERROR; goto cleanup; } currentPosition = dataEnd + strlen(DS_TAG_VERSION_END); // parse the device information while (1) { unsigned int i; const char* deviceTypeStart; const char* deviceTypeEnd; ds_device_type deviceType; const char* deviceNameStart; const char* deviceNameEnd; const char* deviceScoreStart; const char* deviceScoreEnd; const char* deviceDriverStart; const char* deviceDriverEnd; dataStart = findString(currentPosition, contentEnd, DS_TAG_DEVICE); if (dataStart == NULL) { // nothing useful remain, quit... break; } dataStart += strlen(DS_TAG_DEVICE); dataEnd = findString(dataStart, contentEnd, DS_TAG_DEVICE_END); if (dataEnd == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } // parse the device type deviceTypeStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_TYPE); if (deviceTypeStart == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } deviceTypeStart += strlen(DS_TAG_DEVICE_TYPE); deviceTypeEnd = findString(deviceTypeStart, contentEnd, DS_TAG_DEVICE_TYPE_END); if (deviceTypeEnd == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } memcpy(&deviceType, deviceTypeStart, sizeof(ds_device_type)); // parse the device name if (deviceType == DS_DEVICE_OPENCL_DEVICE) { deviceNameStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_NAME); if (deviceNameStart == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } deviceNameStart += strlen(DS_TAG_DEVICE_NAME); deviceNameEnd = findString(deviceNameStart, contentEnd, DS_TAG_DEVICE_NAME_END); if (deviceNameEnd == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } deviceDriverStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION); if (deviceDriverStart == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } deviceDriverStart += strlen(DS_TAG_DEVICE_DRIVER_VERSION); deviceDriverEnd = findString(deviceDriverStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION_END); if (deviceDriverEnd == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } // check if this device is on the system for (i = 0; i < profile->numDevices; i++) { if (profile->devices[i].type == DS_DEVICE_OPENCL_DEVICE) { size_t actualDeviceNameLength; size_t driverVersionLength; actualDeviceNameLength = strlen(profile->devices[i].oclDeviceName); driverVersionLength = strlen(profile->devices[i].oclDriverVersion); if (actualDeviceNameLength == static_cast(deviceNameEnd - deviceNameStart) && driverVersionLength == static_cast(deviceDriverEnd - deviceDriverStart) && strncmp(profile->devices[i].oclDeviceName, deviceNameStart, actualDeviceNameLength) == 0 && strncmp(profile->devices[i].oclDriverVersion, deviceDriverStart, driverVersionLength) == 0) { deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE); if (deviceNameStart == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } deviceScoreStart += strlen(DS_TAG_SCORE); deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END); status = deserializer(profile->devices + i, (const unsigned char*)deviceScoreStart, deviceScoreEnd - deviceScoreStart); if (status != DS_SUCCESS) { goto cleanup; } } } } } else if (deviceType == DS_DEVICE_NATIVE_CPU) { for (i = 0; i < profile->numDevices; i++) { if (profile->devices[i].type == DS_DEVICE_NATIVE_CPU) { deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE); if (deviceScoreStart == NULL) { status = DS_PROFILE_FILE_ERROR; goto cleanup; } deviceScoreStart += strlen(DS_TAG_SCORE); deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END); status = deserializer(profile->devices + i, (const unsigned char*)deviceScoreStart, deviceScoreEnd - deviceScoreStart); if (status != DS_SUCCESS) { goto cleanup; } } } } // skip over the current one to find the next device currentPosition = dataEnd + strlen(DS_TAG_DEVICE_END); } } cleanup: if (contentStart != NULL) free(contentStart); return status; } inline ds_status getNumDeviceWithEmptyScore(ds_profile* profile, unsigned int* num) { unsigned int i; if (profile == NULL || num == NULL) return DS_MEMORY_ERROR; *num = 0; for (i = 0; i < profile->numDevices; i++) { if (profile->devices[i].score == NULL) { (*num)++; } } return DS_SUCCESS; } #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */