From 87c18d6f4cb9bcb8c4c9e2c80e4d779f01675ff6 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Thu, 27 Nov 2014 15:13:12 +0200 Subject: Move more Calc-independent OpenCL stuff from the sc to the opencl module No cleanups yet. Just removed the "sc" namespace parts now when this stuff is no longer Calc-specific. There is still horribly confusing use of the same OpenCLDevice name for both a class and as a namespace, for instance. And the OpenCLDevice class has only public static members even, so effectively it acts as just a namespace anyway... Etc. Change-Id: Idc5f30a721df0101426c676f04a85e02c5dc8443 --- opencl/inc/opencl_device.hxx | 25 ++ opencl/inc/opencl_device_selection.h | 641 +++++++++++++++++++++++++++++++++++ 2 files changed, 666 insertions(+) create mode 100644 opencl/inc/opencl_device.hxx create mode 100644 opencl/inc/opencl_device_selection.h (limited to 'opencl/inc') diff --git a/opencl/inc/opencl_device.hxx b/opencl/inc/opencl_device.hxx new file mode 100644 index 000000000000..7435879ecc83 --- /dev/null +++ b/opencl/inc/opencl_device.hxx @@ -0,0 +1,25 @@ +/* -*- 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 INCLUDED_OPENCL_INC_OPENCL_DEVICE_HXX +#define INCLUDED_OPENCL_INC_OPENCL_DEVICE_HXX + +#include "opencl_device_selection.h" + +namespace OpenCLDevice { + +ds_device getDeviceSelection(const char* pFileName, bool bForceSelection = false); +bool selectedDeviceIsOpenCL(ds_device device); +bool selectedDeviceIsNativeCPU(ds_device device); + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/opencl/inc/opencl_device_selection.h b/opencl/inc/opencl_device_selection.h new file mode 100644 index 000000000000..03373f4a3d0f --- /dev/null +++ b/opencl/inc/opencl_device_selection.h @@ -0,0 +1,641 @@ +/* -*- 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 INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H +#define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include + +#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* oclPlatformVendor; + 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].oclPlatformVendor); + 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; + char vendor[256]; + if (clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor, NULL) != CL_SUCCESS) + vendor[0] = '\0'; + 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]; + + profile->devices[next].oclPlatformVendor = strdup(vendor); + + 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: + 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; + long pos = -1; + + *contentSize = 0; + *content = NULL; + + input = fopen(fileName, "rb"); + if (input == NULL) + { + return DS_FILE_ERROR; + } + + fseek(input, 0L, SEEK_END); + pos = ftell(input); + if (pos < 0) + { + fclose(input); + return DS_FILE_ERROR; + } + + size = pos; + rewind(input); + binary = (char*)malloc(size); + if (binary == NULL) + { + fclose(input); + return DS_FILE_ERROR; + } + size_t bytesRead = fread(binary, sizeof(char), size, input); + (void) bytesRead; // avoid warning + 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 (true) + { + 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 (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; + } + } + } + } + + } + 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); + if (status != DS_SUCCESS) + return status; + + // Check that all the devices present had valid cached scores. If + // not, return DS_INVALID_PROFILE and let the caller re-evaluate + // scores for present devices, and write a new profile file. + for (unsigned int i = 0; i < profile->numDevices; i++) + if (profile->devices[i].score == NULL) + return DS_INVALID_PROFILE; + + return DS_SUCCESS; +} + +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: */ -- cgit