/* opencl.c Copyright (c) 2003-2013 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifdef _WIN32 #include #define HB_OCL_DLOPEN LoadLibraryW(L"OpenCL") #define HB_OCL_DLSYM GetProcAddress #define HB_OCL_DLCLOSE FreeLibrary #else #include #ifdef __APPLE__ #define HB_OCL_DLOPEN dlopen("/System/Library/Frameworks/OpenCL.framework/OpenCL", RTLD_NOW) #else #define HB_OCL_DLOPEN dlopen("libOpenCL.so", RTLD_NOW) #endif #define HB_OCL_DLSYM dlsym #define HB_OCL_DLCLOSE dlclose #endif #include "common.h" #include "opencl.h" hb_opencl_library_t *hb_ocl = NULL; int hb_ocl_init() { if (hb_ocl == NULL) { if ((hb_ocl = hb_opencl_library_init()) == NULL) { return -1; } } return 0; } void hb_ocl_close() { hb_opencl_library_close(&hb_ocl); } hb_opencl_library_t* hb_opencl_library_init() { hb_opencl_library_t *opencl; if ((opencl = calloc(1, sizeof(hb_opencl_library_t))) == NULL) { hb_error("hb_opencl_library_init: memory allocation failure"); goto fail; } opencl->library = HB_OCL_DLOPEN; if (opencl->library == NULL) { goto fail; } #define HB_OCL_LOAD(func) \ { \ if ((opencl->func = (void*)HB_OCL_DLSYM(opencl->library, #func)) == NULL) \ { \ hb_log("hb_opencl_library_init: failed to load function '%s'", #func); \ goto fail; \ } \ } HB_OCL_LOAD(clBuildProgram); HB_OCL_LOAD(clCreateBuffer); HB_OCL_LOAD(clCreateCommandQueue); HB_OCL_LOAD(clCreateContextFromType); HB_OCL_LOAD(clCreateKernel); HB_OCL_LOAD(clCreateProgramWithBinary); HB_OCL_LOAD(clCreateProgramWithSource); HB_OCL_LOAD(clEnqueueCopyBuffer); HB_OCL_LOAD(clEnqueueMapBuffer); HB_OCL_LOAD(clEnqueueNDRangeKernel); HB_OCL_LOAD(clEnqueueReadBuffer); HB_OCL_LOAD(clEnqueueUnmapMemObject); HB_OCL_LOAD(clEnqueueWriteBuffer); HB_OCL_LOAD(clFlush); HB_OCL_LOAD(clGetCommandQueueInfo); HB_OCL_LOAD(clGetContextInfo); HB_OCL_LOAD(clGetDeviceIDs); HB_OCL_LOAD(clGetDeviceInfo); HB_OCL_LOAD(clGetPlatformIDs); HB_OCL_LOAD(clGetPlatformInfo); HB_OCL_LOAD(clGetProgramBuildInfo); HB_OCL_LOAD(clGetProgramInfo); HB_OCL_LOAD(clReleaseCommandQueue); HB_OCL_LOAD(clReleaseContext); HB_OCL_LOAD(clReleaseEvent); HB_OCL_LOAD(clReleaseKernel); HB_OCL_LOAD(clReleaseMemObject); HB_OCL_LOAD(clReleaseProgram); HB_OCL_LOAD(clSetKernelArg); HB_OCL_LOAD(clWaitForEvents); //success return opencl; fail: hb_opencl_library_close(&opencl); return NULL; } void hb_opencl_library_close(hb_opencl_library_t **_opencl) { if (_opencl == NULL) { return; } hb_opencl_library_t *opencl = *_opencl; if (opencl != NULL) { if (opencl->library != NULL) { HB_OCL_DLCLOSE(opencl->library); } opencl->library = NULL; #define HB_OCL_UNLOAD(func) { opencl->func = NULL; } HB_OCL_UNLOAD(clBuildProgram); HB_OCL_UNLOAD(clCreateBuffer); HB_OCL_UNLOAD(clCreateCommandQueue); HB_OCL_UNLOAD(clCreateContextFromType); HB_OCL_UNLOAD(clCreateKernel); HB_OCL_UNLOAD(clCreateProgramWithBinary); HB_OCL_UNLOAD(clCreateProgramWithSource); HB_OCL_UNLOAD(clEnqueueCopyBuffer); HB_OCL_UNLOAD(clEnqueueMapBuffer); HB_OCL_UNLOAD(clEnqueueNDRangeKernel); HB_OCL_UNLOAD(clEnqueueReadBuffer); HB_OCL_UNLOAD(clEnqueueUnmapMemObject); HB_OCL_UNLOAD(clEnqueueWriteBuffer); HB_OCL_UNLOAD(clFlush); HB_OCL_UNLOAD(clGetCommandQueueInfo); HB_OCL_UNLOAD(clGetContextInfo); HB_OCL_UNLOAD(clGetDeviceIDs); HB_OCL_UNLOAD(clGetDeviceInfo); HB_OCL_UNLOAD(clGetPlatformIDs); HB_OCL_UNLOAD(clGetPlatformInfo); HB_OCL_UNLOAD(clGetProgramBuildInfo); HB_OCL_UNLOAD(clGetProgramInfo); HB_OCL_UNLOAD(clReleaseCommandQueue); HB_OCL_UNLOAD(clReleaseContext); HB_OCL_UNLOAD(clReleaseEvent); HB_OCL_UNLOAD(clReleaseKernel); HB_OCL_UNLOAD(clReleaseMemObject); HB_OCL_UNLOAD(clReleaseProgram); HB_OCL_UNLOAD(clSetKernelArg); HB_OCL_UNLOAD(clWaitForEvents); } *_opencl = NULL; } static int hb_opencl_device_is_supported(hb_opencl_device_t* device) { // we only support OpenCL on GPUs for now // FIXME: disable on NVIDIA to to a bug if ((device != NULL) && (device->type & CL_DEVICE_TYPE_GPU) && (device->ocl_vendor != HB_OCL_VENDOR_NVIDIA)) { int major, minor; // check OpenCL version: // OpenCL if (sscanf(device->version, "OpenCL %d.%d", &major, &minor) != 2) { return 0; } return (major > HB_OCL_MINVERSION_MAJOR) || (major == HB_OCL_MINVERSION_MAJOR && minor >= HB_OCL_MINVERSION_MINOR); } return 0; } static hb_opencl_device_t* hb_opencl_device_get(hb_opencl_library_t *opencl, cl_device_id device_id) { if (opencl == NULL || opencl->clGetDeviceInfo == NULL) { hb_error("hb_opencl_device_get: OpenCL support not available"); return NULL; } else if (device_id == NULL) { hb_error("hb_opencl_device_get: invalid device ID"); return NULL; } hb_opencl_device_t *device = calloc(1, sizeof(hb_opencl_device_t)); if (device == NULL) { hb_error("hb_opencl_device_get: memory allocation failure"); return NULL; } cl_int status = CL_SUCCESS; device->id = device_id; status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_VENDOR, sizeof(device->vendor), device->vendor, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_NAME, sizeof(device->name), device->name, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_VERSION, sizeof(device->version), device->version, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_TYPE, sizeof(device->type), &device->type, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_PLATFORM, sizeof(device->platform), &device->platform, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DRIVER_VERSION, sizeof(device->driver), device->driver, NULL); if (status != CL_SUCCESS) { free(device); return NULL; } if (!strcmp(device->vendor, "Advanced Micro Devices, Inc.") || !strcmp(device->vendor, "AMD")) { device->ocl_vendor = HB_OCL_VENDOR_AMD; } else if (!strncmp(device->vendor, "NVIDIA", 6 /* strlen("NVIDIA") */)) { device->ocl_vendor = HB_OCL_VENDOR_NVIDIA; } else { device->ocl_vendor = HB_OCL_VENDOR_OTHER; } return device; } static void hb_opencl_devices_list_close(hb_list_t **_list) { if (_list != NULL) { hb_list_t *list = *_list; hb_opencl_device_t *device; while (list != NULL && hb_list_count(list) > 0) { if ((device = hb_list_item(list, 0)) != NULL) { hb_list_rem(list, device); free(device); } } } hb_list_close(_list); } static hb_list_t* hb_opencl_devices_list_get(hb_opencl_library_t *opencl, cl_device_type device_type) { if (opencl == NULL || opencl->library == NULL || opencl->clGetDeviceIDs == NULL || opencl->clGetDeviceInfo == NULL || opencl->clGetPlatformIDs == NULL) { hb_error("hb_opencl_devices_list_get: OpenCL support not available"); return NULL; } hb_list_t *list = hb_list_init(); if (list == NULL) { hb_error("hb_opencl_devices_list_get: memory allocation failure"); return NULL; } cl_device_id *device_ids; hb_opencl_device_t *device; cl_platform_id *platform_ids; cl_uint i, j, num_platforms, num_devices; if (opencl->clGetPlatformIDs(0, NULL, &num_platforms) != CL_SUCCESS || !num_platforms) { goto fail; } if ((platform_ids = malloc(sizeof(cl_platform_id) * num_platforms)) == NULL) { hb_error("hb_opencl_devices_list_get: memory allocation failure"); goto fail; } if (opencl->clGetPlatformIDs(num_platforms, platform_ids, NULL) != CL_SUCCESS) { goto fail; } for (i = 0; i < num_platforms; i++) { if (opencl->clGetDeviceIDs(platform_ids[i], device_type, 0, NULL, &num_devices) != CL_SUCCESS || !num_devices) { // non-fatal continue; } if ((device_ids = malloc(sizeof(cl_device_id) * num_devices)) == NULL) { hb_error("hb_opencl_devices_list_get: memory allocation failure"); goto fail; } if (opencl->clGetDeviceIDs(platform_ids[i], device_type, num_devices, device_ids, NULL) != CL_SUCCESS) { // non-fatal continue; } for (j = 0; j < num_devices; j++) { if ((device = hb_opencl_device_get(opencl, device_ids[j])) != NULL) { hb_list_add(list, device); } } } return list; fail: hb_opencl_devices_list_close(&list); return NULL; } int hb_opencl_available() { static int opencl_available = -1; if (opencl_available >= 0) { return opencl_available; } opencl_available = 0; /* * Check whether we can load the OpenCL library, then check devices and make * sure we support running OpenCL code on at least one of them. */ hb_opencl_library_t *opencl; if ((opencl = hb_opencl_library_init()) != NULL) { int i; hb_list_t *device_list; hb_opencl_device_t *device; if ((device_list = hb_opencl_devices_list_get(opencl, CL_DEVICE_TYPE_ALL)) != NULL) { for (i = 0; i < hb_list_count(device_list); i++) { if ((device = hb_list_item(device_list, i)) != NULL && (hb_opencl_device_is_supported(device))) { opencl_available = 1; break; } } hb_opencl_devices_list_close(&device_list); } hb_opencl_library_close(&opencl); } return opencl_available; } void hb_opencl_info_print() { /* * Note: this function should not log any warnings or errors. * Its only purpose is to list OpenCL-capable devices, so let's initialize * only what we absolutely need here, rather than calling library_open(). */ hb_opencl_library_t ocl, *opencl = &ocl; if ((opencl->library = (void*)HB_OCL_DLOPEN) == NULL || (opencl->clGetDeviceIDs = (void*)HB_OCL_DLSYM(opencl->library, "clGetDeviceIDs" )) == NULL || (opencl->clGetDeviceInfo = (void*)HB_OCL_DLSYM(opencl->library, "clGetDeviceInfo" )) == NULL || (opencl->clGetPlatformIDs = (void*)HB_OCL_DLSYM(opencl->library, "clGetPlatformIDs")) == NULL) { // zero or insufficient OpenCL support hb_log("OpenCL: library not available"); goto end; } int i, idx; hb_list_t *device_list; hb_opencl_device_t *device; if ((device_list = hb_opencl_devices_list_get(opencl, CL_DEVICE_TYPE_ALL)) != NULL) { for (i = 0, idx = 1; i < hb_list_count(device_list); i++) { if ((device = hb_list_item(device_list, i)) != NULL) { // don't list CPU devices (always unsupported) if (!(device->type & CL_DEVICE_TYPE_CPU)) { hb_log("OpenCL device #%d: %s %s", idx++, device->vendor, device->name); hb_log(" - OpenCL version: %s", device->version + 7 /* strlen("OpenCL ") */); hb_log(" - driver version: %s", device->driver); hb_log(" - device type: %s%s", device->type & CL_DEVICE_TYPE_CPU ? "CPU" : device->type & CL_DEVICE_TYPE_GPU ? "GPU" : device->type & CL_DEVICE_TYPE_CUSTOM ? "Custom" : device->type & CL_DEVICE_TYPE_ACCELERATOR ? "Accelerator" : "Unknown", device->type & CL_DEVICE_TYPE_DEFAULT ? " (default)" : ""); hb_log(" - supported: %s", hb_opencl_device_is_supported(device) ? "YES" : "no"); } } } hb_opencl_devices_list_close(&device_list); } end: hb_opencl_library_close(&opencl); }