KhronosGroup / Vulkan-Hpp

Open-Source Vulkan C++ API
Apache License 2.0
3.13k stars 307 forks source link

Question: VulkanHpp VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC usage with VulkanMemoryAllocator #1879

Closed caiovpsilveira closed 5 months ago

caiovpsilveira commented 5 months ago

Hi, I have a question about the usage of VulkanMemoryAllocator with the DispatchLoaderDynamic. I'm using the default dispatcher.

From the VMA documentation, it mentions that there are three ways of Importing Vulkan functions. In my understanding the DispatchLoaderDynamic behaves exactly like Volk, so I tried using the third method.

I tried the following:

#define VK_NO_PROTOTYPES // which also sets the DISPATCH_LOADER_DINAMIC to default
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#define VMA_IMPLEMENTATION

#include <vk_mem_alloc.h>
#include <vulkan/vulkan.hpp>

VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
VULKAN_HPP_DEFAULT_DISPATCHER.init();
...
m_instance = vk::createInstance(instanceCreateInfo);
VULKAN_HPP_DEFAULT_DISPATCHER.init(m_instance);
...
m_device = m_physicalDevice.createDevice(deviceCreateInfo);
VULKAN_HPP_DEFAULT_DISPATCHER.init(m_device);

Then, when creating the VmaVulkanFuncs, I populated all members of the structure:

    const auto& d = VULKAN_HPP_DEFAULT_DISPATCHER;
    VmaVulkanFunctions vulkanFunctions {.vkGetInstanceProcAddr = d.vkGetInstanceProcAddr,
                                        .vkGetDeviceProcAddr = d.vkGetDeviceProcAddr,
                                        .vkGetPhysicalDeviceProperties = d.vkGetPhysicalDeviceProperties,
                                        .vkGetPhysicalDeviceMemoryProperties = d.vkGetPhysicalDeviceMemoryProperties,
                                        .vkAllocateMemory = d.vkAllocateMemory,
                                        .vkFreeMemory = d.vkFreeMemory,
                                        .vkMapMemory = d.vkMapMemory,
                                        .vkUnmapMemory = d.vkUnmapMemory,
                                        .vkFlushMappedMemoryRanges = d.vkFlushMappedMemoryRanges,
                                        .vkInvalidateMappedMemoryRanges = d.vkInvalidateMappedMemoryRanges,
                                        .vkBindBufferMemory = d.vkBindBufferMemory,
                                        .vkBindImageMemory = d.vkBindImageMemory,
                                        .vkGetBufferMemoryRequirements = d.vkGetBufferMemoryRequirements,
                                        .vkGetImageMemoryRequirements = d.vkGetImageMemoryRequirements,
                                        .vkCreateBuffer = d.vkCreateBuffer,
                                        .vkDestroyBuffer = d.vkDestroyBuffer,
                                        .vkCreateImage = d.vkCreateImage,
                                        .vkDestroyImage = d.vkDestroyImage,
                                        .vkCmdCopyBuffer = d.vkCmdCopyBuffer,
                                        .vkGetBufferMemoryRequirements2KHR = d.vkGetBufferMemoryRequirements2KHR,
                                        .vkGetImageMemoryRequirements2KHR = d.vkGetImageMemoryRequirements2KHR,
                                        .vkBindBufferMemory2KHR = d.vkBindBufferMemory2KHR,
                                        .vkBindImageMemory2KHR = d.vkBindImageMemory2KHR,
                                        .vkGetPhysicalDeviceMemoryProperties2KHR =
                                            d.vkGetPhysicalDeviceMemoryProperties2KHR,
                                        .vkGetDeviceBufferMemoryRequirements = d.vkGetDeviceBufferMemoryRequirements,
                                        .vkGetDeviceImageMemoryRequirements = d.vkGetDeviceImageMemoryRequirements};

VmaAllocatorCreateInfo allocatorCreateInfo {};
allocatorCreateInfo.physicalDevice = m_physicalDevice, allocatorCreateInfo.device = m_device,
allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
allocatorCreateInfo.instance = m_instance, allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3;

VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &m_allocator);

However, when creating the vmaAllocator, I get the following assertion:

/usr/include/vk_mem_alloc.h:13161: void VmaAllocator_T::ValidateVulkanFunctions(): Assertion `m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != nullptr' failed.
Aborted

This is not the first function pointer checked, so the others must be valid retrieved from the dispatcher. My question is what is happening?

If, instead, I swap to using the second method pointed in the VMA documentation:

#define VK_NO_PROTOTYPES
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1   // why? Shouldn't Vulkan HPP default dispatcher be able to load all ptrs?
#define VMA_IMPLEMENTATION
VULKAN_HPP_DEFAULT_DISPATCHER.init();
...
m_instance = vk::createInstance(instanceCreateInfo);
VULKAN_HPP_DEFAULT_DISPATCHER.init(m_instance);
...
m_device = m_physicalDevice.createDevice(deviceCreateInfo);
VULKAN_HPP_DEFAULT_DISPATCHER.init(m_device);
...
const auto& d = VULKAN_HPP_DEFAULT_DISPATCHER;
VmaVulkanFunctions vulkanFunctions {};
vulkanFunctions.vkGetInstanceProcAddr = d.vkGetInstanceProcAddr;
vulkanFunctions.vkGetDeviceProcAddr = d.vkGetDeviceProcAddr;

VmaAllocatorCreateInfo allocatorCreateInfo {};
allocatorCreateInfo.physicalDevice = m_physicalDevice, allocatorCreateInfo.device = m_device,
allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
allocatorCreateInfo.instance = m_instance, allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3;

VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &m_allocator);

Then the assertion is not triggered, I don't know the details of how VMA handles it internally.

Am I doing something wrong, or it this an issue with the VMA or what? I'm confused. In the second case there both the VMA and the DispatchLoaderDynamic are fetching the function ptrs? Isn't this redundant?

caiovpsilveira commented 5 months ago

Fixed.

With tr3v1n comment on r/Vulkan:

If you want to use an extension that has been promoted in the version of Vulkan you are using, you have to drop the KHR bit from it. The dynamic dispatcher does this, and the promoted version's function pointer will have the extension version if you load the extension on older versions of Vulkan as a fallback. VMA takes a similar approach when it does the function pointer loading, too.