使用 Vulkan VkImage 作为 CUDA cuArray
使用 Vulkan VkImage 作为 CUDA cuArray
【问题标题】:Use Vulkan VkImage as a CUDA cuArray使用 Vulkan VkImage 作为 CUDA cuArray
【发布时间】:2019-08-20 20:01:10
【问题描述】:
将 Vulkan VkImage 用作 CUDA cuArray 的正确方法是什么?
我一直在尝试遵循一些示例,但是我在调用 cuExternalMemoryGetMappedMipmappedArray() 时收到了 CUDA_ERROR_INVALID_VALUE
以有序的方式提供信息。
我正在使用 CUDA 10.1
基本代码来自https://github.com/SaschaWillems/Vulkan,特别是我正在使用01 - Vulkan Gears 演示,丰富了saveScreenshot 方法09 - Capturing screenshots
我不会将快照图像保存到文件中,而是将快照图像作为 CUarray 发送到 CUDA。
我已启用以下实例和设备扩展:
std::vector<const char*> instanceExtensions = {VK_EXT_DEBUG_REPORT_EXTENSION_NAME,VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME };std::vector<const char*> deviceExtensions = { VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME };
我有一个VkImage,创建如下:
// Create the linear tiled destination image to copy to and to read the memory fromVkImageCreateInfo imageCreateCI(vks::initializers::imageCreateInfo());imageCreateCI.imageType = VK_IMAGE_TYPE_2D;// Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differimageCreateCI.format = VK_FORMAT_R8G8B8A8_UNORM;imageCreateCI.extent.width = width;imageCreateCI.extent.height = height;imageCreateCI.extent.depth = 1;imageCreateCI.arrayLayers = 1;imageCreateCI.mipLevels = 1;imageCreateCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;imageCreateCI.samples = VK_SAMPLE_COUNT_1_BIT;imageCreateCI.tiling = VK_IMAGE_TILING_LINEAR;imageCreateCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;imageCreateCI.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;VkExternalMemoryImageCreateInfoKHR extImageCreateInfo = {};/** Indicate that the memory backing this image will be exported in an* fd. In some implementations, this may affect the call to* GetImageMemoryRequirements() with this image.*/extImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;extImageCreateInfo.handleTypes |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;imageCreateCI.pNext = &extImageCreateInfo;// Create the imageVkImage dstImage;VK_CHECK_RESULT(vkCreateImage(device, &imageCreateCI, nullptr, &dstImage));// Create memory to back up the imageVkMemoryRequirements memRequirements;VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo());VkDeviceMemory dstImageMemory;vkGetImageMemoryRequirements(device, dstImage, &memRequirements);memAllocInfo.allocationSize = memRequirements.size;// Memory must be host visible to copy frommemAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);VkExportMemoryAllocateInfoKHR exportInfo = {};exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;exportInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;memAllocInfo.pNext = &exportInfo;VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory));VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0));
从那里我会:
获取 Vulkan 内存处理程序:
intCuEncoderImpl::getVulkanMemoryHandle(VkDevice device,VkDeviceMemory memory) {// Get handle to memory of the VkImageint fd = -1;VkMemoryGetFdInfoKHR fdInfo = { };fdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;fdInfo.memory = memory;fdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;auto func = (PFN_vkGetMemoryFdKHR) vkGetDeviceProcAddr(device,"vkGetMemoryFdKHR");if (!func) {printf("Failed to locate function vkGetMemoryFdKHR\n");return -1;}VkResult r = func(device, &fdInfo, &fd);if (r != VK_SUCCESS) {printf("Failed executing vkGetMemoryFdKHR [%d]\n", r);return -1;}return fd;}
导入内存:
CUDA_EXTERNAL_MEMORY_HANDLE_DESC memDesc = { };memDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD;memDesc.handle.fd = getVulkanMemoryHandle(device, memory);memDesc.size = extent.width*extent.height*4;CUDA_DRVAPI_CALL(cuImportExternalMemory(&externalMem, &memDesc));
并映射内存:这是失败的步骤。
CUarray CuEncoderImpl::getCUDAArrayFromExternalMemory(const VkExtent3D &extent,const CUexternalMemory &m_extMem) {CUmipmappedArray m_mipmapArray;CUresult result = CUDA_SUCCESS;CUarray array;CUDA_ARRAY3D_DESCRIPTOR arrayDesc = { };arrayDesc.Width = extent.width;arrayDesc.Height = extent.height;arrayDesc.Depth = 0;arrayDesc.Format = CU_AD_FORMAT_UNSIGNED_INT32;arrayDesc.NumChannels = 4;arrayDesc.Flags = CUDA_ARRAY3D_SURFACE_LDST;CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC mipmapArrayDesc = { };mipmapArrayDesc.arrayDesc = arrayDesc;mipmapArrayDesc.numLevels = 1;mipmapArrayDesc.offset = 0;CUDA_DRVAPI_CALL(cuExternalMemoryGetMappedMipmappedArray(&m_mipmapArray, m_extMem, &mipmapArrayDesc));CUDA_DRVAPI_CALL(cuMipmappedArrayGetLevel(&array, m_mipmapArray, 0));return array;
}
我一直在尝试多种参数组合,但到目前为止都失败了。错误指向一个无效的参数,但我不知道如何找出问题所在。
唯一可行的方法是将 Vulkan 映像内存映射到主机缓冲区,然后将其复制到 CUDA 数组中......但我想这很昂贵,如果可能的话我想避免它。
【问题讨论】:
@talonmies Vulkan-CUDA 互操作性是 CUDA 10 的一项功能,请参阅 devblogs.nvidia.com/cuda-10-features-revealed
标签: cuda gpu nvidia vulkan cuda-arrays
【解决方案1】:
为了记录,我终于让它工作了。
我必须对问题中列出的代码进行一些注释和修改:
Vulkan-CUDA 互操作性被宣传为 CUDA 10 的一项功能,请参阅CUDA 10 Features revealed
要映射的图像的平铺必须是 `VK_IMAGE_TILING_OPTIMAL
imageCreateCI.tiling = VK_IMAGE_TILING_OPTIMAL;
该图像的内存必须使用VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT 分配
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
导入内存时的内存描述符应使用内存要求中返回的内存大小(下面的size 是创建图像的代码中的memRequirements.size):
CUDA_EXTERNAL_MEMORY_HANDLE_DESC memDesc = { };memDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD;memDesc.handle.fd = getVulkanMemoryHandle(device, memory);memDesc.size = size;CUDA_DRVAPI_CALL(cuImportExternalMemory(&externalMem, &memDesc));
最后映射的数组被描述为CU_AD_FORMAT_UNSIGNED_INT8,有四个通道和CUDA_ARRAY3D_COLOR_ATTACHMENT
CUDA_ARRAY3D_DESCRIPTOR arrayDesc = { };arrayDesc.Width = extent.width;arrayDesc.Height = extent.height;arrayDesc.Depth = 0;arrayDesc.Format = CU_AD_FORMAT_UNSIGNED_INT8;arrayDesc.NumChannels = 4;arrayDesc.Flags = CUDA_ARRAY3D_COLOR_ATTACHMENT;CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC mipmapArrayDesc = { };mipmapArrayDesc.arrayDesc = arrayDesc;mipmapArrayDesc.numLevels = 1;mipmapArrayDesc.offset = 0;CUDA_DRVAPI_CALL(cuExternalMemoryGetMappedMipmappedArray(&m_mipmapArray, m_extMem, &mipmapArrayDesc));
复制
在这些更改之后,我能够让它工作。 我很少有更改是我这边明显的错误(比如大小),我在第 100 次仔细阅读文档时发现的一些东西,其他人是对文档中提示的猜测,最后,大量的试验和错误.
【讨论】:
非常感谢你,我设法在不到一天的时间内运行了它:)
不过,我不确定一件事。我们必须在这个管道中调用哪些销毁/释放函数?我猜,cudaFreeMipmappedArray 和 cudaDestroyExternalMemory,但不是 mipmap 级别的 cudaFreeArray?
CUDA 10 功能揭晓:图灵、CUDA 图等 |英伟达技术博客 (nvidia.com)
CUDA Toolkit 12.1 Downloads | NVIDIA Developer
NVIDIA/cuda-samples: Samples for CUDA Developers which demonstrates features in CUDA Toolkit (github.com)