/* 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);
}