External device resources and other handles can be shared with HIP in order to provide interoperability between different GPU APIs. This example showcases a HIP program that interacts with the Vulkan API: A HIP kernel is used to simulate a sine wave over a grid of points, in a buffer that is shared with Vulkan. The resulting data is then rendered to a window using the Vulkan API. A set of shared semaphores is used to guarantee synchronous access to the device memory shared between HIP and Vulkan.
- A window is opened using the GLFW library.
- The Vulkan API is initialized: Function pointers are loaded, the Vulkan instance is created.
- A physical device is picked to execute the example kernel on and to render the result to the window. This physical device must be the same for HIP and for Vulkan in order to be able to share the required resources. This is done by comparing the device's UUID, which can be queried from a HIP device by querying
hipDeviceGetUuid
and from a Vulkan physical device by passingVkPhysicalDeviceIDProperties
tovkGetPhysicalDeviceProperties2
. If the UUIDs from a particular HIP device and Vulkan device are the same, they represent the same physical or virtual device. - A Vulkan logical device and related handles are initialized from the physical device handle.
- A HIP stream is created on the same physical device.
- A Vulkan swapchain and related handles are initialized for the window from the logical device and related handles.
- Additional Vulkan handles required for the rendering process are initialized: A render pass, the graphics pipeline, frame buffers and other frame resources.
- Three buffers are allocated using Vulkan: A buffer holding x- and y-coordinates for the triangle grid, a separate buffer holding a height value corresponding to each point in the triangle grid, and an index buffer that defines the triangles grid made up of the grid coordinates. The height buffer is going to be shared with HIP, and therefore it needs to be created in a way that allows it to be exported to a native memory handle. This requires passing
VkExternalMemoryBufferCreateInfoKHR
tovkCreateBuffer
when creating a buffer, withVkExternalMemoryBufferCreateInfoKHR::handleTypes
initialized to the appropriate type for the native platform. Additionally, this requires setting the same value onVkExportMemoryAllocateInfoKHR
, which must be passed tovkAllocateMemory
when allocating memory for a buffer that is to be exported. - The x- and y-coordinates buffer and the index buffer are initialized with their contents. These buffers do not change during the duration of the program.
- A HIP external memory handle is created from the Vulkan height buffer memory handle. This is done by first exporting the Vulkan buffer to a platform-native handle using
VkGetMemoryFd
orVkGetMemoryWin32Handle
depending on the platform, and then importing that handle to HIP usinghipImportExternalMemory
. - A pointer to the device memory of the height buffer is obtained from the HIP external memory handle using
hipExternalMemoryGetMappedBuffer
. - Two semaphores used to synchronize memory accesses between HIP and Vulkan are initialized: The first synchronizes the access from when the buffer was used to render the previous frame in Vulkan to when the HIP kernel is invoked, , and the second synchronizes the access from when the HIP kernel is finished to when Vulkan can use the buffer to render the next frame. Similar to buffers, these must be created in a way that allows them to be exported to a platform-native semaphore handle, so that they may later be imported as HIP external semaphore. This is done by passing
VkExportSemaphoreCreateInfoKHR
tovkCreateSemaphore
, of whichhandleTypes
must again be initialized to the appropriate platform-dependent handle type. - The Vulkan semaphores are converted to HIP external semaphores. This is done by first exporting a Vulkan semaphore handle to a native semaphore handle, either by
vkGetSemaphoreFdKHR
orvkGetSemaphoreWin32HandleKHR
depending on the target platform. The resulting handle is passed tohipImportExternalSemaphore
to obtain the HIP semaphore handle.
A frame is rendered as follows:
- The frame resources for the current frame in the frame pipeline are fetched from memory.
- The next image index is acquired from the swapchain.
- The command pool associated to the current frame is reset and the associated command buffer is initialized.
hipWaitExternalSemaphoresAsync
is used to ensure that Vulkan has finished rendering the previous frame before the HIP kernel is invoked. Note that this function is not required on the first frame.- The HIP kernel is invoked.
hipSignalExternalSemaphoresAsync
is used to signal Vulkan that HIP is now finished with the buffer and that Vulkan can proceed with rendering.- The Vulkan rendering commands are recorded to the current frame's command buffer.
- The command buffer is submitted to the Vulkan graphics queue. The semaphore that synchronizes the HIP kernel invocation with the Vulkan rending commands is passed to
VkSubmitInfo::pWaitSemaphores
to make Vulkan wait on the semaphore signal before proceeding with rendering. As a small optimization, the corresponding element inVkSubmitInfo::pWaitDstStageMask
is set toVK_PIPELINE_STAGE_VERTEX_INPUT_BIT
. The height buffer is only needed at the vertex input stage, and this way the prior stages can already be executed by Vulkan even if the semaphore is not signaled yet. The semaphore that synchronizes between rendering the previous frame and running the HIP kernel for the next frame is passed tovkSubmitInfo::pSignalSemaphores
, so that Vulkan signals it when the frame is finished with rendering. - The swapchain is asked to present the current frame to the screen.
To share memory allocated by Vulkan with HIP, the VkDeviceMemory
must be created by passing the VkExportMemoryAllocateInfoKHR
structure to vkAllocateDeviceMemory
. This structure needs the appropriate handleTypes
set to a type that can be shared with HIP for the current platform; VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
for Linux and VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR
or VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR
for Windows. Any Vulkan buffer that is to be associated with this device memory must similarly be created by passing VkExternalMemoryBufferCreateInfoKHR
to vkCreateBuffer
, of which the handleTypes
member must be initialized to the same value. The VkDeviceMemory
handle can then be exported to a native file descriptor or HANDLE
using vkGetMemoryFdKHR
or vkGetMemoryWin32HandleKHR
respectively on Linux and Windows. A hipExternalMemory_t
can then be imported from a native handle through hipImportExternalMemory
. This function must be passed an instance of hipExternalmemoryHandleDesc
, of which type
is initialized with a handle type compatible with the Vulkan handleTypes
. This mapping is as follows:
Vulkan memory handle type | HIP memory handle type |
---|---|
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR |
hipExternalMemoryHandleTypeOpaqueFd |
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR |
hipExternalMemoryHandleTypeOpaqueWin32 |
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR |
hipExternalMemoryHandleTypeOpaqueWin32Kmt |
To actually use this external memory handle in HIP the corresponding HIP device memory pointer should first be obtained. This can be done with the hipExternalMemoryGetMappedBuffer
function.
Sharing semaphores follows a similar process: The VkSemaphore
must be created by passing VkExportSemaphoreCreateInfoKHR
, of which handleTypes
must be initialized to VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
for Linux, or VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR
or VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR
for Windows. The VkSemaphore
handle can then be exported to a native Linux file descriptor or Windows HANDLE
using vkGetSemaphoreFdKHR
or vkGetSemaphoreWin32HandleKHR
on Linux and Windows respectively. The hipExternalSemaphore_t
can then be created using hipImportExternalSemaphore
. It must be passed an instance of hipExternalSemaphoreHandleDesc
, of which type
is again initialized with a compatible HIP-version of the Vulkan handleTypes
. This mapping is as follows:
Vulkan semaphore handle type | HIP semaphore handle type |
---|---|
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR |
hipExternalSemaphoreHandleTypeOpaqueFd |
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR |
hipExternalSemaphoreHandleTypeOpaqueWin32 |
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR |
hipExternalSemaphoreHandleTypeOpaqueWin32Kmt |
To wait on a shared semaphore in HIP, hipWaitExternalSemaphoresAsync
should be used. This must be passed a number of hipExternalSemaphoreWaitParams
structures, each corresponding to a semaphore with the same index. When using timeline semaphores, its fence.value
member can be used to specify which timeline semaphore value to wait on.
To signal a shared semaphore in HIP, the hipSignalExternalSemaphoresAsync
function can be used. This must be passed a number of hipExternalSemaphoreSignalParams
structures, each corresponding to a semaphore with the same index. When using timeline semaphores, its fence.value
member should be set to specify the value to which the semaphore should be set.
This example has additional library dependencies besides HIP:
- GLFW3. GLFW can be installed either through the package manager, or can be obtained from its home page. If using CMake, the
glfw3Config.cmake
file must be in a path that CMake searches by default or must be passed using-DCMAKE_MODULE_PATH
. The official GLFW3 binaries do not ship this file on Windows, and so GLFW3 must either be compiled manually. CMake will be able to find GLFW on Windows if it is installed inC:\Program Files(x86)\glfw\
. Alternatively, GLFW can be obtained from vcpkg, which does ship the required cmake files. In this case, the vcpkg toolchain path should be passed to CMake using-DCMAKE_TOOLCHAIN_FILE="/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake"
. If using Visual Studio, the easiest way to obtain GLFW is by installing glfw3 from vcpkg. Alternatively, the appropriate path to the GLFW3 library and header directories can be set in Properties->Linker->General->Additional Library Directories and Properties->C/C++->General->Additional Include Directories. When using this method, the appropriate name for the glfw library should also be updated under Properties->C/C++->Linker->Input->Additional Dependencies. - Vulkan headers, validation layers, and
glslangValidator
are required. The easiest way to obtain this is by installing the LunarG Vulkan SDK. CMake will be able to find the SDK using theVULKAN_SDK
environment variable, which is set by default using the SDK activation script on Linux. On Windows, this environment variable is not automatically provided, and so should be set to the appropriate path before invoking CMake. The Visual Studio projects will automatically pick upVULKAN_SDK
. Alternatively, the required Vulkan components can be installed through the system package manager. Note that libvulkan is not required, the example loads function pointers dynamically.
threadIdx
,blockIdx
,blockDim
hipComputeModeProhibited
hipCUDAErrorTohipError
hipDestroyExternalMemory
hipDestroyExternalSemaphore
hipDeviceGetUuid
hipExternalMemoryBufferDesc
hipExternalMemoryGetMappedBuffer
hipExternalMemoryHandleDesc
hipExternalMemoryHandleType
hipExternalMemoryHandleTypeOpaqueFd
hipExternalMemoryHandleTypeOpaqueWin32
hipExternalMemoryHandleTypeOpaqueWin32Kmt
hipExternalSemaphoreHandleDesc
hipExternalSemaphoreHandleType
hipExternalSemaphoreHandleTypeOpaqueFd
hipExternalSemaphoreHandleTypeOpaqueWin32
hipExternalSemaphoreHandleTypeOpaqueWin32Kmt
hipExternalSemaphoreSignalParams
hipExternalSemaphoreWaitParams
hipGetDeviceCount
hipGetDeviceProperties
hipGetLastError
hipImportExternalMemory
hipImportExternalSemaphore
hipSetDevice
hipSignalExternalSemaphoresAsync
hipStreamCreate
hipStreamDestroy
hipStreamSynchronize
hipWaitExternalSemaphoresAsync