OpenCL 正在改进与其他 API (如 Vulkan )的互操作方式。本文向您介绍了最新的 OpenCL 互操作风格,最新的 NVIDIA 驱动程序已经支持这种风格。我们提供了可下载的示例代码,所以您今天可以尝试这个新功能。
需要一种新的互操作方式
开发人员通常将 OpenCL for compute 与其他 API (如 OpenGL )一起使用,以访问包括图形渲染在内的功能。 OpenCL 长期以来一直支持通过扩展与 OpenGL 、 OpenGL ES 、 EGL 、 Direct3D 10 和 Direct3D 11 共享隐式缓冲区和图像对象:
cl_khr_gl_sharing
cl_khr_gl_event
cl_khr_egl_image
cl_khr_egl_event
cl_khr_d3d10_sharing
cl_khr_d3d11_sharing
新一代 GPU API (如 Vulkan )使用对外部内存的显式引用以及信号量来协调对共享资源的访问。到目前为止,还没有 OpenCL 扩展来支持外部内存和信号量与这类新的 API 共享。
OpenCL 和 Vulkan 之间的互操作在移动和桌面平台上都有很强的需求。 NVIDIA 与 Khronos OpenCL 工作组密切合作,发布了一套临时跨供应商的 KHR 扩展。这些扩展使应用程序能够在 OpenCL 和 Vulkan 等 API 之间高效地共享数据,与使用隐式资源的前一代互操作 API 相比,灵活性显著提高。
这组新的外部内存和信号量共享扩展提供了一个通用框架,使 OpenCL 能够使用 Vulkan 开发人员熟悉的方法导入外部 API 导出的外部内存和信号量句柄。然后, OpenCL 使用这些信号量来同步外部运行时,协调共享内存的使用。
然后可以添加特定于 API 的外部互操作扩展,以处理与特定 API 交互的细节。 Vulkan 互操作现在可用,并计划使用其他 API ,如 DirectX 12 。
OpenCL 新的外部信号量和内存共享功能包括单独的一组精心构造的扩展。
信号量扩展
这组扩展增加了从操作系统特定的信号量句柄创建 OpenCL 信号量对象的能力。
- cl_khr_semaphore – 表示带有等待和信号的信号量。这是一个新的 OpenCL 对象类。
- cl_khr_external_semaphore – 使用导入和导出外部信号量的机制扩展
cl_khr_semaphore
,类似于 VK_KHR_external_semaphore 。
以下扩展使用特定于句柄类型的行为扩展cl_khr_external_semaphore
:
cl_khr_external_semaphore_opaque_fd
– 使用带有引用传输的 Linux fd 句柄共享外部信号量,类似于 VK_KHR_external_semaphore_fd 。cl_khr_external_semaphore_win32
– 与 VK_KHR_external_semaphore_win32 类似,使用 win32 NT 和 KMT 句柄与引用转移共享外部信号量。
内存扩展
这些扩展增加了从操作系统特定的内存句柄创建 OpenCL 内存对象的能力。它们的设计与 Vulkan 外部存储器扩展 VK_KHR_external_memory . 类似
- cl_khr_external_memory – 从其他 API 导入外部内存。
以下扩展使用特定于句柄类型的行为扩展cl_khr_external_memory
:
cl_khr_external_memory_opaque_fd
– 使用 Linux fd 句柄共享外部内存,类似于 VK_KHR_external_memory_fd 。cl_khr_external_memory_win32
– 使用 win32 NT 和 KMT 句柄共享外部内存,类似于 VK_KHR_external_memory_win32 。
使用 OpenCL
典型的互操作用例包括以下步骤。
检查所需的支持是否可用:
- 检查底层 OpenCL 平台和带有
clGetPlatformInfo
和clGetDeviceInfo
的设备是否支持所需的扩展cl_khr_external_semaphore
和cl_khr_external_memory
。 - 为了能够使用 Win32 信号量和内存句柄,请检查
cl_khr_external_semaphore_win32_khr
和cl_khr_external_memory_win32_khr
扩展是否存在。 - 为了能够使用 FD 信号量和内存句柄,请检查
cl_khr_external_semaphore_opaque_fd_khr
和cl_khr_external_memory_opaque_fd_khr
扩展是否存在。这也可以通过查询支持的句柄类型来检查。
导入外部信号量需要cl_khr_external_semaphore
。如果支持cl_khr_external_semaphore_opaque_fd
,则可以使用clCreateSemaphoreWithPropertiesKHR
和 OpenCL 中的 FD 句柄导入 Vulkan 导出的外部信号量。
// Get cl_devices of the platform. clGetDeviceIDs(..., &devices, &deviceCount);
// Create cl_context with just first device clCreateContext(..., 1, devices, ...);
// Obtain fd/win32 or similar handle for external semaphore to be imported from the other API. int fd = getFdForExternalSemaphore();// Create clSema of type cl_semaphore_khr usable on the only available device assuming the semaphore was imported from the same device.
cl_semaphore_properties_khr sema_props[] = {(cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_KHR, (cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_BINARY_KHR, (cl_semaphore_properties_khr)CL_SEMAPHORE_HANDLE_OPAQUE_FD_KHR, (cl_semaphore_properties_khr)fd, 0}; int errcode_ret = 0; cl_semaphore_khr clSema = clCreateSemaphoreWithPropertiesKHR(context, sema_props, &errcode_ret);
导入图像需要cl_khr_external_memory
和对图像的支持。在 OpenCL 中,通过clCreateSemaphoreWithPropertiesKHR
使用 Win32 句柄导入 Vulkan 导出的外部信号量。
// Get cl_devices of the platform. clGetDeviceIDs(..., &devices, &deviceCount);
// Create cl_context with just first device clCreateContext(..., 1, devices, ...);
// Obtain fd/win32 or similar handle for external semaphore to be imported from the other API. void *handle = getWin32HandleForExternalSemaphore();
// Create clSema of type cl_semaphore_khr usable on the only available device assuming the semaphore was imported from the same device. cl_semaphore_properties_khr sema_props[] = {(cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_KHR, (cl_semaphore_properties_khr)CL_SEMAPHORE_TYPE_BINARY_KHR, (cl_semaphore_properties_khr)CL_SEMAPHORE_HANDLE_OPAQUE_WIN32_KHR, (cl_semaphore_properties_khr)handle, 0}; int errcode_ret = 0; cl_semaphore_khr clSema = clCreateSemaphoreWithPropertiesKHR(context, sema_props, &errcode_ret);
在 OpenCL 中,使用 FD 句柄将 Vulkan 导出的外部内存作为缓冲内存与clCreateBufferWithProperties
一起导入。
// Get cl_devices of the platform.
clGetDeviceIDs(..., &devices, &deviceCount);
// Create cl_context with just first device
clCreateContext(..., 1, devices, ...);
// Obtain fd/win32 or similar handle for external memory to be imported from other API.
int fd = getFdForExternalMemory();
// Create extMemBuffer of type cl_mem from fd.
cl_mem_properties_khr extMemProperties[] =
{ (cl_mem_properties_khr)CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR,
(cl_mem_properties_khr)fd,
0
};
cl_mem extMemBuffer = clCreateBufferWithProperties(/*context*/ clContext,
/*properties*/ extMemProperties,
/*flags*/ 0,
/*size*/ size,
/*host_ptr*/ NULL,
/*errcode_ret*/ &errcode_ret);
在 OpenCL 中,使用clCreateImageWithProperties
将 Vulkan 导出的外部内存作为图像内存导入。
// Create img of type cl_mem. Obtain fd/win32 or similar handle for external memory to be imported from other API. int fd = getFdForExternalMemory();
// Set cl_image_format based on external image info cl_image_format clImgFormat = { }; clImageFormat.image_channel_order = CL_RGBA; clImageFormat.image_channel_data_type = CL_UNORM_INT8;
// Set cl_image_desc based on external image info size_t clImageFormatSize; cl_image_desc image_desc = { }; image_desc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; image_desc.image_width = width; image_desc.image_height = height; image_desc.image_depth = depth; cl_mem_properties_khr extMemProperties[] = { (cl_mem_properties_khr)CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR, (cl_mem_properties_khr)fd, 0 };
cl_mem img = clCreateImageWithProperties(/*context*/ clContext, /*properties*/ extMemProperties, /*flags*/ 0, /*image_format*/ &clImgFormat, /*image_desc*/ &image_desc, /*errcode_ret*/ &errcode_ret)
使用信号量 wait 和 signal 在 OpenCL 和 Vulkan 之间同步。
// Create clSema using one of the above examples of external semaphore creation. int errcode_ret = 0; cl_semaphore_khr clSema = clCreateSemaphoreWithPropertiesKHR(context, sema_props, &errcode_ret); while (true) { // (not shown) Signal the semaphore from the other API,
// Wait for the semaphore in OpenCL clEnqueueWaitSemaphoresKHR( /*command_queue*/ command_queue, /*num_sema_objects*/ 1, /*sema_objects*/ &clSema, /*num_events_in_wait_list*/ 0, /*event_wait_list*/ NULL, /*event*/ NULL); clEnqueueNDRangeKernel(command_queue, ...); clEnqueueSignalSemaphoresKHR(/*command_queue*/ command_queue, /*num_sema_objects*/ 1, /*sema_objects*/ &clSema, /*num_events_in_wait_list*/ 0, /*event_wait_list*/ NULL, /*event*/ NULL); // (not shown) Launch work in the other API that waits on 'clSema'
今天就试试吧!
您可以使用可下载的 sample code 以及 NVIDIA R510 (或更高版本)驱动程序,尝试新的 NVIDIA OpenCL 实现 Vulkan 互操作:
有关更多信息,请参阅 Khronos 发布了用于神经网络推理和 OpenCL / Vulkan 互操作的 OpenCL 3.0 扩展 .