Stephane Germain
Stephane Germain

Reputation: 1

Inter process usage of Vulkan VK_KHR_EXTERNAL_MEMORY_EXTENSION

I am trying to make VK_KHR_EXTERNAL_MEMORY_EXTENSION work. Similar to this
https://stackoverflow.com/questions/50020878/how-to-correctly-share-vkdevicememory-what-is-vkexternalmemorybuffercreateinfok/

It is working well when the import and the export are done in the same process.

But I am not sure if what I try has been already done, I cannot find any example and I don't think that the test code is testing that too. I want one process that creates a VkBuffer, populate it and export it / share the FD. After, IN ANOTHER PROCESS, I try to import that VkBuffer.

I have different trouble, depending on whether it is tested under Windows or Linux. Under Linux, I got VK_OUT_OF_MEMORY at vkAllocateMemory call. On Windows, I sometimes have no error but only '0' or noise as payload, sometime error about a mismatch in the size of the memory (but a size that exists somewhere else in the producer app) so look like if the FD (HANDLE) was referring to another 'random' resource. I also tried to call DuplicatHandle on 'consumer / import side' but in that case, I receive an error about "invalid Handle" from DuplicatHandle.

Here the config structure used on export side: VkBufferCreateInfo, VkExternalMemoryBufferCreateInfo, VkMemoryDedicatedRequirementsKHR, VkMemoryDedicatedAllocateInfo, VkExportMemoryAllocateInfoKHR, VkGetMemoryWin32HandleKHR / VkGetMemoryFdKHR, VkMemoryAllocateInfo

Here the config structire used on import side: VkExternalMemoryBufferCreateInfo, VkBufferCreateInfo, VkMemoryDedicatedAllocateInfo, VkImportMemoryWin32HandleInfoKHR / VkImportMemoryFdInfoKHR, VkMemoryAllocateInfo, VkMemoryDedicatedRequirementsKHR

I tried with and without MemoryDedicated.

Anyway, vkGetBufferMemoryRequirements2 repport that prefersDedicatedAllocation and requiresDedicatedAllocation to false.

I expect to acces to the payload as it was on producer side. Thanks

Here some code:

    int64_t createBuffer(VkPhysicalDevice& p_vkPhysicalDevice, VkDevice& p_vkDevice, VkDeviceSize size,
    VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& p_refVkBuffer, VkDeviceMemory& bufferMemory,
    VkSharingMode p_VkSharingMode, uint32_t* p_pVkQueueFamliyIndices)

{
    //PRINTLN("createBuffer size: %d", size);
    if (size == 0)
    {
        ERROR_PRINTLN("createBuffer with size 0 is illegal.");
        return -1;
    }

    VkBufferCreateInfo bufferInfo = {};
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.size = size;
    bufferInfo.usage = usage;

    bufferInfo.sharingMode = p_VkSharingMode; //VK_SHARING_MODE_EXCLUSIVE;

    if (p_pVkQueueFamliyIndices)
    {
        bufferInfo.queueFamilyIndexCount = 2;
        bufferInfo.pQueueFamilyIndices = p_pVkQueueFamliyIndices;
    }


#if SUPPORT_EXTERNAL_MEMORY

    VkExternalMemoryBufferCreateInfo  externalMemBufferCreateInfo = {};
    externalMemBufferCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO;
    externalMemBufferCreateInfo.handleTypes = GetFdType();

    bufferInfo.pNext = &externalMemBufferCreateInfo;

#endif

    VK_CHECK_RESULT(vkCreateBuffer(p_vkDevice, &bufferInfo, nullptr, &p_refVkBuffer));

    VkMemoryDedicatedRequirementsKHR dedicatedRequirements = {};
    dedicatedRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
    dedicatedRequirements.requiresDedicatedAllocation = VK_TRUE;

    VkMemoryRequirements2 memoryRequirements =
    {
        .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
        .pNext = &dedicatedRequirements,
    };

    const VkBufferMemoryRequirementsInfo2 bufferRequirementsInfo =
    {
        .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2,
        .pNext = NULL,
        .buffer = p_refVkBuffer
    };
    vkGetBufferMemoryRequirements2(p_vkDevice, &bufferRequirementsInfo, &memoryRequirements);

    PRINTLN("prefersDedicatedAllocation: %d  requiresDedicatedAllocation: %d",
        dedicatedRequirements.prefersDedicatedAllocation, dedicatedRequirements.requiresDedicatedAllocation);


#if SUPPORT_EXTERNAL_MEMORY
    VkMemoryDedicatedAllocateInfo dedicated_alloc_info = {};

    dedicated_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
    dedicated_alloc_info.pNext = NULL;
    dedicated_alloc_info.image = VK_NULL_HANDLE;
    dedicated_alloc_info.buffer = p_refVkBuffer;

    VkExportMemoryAllocateInfoKHR export_memory_allocate_info = {};
    export_memory_allocate_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
    export_memory_allocate_info.handleTypes = GetFdType();
    export_memory_allocate_info.pNext = &dedicated_alloc_info;

#ifdef _WIN32

    WinSecurityAttributes            win_security_attributes;
    VkExportMemoryWin32HandleInfoKHR export_memory_win32_handle_info = {};
    export_memory_win32_handle_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR;

    export_memory_win32_handle_info.pAttributes = &win_security_attributes;
    export_memory_win32_handle_info.dwAccess = DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE;
    //export_memory_win32_handle_info.dwAccess = GENERIC_ALL;

    export_memory_win32_handle_info.pNext = &export_memory_allocate_info;
#endif

#endif


    VkMemoryAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memoryRequirements.memoryRequirements.size;
    allocInfo.memoryTypeIndex = findMemoryType(p_vkPhysicalDevice, memoryRequirements.memoryRequirements.memoryTypeBits, properties);


#if SUPPORT_EXTERNAL_MEMORY

#if _WIN32
    allocInfo.pNext = &export_memory_win32_handle_info;
#else
    allocInfo.pNext = &export_memory_allocate_info;
#endif

#endif

    VK_CHECK_RESULT(vkAllocateMemory(p_vkDevice, &allocInfo, nullptr, &bufferMemory)); // Allocate with CUDA (aka EXTERNAL_MEMORY)
    PRINTF("********************* vkAllocateMemory ( %d ) Will Do the initial Bind of the memory (srv side)\r\n", allocInfo.allocationSize);
    if(bufferMemory)
    {
        VK_CHECK_RESULT(vkBindBufferMemory(p_vkDevice, p_refVkBuffer, bufferMemory, 0));
    } // No need to re-print error, VK_CHECK_RESULT already done it
    

    return 0;

}


#ifdef _WIN32
HANDLE get_vulkan_memory_handle(VkDevice& p_vkDevice, VkDeviceMemory memory)
{
    HANDLE                        handle;

    VkMemoryGetWin32HandleInfoKHR win32_handle_info = {};
    win32_handle_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
    win32_handle_info.handleType = GetFdType();
    win32_handle_info.memory = memory;
    
    PFN_vkGetMemoryWin32HandleKHR fct_vkGetMemoryWin32Handle = (PFN_vkGetMemoryWin32HandleKHR)PFN_vkGetMemoryWin32HandleKHR(vkGetDeviceProcAddr(p_vkDevice, "vkGetMemoryWin32HandleKHR"));
    fct_vkGetMemoryWin32Handle(p_vkDevice, &win32_handle_info, &handle);

    PRINTLN("get_vulkan_memory_handle return: 0x%p", handle);

    DWORD info;
    bool ret = GetHandleInformation(handle, &info);
    PRINTLN("GetHandleInformation ret: %d Info: 0x%X", ret, info);

    return handle;
}

HANDLE get_vulkan_semaphore_handle(VkDevice& p_vkDevice, VkSemaphore& sempahore)
{
    HANDLE                           handle;

    VkSemaphoreGetWin32HandleInfoKHR win32_handle_info{};
    win32_handle_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR;
    win32_handle_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT;
    win32_handle_info.semaphore = sempahore;

    //vkGetSemaphoreWin32HandleKHR(p_vkDevice, &win32_handle_info, &handle);
    typedef VkResult(*fct_vkGetSemaphoreWin32HandleKHR_t)(VkDevice, const VkSemaphoreGetWin32HandleInfoKHR*, HANDLE*);
    fct_vkGetSemaphoreWin32HandleKHR_t fct_vkGetSemaphoreWin32HandleKHR = (fct_vkGetSemaphoreWin32HandleKHR_t)PFN_vkGetSemaphoreWin32HandleKHR(vkGetDeviceProcAddr(p_vkDevice, "vkGetSemaphoreWin32HandleKHR"));
    fct_vkGetSemaphoreWin32HandleKHR(p_vkDevice, &win32_handle_info, &handle);

    return handle;
}

#else

int get_vulkan_memory_handle(VkDevice& p_vkDevice, VkDeviceMemory memory)
{
    int fd;
    VkMemoryGetFdInfoKHR fd_info{};
    fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
    fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
    fd_info.memory = memory;

    //vkGetMemoryFdKHR(p_vkDevice, &fd_info, &fd);
    typedef VkResult(*fct_vkGetMemoryFdKHR_t)(VkDevice, const VkMemoryGetFdInfoKHR*, int*);
    fct_vkGetMemoryFdKHR_t fct_vkGetMemoryFdKHR = (fct_vkGetMemoryFdKHR_t)PFN_vkGetMemoryFdKHR(vkGetDeviceProcAddr(p_vkDevice, "vkGetMemoryFdKHR"));
    fct_vkGetMemoryFdKHR(p_vkDevice, &fd_info, &fd);

    PRINTLN("get_vulkan_memory_handle return: 0x%X", fd);

    return fd;
}

int get_vulkan_semaphore_handle(VkDevice& p_vkDevice, VkSemaphore& sempahore)
{
    int fd;
    VkSemaphoreGetFdInfoKHR fd_info{};
    fd_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
    fd_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
    fd_info.semaphore = sempahore;
    
    //vkGetSemaphoreFdKHR(p_vkDevice, &fd_info, &fd);
    typedef VkResult(*fct_vkGetSemaphoreFdKHR_t)(VkDevice, const VkSemaphoreGetFdInfoKHR*, int*);
    fct_vkGetSemaphoreFdKHR_t fct_vkGetSemaphoreFdKHR = (fct_vkGetSemaphoreFdKHR_t)PFN_vkGetSemaphoreFdKHR(vkGetDeviceProcAddr(p_vkDevice, "vkGetSemaphoreFdKHR"));
    fct_vkGetSemaphoreFdKHR(p_vkDevice, &fd_info, &fd);
    
    return fd;
}
#endif


VkExternalMemoryHandleTypeFlagBits GetFdType()
{
#if _WIN32
    return IsWindows8OrGreater() ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;
#else
    return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
#endif
}

On the other process, let say consumer / import side, here the related code:

#ifdef _WIN32
unsigned char* GetBufferExtFdMapped(const VkDevice* p_pDev, const VkPhysicalDevice* p_pPhyDev, HANDLE p_fd, size_t p_size)
#else
unsigned char* GetBufferExtFdMapped(const VkDevice* p_pDev, const VkPhysicalDevice* p_pPhyDev, int p_fd, size_t p_size)
#endif
{

    VkExternalMemoryBufferCreateInfo stVkExtMemBufCreateInfo = {};
    stVkExtMemBufCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO;
    stVkExtMemBufCreateInfo.handleTypes = GetFdType();

    // Example: Creating a buffer using the imported memory
    VkBufferCreateInfo bufferCreateInfo = {};
    bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferCreateInfo.size = p_size; // Size of the buffer
    bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
    bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 

    bufferCreateInfo.pNext = &stVkExtMemBufCreateInfo;

    VK_CHECK_RESULT(vkCreateBuffer(*p_pDev, &bufferCreateInfo, nullptr, &m_ExtFdBuffer));

    VkMemoryDedicatedAllocateInfo dedicated_info = {};
    dedicated_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
    dedicated_info.buffer = m_ExtFdBuffer;

#if _WIN32
    VkImportMemoryWin32HandleInfoKHR importMemoryOSSpecificHandleInfo = {};
    importMemoryOSSpecificHandleInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR;
    importMemoryOSSpecificHandleInfo.handle = p_fd;
#else

    VkImportMemoryFdInfoKHR importMemoryOSSpecificHandleInfo = {};
    importMemoryOSSpecificHandleInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
    importMemoryOSSpecificHandleInfo.fd = p_fd; // The file descriptor representing the memory
#endif
    importMemoryOSSpecificHandleInfo.handleType = GetFdType();

    importMemoryOSSpecificHandleInfo.pNext = &dedicated_info;


    VkMemoryAllocateInfo memoryAllocateInfo = {};
    memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    
    VkMemoryDedicatedRequirementsKHR dedicatedRequirements = {};
    dedicatedRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
    dedicatedRequirements.requiresDedicatedAllocation = VK_TRUE;

    VkMemoryRequirements2 memoryRequirements =
    {
        .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
        .pNext = &dedicatedRequirements,
    };

    const VkBufferMemoryRequirementsInfo2 bufferRequirementsInfo =
    {
        .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2,
        .pNext = NULL,
        .buffer = m_ExtFdBuffer
    };
    vkGetBufferMemoryRequirements2(*p_pDev, &bufferRequirementsInfo, &memoryRequirements);

    memoryAllocateInfo.pNext = &importMemoryOSSpecificHandleInfo;

    ////////////////////memoryAllocateInfo.allocationSize = p_size; // Set to the p_size of the memory you want to import

    memoryAllocateInfo.allocationSize = memoryRequirements.memoryRequirements.size;
    memoryAllocateInfo.memoryTypeIndex = findMemoryType(*p_pPhyDev, memoryRequirements.memoryRequirements.memoryTypeBits,
        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);


    m_ExtFdDeviceMemory = NULL;
    VK_CHECK_RESULT(vkAllocateMemory(*p_pDev, &memoryAllocateInfo, nullptr, &m_ExtFdDeviceMemory));
    if (m_ExtFdDeviceMemory)
    {
        // Bind the imported memory to the buffer
        VK_CHECK_RESULT(vkBindBufferMemory(*p_pDev, m_ExtFdBuffer, m_ExtFdDeviceMemory, 0));

        unsigned char* pMappedBuffer = NULL;
        VK_CHECK_RESULT_FATAL(vkMapMemory(*p_pDev,
            m_ExtFdDeviceMemory,
            0, VK_WHOLE_SIZE, 0,
            (void**)&pMappedBuffer));

        return pMappedBuffer;
    }
    else
    {
        vkDestroyBuffer(*p_pDev, m_ExtFdBuffer, nullptr);
        return NULL;
    }

}

Upvotes: 0

Views: 65

Answers (0)

Related Questions