// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2011 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . // BOINC API for OpenCL // // To get the cl_device_id and cl_platform_id for the OpenCL GPU // assigned to your application call this function: // int boinc_get_opencl_ids(int argc, char** argv, char *type, cl_device_id* device, cl_platform_id* platform); // // To use this function, link your application with libboinc_opencl.a // #if defined(_WIN32) && !defined(__STDWX_H__) && !defined(_BOINC_WIN_) && !defined(_AFX_STDAFX_H_) #include "boinc_win.h" #endif #ifdef _WIN32 #include "win_util.h" #else #include #endif #include "error_numbers.h" #include "coproc.h" #include "str_replace.h" #include "boinc_api.h" #include "boinc_opencl.h" static int compareBOINCVersionTo(int toMajor, int toMinor, int toRelease); // A few complicating factors: // Windows & Linux have a separate OpenCL platform for each vendor // (NVIDIA, AMD, Intel). // Mac has only one platform (Apple) which reports GPUs from all vendors. // // In all systems, opencl_device_indexes start at 0 for each platform // and device_nums start at 0 for each vendor. // // On Macs, OpenCL does not always recognize all GPU models detected by // CUDA, so a device_num may not correspond to its opencl_device_index // even if all GPUs are from NVIDIA. // int get_vendor(cl_device_id device_id, char* vendor, int len) { int retval = 0; retval = clGetDeviceInfo( device_id, CL_DEVICE_VENDOR, len, vendor, NULL ); if (retval != CL_SUCCESS) return retval; if (!strlen(vendor)) return CL_INVALID_DEVICE_TYPE; if ((strstr(vendor, "AMD")) || (strstr(vendor, "Advanced Micro Devices, Inc.")) ) { strlcpy(vendor, GPU_TYPE_ATI, len); // "ATI" } if (strcasestr(vendor, "nvidia")) { strlcpy(vendor, GPU_TYPE_NVIDIA, len); // "NVIDIA" } if (strcasestr(vendor, "intel")) { strlcpy(vendor, GPU_TYPE_INTEL, len); // "intel_gpu" } return 0; } // returns an OpenCL error num or zero // int boinc_get_opencl_ids_aux( char* type, int opencl_device_index, int device_num, cl_device_id* device, cl_platform_id* platform ) { cl_platform_id platforms[MAX_OPENCL_PLATFORMS]; cl_uint num_platforms, platform_index, num_devices; cl_device_id devices[MAX_COPROC_INSTANCES]; char vendor[256]; // Device vendor (NVIDIA, ATI, AMD, etc.) int retval = 0; cl_device_id device_id; int device_num_for_type = -1; int device_index; if ((!type) || (!strlen(type))) return CL_INVALID_DEVICE_TYPE; retval = clGetPlatformIDs(MAX_OPENCL_PLATFORMS, platforms, &num_platforms); if (num_platforms == 0) return CL_DEVICE_NOT_FOUND; if (retval != CL_SUCCESS) return retval; for (platform_index=0; platform_index= 0) { if (opencl_device_index < (int)num_devices) { device_id = devices[opencl_device_index]; retval = get_vendor(device_id, vendor, sizeof(vendor)); if (retval != CL_SUCCESS) continue; if (!strcmp(vendor, type)) { *device = device_id; *platform = platforms[platform_index]; return 0; } } continue; } // Older versions of init_data.xml don't have the gpu_opencl_dev_index // field so use the value of gpu_device_num. // NOTE: This may return the wrong device on older versions of BOINC if // OpenCL does not recognize all GPU models detected by CUDA or CAL for (device_index=0; device_index<(int)num_devices; ++device_index) { device_id = devices[device_index]; retval = get_vendor(device_id, vendor, sizeof(vendor)); if (retval != CL_SUCCESS) continue; if (!strcmp(vendor, type)) { if(++device_num_for_type == device_num) { *device = device_id; *platform = platforms[platform_index]; return 0; } } } } fprintf(stderr, "GPU not found: type=%s, opencl_device_index=%d, device_num=%d\n", type, opencl_device_index, device_num); return CL_DEVICE_NOT_FOUND; } // This version is compatible with older clients. // Usage: // Pass the argc and argv received from the BOINC client // type: may be PROC_TYPE_NVIDIA_GPU, PROC_TYPE_AMD_GPU or PROC_TYPE_INTEL_GPU // (it may also be 0, but then it will fail on older clients.) // // The argc, argv and type arguments are ignored for 6.13.3 or later clients. // // returns // - 0 if success // - ERR_FOPEN if init_data.xml missing // - ERR_XML_PARSE if can't parse init_data.xml // - CL_INVALID_DEVICE_TYPE if unable to get gpu_type information // - CL_INVALID_DEVICE if unable to get opencl_device_index or gpu device_num // - CL_DEVICE_NOT_FOUND if the requested device was not found // - an OpenCL error number if OpenCL error // int boinc_get_opencl_ids( int argc, char** argv, int type, cl_device_id* device, cl_platform_id* platform ){ int retval; APP_INIT_DATA aid; char *gpu_type = NULL; int gpu_device_num = -1; int i; retval = boinc_parse_init_data_file(); if (retval) return retval; boinc_get_init_data(aid); if (strlen(aid.gpu_type)) { gpu_type = aid.gpu_type; } else { switch (type) { case PROC_TYPE_NVIDIA_GPU: gpu_type = (char *)GPU_TYPE_NVIDIA; break; case PROC_TYPE_AMD_GPU: gpu_type = (char *)GPU_TYPE_ATI; break; case PROC_TYPE_INTEL_GPU: gpu_type = (char *)GPU_TYPE_INTEL; break; } } if ((!gpu_type) || !strlen(gpu_type)) { fprintf(stderr, "GPU type not found in %s\n", INIT_DATA_FILE); return CL_INVALID_DEVICE_TYPE; } if (aid.gpu_opencl_dev_index < 0) { if (compareBOINCVersionTo(7,0,12) >= 0) { // gpu_opencl_dev_index was added in BOINC version 7.0.12. // A gpu_opencl_dev_index value of -1 in version 7.0.12 or later // means BOINC client did not assign an OpenCL GPU to this task. fprintf(stderr, "Illegal value for gpu_opencl_dev_index: %d in BOINC Client %d.%d.%d\n", aid.gpu_opencl_dev_index, aid.major_version, aid.minor_version, aid.release); return CL_INVALID_DEVICE; } // Older versions of init_data.xml don't have the gpu_opencl_dev_index // field so use the value of gpu_device_num if available. gpu_device_num = aid.gpu_device_num; if (gpu_device_num < 0) { if (compareBOINCVersionTo(6,13,3) < 0) { // gpu_device_num and gpu_type fields were added in BOINC version 6.13.3. // Very old versions of init_data.xml don't have gpu_device_num field // but instead pass the device number as a command-line argument. for (i=0; i= 0) { // gpu_opencl_dev_index was added in BOINC version 7.0.12. // A gpu_opencl_dev_index value of -1 in version 7.0.12 or // later means BOINC did not assign an OpenCL GPU to this task. fprintf(stderr, "Illegal value for gpu_opencl_dev_index: %d in BOINC Client %d.%d.%d\n", aid.gpu_opencl_dev_index, aid.major_version, aid.minor_version, aid.release); return CL_INVALID_DEVICE; } if (aid.gpu_device_num < 0) { if (compareBOINCVersionTo(6,13,3) >= 0) { // gpu_device_num and gpu_type fields were added in BOINC version 6.13.3. // A gpu_device_num value of -1 in version 6.13.3 or later means // BOINC did not assign a GPU to this task. fprintf(stderr, "Illegal value for gpu_device_num: %d in BOINC Client %d.%d.%d\n", aid.gpu_device_num, aid.major_version, aid.minor_version, aid.release); return CL_INVALID_DEVICE; } } } retval = boinc_get_opencl_ids_aux( aid.gpu_type, aid.gpu_opencl_dev_index, aid.gpu_device_num, device, platform ); return retval; } static int compareBOINCVersionTo(int toMajor, int toMinor, int toRelease) { APP_INIT_DATA aid; boinc_get_init_data(aid); if (aid.major_version < toMajor) return -1; if (aid.major_version > toMajor) return 1; if (aid.minor_version < toMinor) return -1; if (aid.minor_version > toMinor) return 1; if (aid.release < toRelease) return -1; if (aid.release > toRelease) return 1; return 0; }