/* 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" int hb_opencl_library_open(hb_opencl_library_t *opencl) { if (opencl == NULL) { 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_open: 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(clReleaseProgram); HB_OCL_LOAD(clSetKernelArg); HB_OCL_LOAD(clWaitForEvents); return 0; fail: hb_opencl_library_close(opencl); return -1; } void hb_opencl_library_close(hb_opencl_library_t *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(clReleaseProgram); HB_OCL_UNLOAD(clSetKernelArg); HB_OCL_UNLOAD(clWaitForEvents); } } static int hb_opencl_device_is_supported(cl_device_type type, const char *vendor, const char *version) { int major, minor; // we only support OpenCL on GPUs // disable on NVIDIA to to a bug (FIXME) if (!(type & CL_DEVICE_TYPE_GPU) || !(strncmp(vendor, "NVIDIA", 6 /* strlen("NVIDIA") */))) { return 0; } // check OpenCL version; format: // OpenCL if (sscanf(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); } int hb_opencl_available() { static int opencl_available = -1; if (opencl_available >= 0) { return opencl_available; } opencl_available = 0; cl_device_type type; char vendor[100], version[100]; cl_device_id *device_ids = NULL; cl_platform_id *platform_ids = NULL; hb_opencl_library_t lib, *opencl = &lib; cl_uint i, j, num_platforms, num_devices; /* * 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. */ if (hb_opencl_library_open(opencl) == 0) { if (opencl->clGetPlatformIDs(0, NULL, &num_platforms) != CL_SUCCESS || !num_platforms) { goto end; } if ((platform_ids = malloc(sizeof(cl_platform_id) * num_platforms)) == NULL) { goto end; } if (opencl->clGetPlatformIDs(num_platforms, platform_ids, NULL) != CL_SUCCESS) { goto end; } for (i = 0; i < num_platforms; i++) { if (opencl->clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_ALL, 0, NULL, &num_devices) != CL_SUCCESS || !num_devices) { goto end; } if ((device_ids = malloc(sizeof(cl_device_id) * num_devices)) == NULL) { goto end; } if (opencl->clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_ALL, num_devices, device_ids, NULL) != CL_SUCCESS) { goto end; } for (j = 0; j < num_devices; j++) { if (device_ids[j] != NULL) { opencl->clGetDeviceInfo(device_ids[j], CL_DEVICE_VENDOR, sizeof(vendor), vendor, NULL); opencl->clGetDeviceInfo(device_ids[j], CL_DEVICE_VERSION, sizeof(version), version, NULL); opencl->clGetDeviceInfo(device_ids[j], CL_DEVICE_TYPE, sizeof(type), &type, NULL); if (hb_opencl_device_is_supported(type, (const char*)vendor, (const char*)version)) { opencl_available = 1; goto end; } } } free(device_ids); device_ids = NULL; } } end: free(device_ids); free(platform_ids); 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 lib, *opencl = &lib; 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; } cl_device_type type; cl_device_id *device_ids; cl_platform_id *platform_ids; cl_uint i, j, k, num_platforms, num_devices; char vendor[100], name[1024], version[100], driver[1024]; if (opencl->clGetPlatformIDs(0, NULL, &num_platforms) != CL_SUCCESS || !num_platforms) { goto end; } if ((platform_ids = malloc(sizeof(cl_platform_id) * num_platforms)) == NULL) { goto end; } if (opencl->clGetPlatformIDs(num_platforms, platform_ids, NULL) != CL_SUCCESS) { goto end; } for (i = 0, k = 1; i < num_platforms; i++) { if (opencl->clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_ALL, 0, NULL, &num_devices) != CL_SUCCESS || !num_devices) { goto end; } if ((device_ids = malloc(sizeof(cl_device_id) * num_devices)) == NULL) { goto end; } if (opencl->clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_ALL, num_devices, device_ids, NULL) != CL_SUCCESS) { goto end; } for (j = 0; j < num_devices; j++) { if (device_ids[j] != NULL) { opencl->clGetDeviceInfo(device_ids[j], CL_DEVICE_VENDOR, sizeof(vendor), vendor, NULL); opencl->clGetDeviceInfo(device_ids[j], CL_DEVICE_NAME, sizeof(name), name, NULL); opencl->clGetDeviceInfo(device_ids[j], CL_DEVICE_VERSION, sizeof(version), version, NULL); opencl->clGetDeviceInfo(device_ids[j], CL_DRIVER_VERSION, sizeof(driver), driver, NULL); opencl->clGetDeviceInfo(device_ids[j], CL_DEVICE_TYPE, sizeof(type), &type, NULL); // don't list unsupported devices if (type & CL_DEVICE_TYPE_CPU) { continue; } hb_log("OpenCL device #%d: %s %s", k++, vendor, name); hb_log(" - OpenCL version: %s", version + 7 /* strlen("OpenCL ") */); hb_log(" - driver version: %s", driver); hb_log(" - device type: %s%s", type & CL_DEVICE_TYPE_CPU ? "CPU" : type & CL_DEVICE_TYPE_GPU ? "GPU" : type & CL_DEVICE_TYPE_CUSTOM ? "Custom" : type & CL_DEVICE_TYPE_ACCELERATOR ? "Accelerator" : "Unknown", type & CL_DEVICE_TYPE_DEFAULT ? " (default)" : ""); hb_log(" - supported: %s", hb_opencl_device_is_supported(type, (const char*)vendor, (const char*)version) ? "yes" : "no"); } } free(device_ids); } end: hb_opencl_library_close(opencl); }