blog:2022:1105_vulkan_memory_allocator_support

NervLand: Adding support of Vulkan Memory Allocator

With my latest sessions on my VulkanApp lua experiment I'm finally starting to get back on rails with my vulkan 3D engine πŸ‘, and in the last post, we ended with a nice first triangle display, yeepee!

In this session, we will introduce the support for the Vulkan Memory Allocator library in our project, as managing the device memory is going to be a serious pain otherwise.

  • At first I thought I could place the vk_mem.alloc.h file right inside my nvVulkan src/ folder, but now I realize that this could mean some trouble with the NervBind layer trying to parse the content of that giant +700KB header file too… so I'm not even going to try it :-) β‡’ Let's move this header into an auxiliary include folder instead.
  • Arrff, I get a lot of warnings from clang in this new header file now 😞:
    D:/Projects/NervLand/sources/nvVulkan/vma\vk_mem_alloc.h:17132:5: warning: pointer is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified) [-Wnullability-completeness]
        VkBuffer* pBuffer,
        ^
    D:/Projects/NervLand/sources/nvVulkan/vma\vk_mem_alloc.h:17132:5: note: insert '_Nullable' if the pointer may be null
        VkBuffer* pBuffer,
        ^
                 _Nullable
    D:/Projects/NervLand/sources/nvVulkan/vma\vk_mem_alloc.h:17132:5: note: insert '_Nonnull' if the pointer should never be null
        VkBuffer* pBuffer,
        ^
  • β‡’ Just ignoring this clang warning for now with the content:
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wnullability-completeness"
    
    #define VMA_IMPLEMENTATION
    #include <vk_mem_alloc.h>
    
    #pragma clang diagnostic pop
  • OK, but actually after checking the build process witht he MSVC compiler I updated this code to be:
    #ifdef __clang__
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wnullability-completeness"
    #endif
    
    #define VMA_IMPLEMENTATION
    #include <vk_mem_alloc.h>
    
    #ifdef __clang__
    #pragma clang diagnostic pop
    #endif
    
  • Oh, and in the process, I also updated the C++ language level to c++20 in fact (as msvc was complaining about some init structure in VulkanEngine.cpp otherwise).
  • During the vulkan init process, we should create the vulkan instance, the physical device, and the logical device, and then we can initialize the VmaAllocator. So let's just create this as part of our VulkanDevice object.
  • So prepared the construction of the VMA allocator as follow:
        auto* pdev = get_physical_device();
        VmaAllocatorCreateInfo vmainfo{};
        vmainfo.flags = 0;
        vmainfo.physicalDevice = pdev->getVk();
        vmainfo.device = _device;
        vmainfo.instance = pdev->get_instance()->getVk();
        vmainfo.vulkanApiVersion = VK_API_VERSION_1_3;
    
        logDEBUG("Creating VMA allocator...");
        CHECK_VK_MSG(vmaCreateAllocator(&vmainfo, &_allocator),
                     "Cannot create VMA allocator.");
  • But that's not enough in my context since I'm using the dynamic loading of the vulkan functions, so I get the error:
    2022-11-04 20:12:55.771051 [DEBUG] Creating VMA allocator...
    Assertion failed: m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr && "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass " "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. " "Other members can be null.", file D:/Projects/NervLand/sources/nvVulkan/vma\vk_mem_alloc.h, line 14334
  • Let's now fix that:
        // Init the VmaAllocator:
        // cf.
        // https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/struct_vma_allocator_create_info.html
        auto* pdev = get_physical_device();
        auto* eng = VulkanEngine::instance();
    
        VmaAllocatorCreateInfo vmainfo{};
        VmaVulkanFunctions vkfuncs{};
        vkfuncs.vkGetInstanceProcAddr = eng->vkGetInstanceProcAddr;
        vkfuncs.vkGetDeviceProcAddr = inst->vkGetDeviceProcAddr;
    
        vmainfo.flags = 0;
        vmainfo.physicalDevice = pdev->getVk();
        vmainfo.device = _device;
        vmainfo.instance = inst->getVk();
        vmainfo.vulkanApiVersion = VK_API_VERSION_1_3;
        vmainfo.pVulkanFunctions = &vkfuncs;
    
        logTRACE("Creating VMA allocator...");
        CHECK_VK_MSG(vmaCreateAllocator(&vmainfo, &_allocator),
                     "Cannot create VMA allocator.");
  • And we should also update the include of the main header accordingly:
    #define VMA_STATIC_VULKAN_FUNCTIONS 0
    #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
    #include <vk_mem_alloc.h>
    
  • β‡’ And now creating/destroying the VMA allocator seems to work as expected.
  • Next step is to update our creation of images to use the VMA allocator:
        // Use the VMA allocator to create this resource:
        VmaAllocationCreateInfo allocInfo = {};
        allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
        auto* allocator = _device->get_vma_allocator();
        CHECK_VK_MSG(vmaCreateImage(allocator, &image_create_info, &allocInfo,
                                    &_image, &_allocation, nullptr),
                     "Cannot create image");
  • Note: We also have to stop binding memory for the depth images in the lua app (or we get validation errors of course):
            local depthImg = self.vkeng:create_image_2d(self.width, self.height, vk.Format.D32_SFLOAT,
                vk.ImageUsageFlagBits.DEPTH_STENCIL_ATTACHMENT_BIT)
            -- logDEBUG("Depth image idx: ", depthImg)
            -- self:allocate_image_memory(depthImg)
  • Then introduced the same kind of changes for the creation of buffers:
        // Use the VMA allocator to create this resource:
        VmaAllocationCreateInfo allocInfo = {};
        allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
        auto* allocator = _device->get_vma_allocator();
        CHECK_VK_MSG(vmaCreateBuffer(allocator, &buffer_create_info, &allocInfo,
                                     &_buffer, &_allocation, nullptr),
                     "Cannot create buffer");
    
  • And that's it! Nothing too tricky here in the end, so let's move on.
Of course, we are releasing all our resources above with corresponding calls to vmaDestroyImage, vmaDestroyBuffer and vmaDestroyAllocator
  • blog/2022/1105_vulkan_memory_allocator_support.txt
  • Last modified: 2022/11/04 21:14
  • by 127.0.0.1