boinc/api/boinc_opencl.cpp

321 lines
11 KiB
C++

// 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 <http://www.gnu.org/licenses/>.
// 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 <string>
#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<num_platforms; ++platform_index) {
retval = clGetDeviceIDs(
platforms[platform_index], CL_DEVICE_TYPE_GPU,
MAX_COPROC_INSTANCES, devices, &num_devices
);
if (retval != CL_SUCCESS) continue;
// Use gpu_opencl_dev_index if available
if (opencl_device_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<argc-1; i++) {
if ((!strcmp(argv[i], "--device")) || (!strcmp(argv[i], "-device"))) {
gpu_device_num = atoi(argv[i+1]);
break;
}
}
}
if (gpu_device_num < 0) {
// BOINC client apparently 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;
}
}
} // End if (aid.gpu_opencl_dev_index < 0)
retval = boinc_get_opencl_ids_aux(
gpu_type, aid.gpu_opencl_dev_index, gpu_device_num, device, platform
);
return retval;
}
// Deprecated: use the version above instead
//
// 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(cl_device_id* device, cl_platform_id* platform) {
int retval;
APP_INIT_DATA aid;
retval = boinc_parse_init_data_file();
if (retval) return retval;
boinc_get_init_data(aid);
if (!strlen(aid.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 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;
}