前言
VMA(Vulkan Memory Allocator),是AMD提供的Vulkan内存分配管理器,那么Vulkan的内存分配为何要使用VMA这种内存分配器呢?原因就在于其显存的分配次数是有限的(比如4096次),那么我们就需要分配一整块显存,然后自己使用offset以及size来进行分割使用,这个过程冗长繁杂,而且容易出错,那么VMA就成为了我们管理Vulkan内存的首要选择
一、工程安装
Vulkan Memory Allocator属于单头文件的“stb-style”风格库。你不需要单独去构建它,只需要把头文件加入到工程里就好了。
“Single Header”并不意味着所有功能都已经作为了inline函数,而是说需要用预编译宏来导出这些函数,如果你不使用宏来导出函数就会遇到链接错误。
引入方法:
1 包含“ vk_mem_alloc.h ”头文件到任何一个用得到vma的cpp文件当中,这样vma的各种成员声明就可以完成了。
2 在其中一个cpp文件当中,开启如下的宏定义。
#define VMA_IMPLEMENTATION
#include "vk_mem_alloc.h"
对于使用者而言,可能单独创建一个cpp文件,专门用来书写上述代码,即专门用来导出VMA的成员实现(比如命名为vmaExporter.cpp)。
这个库的头文件里面已经包含了<vulkan/vulkan.h>,因此也间接包含了<windows.h>,在这两个头文件之前如果你需要定义一些宏来启用/关闭特性,那么请在vma头文件引入之前来定义。
对于VMA而言,库内部肯定会调用vulkan的函数,那么你的工程对于vulkan方面的配置就可以如下灵活处理:
1 默认情况下(推荐):
vma是认为你链接了vulkan的静态库(lib),则直接就调用vulkan函数,随后在编译期间就顺利链接(link),即无脑模式。
如果你不是用的vulkan静态库,就可以在vma头文件引入之前,做一个声明
#define VMA_STATIC_VULKAN_FUNCTIONS 0
然后就可以开启下面的引入方法了。
2 自动获取Vulkan函数:
你可以定义:
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1:
然后使用VmaAllocatorCreateinfo当中的pVulkanFunction这个成员变量,这个变量其实指向的是一个结构体:
typedef struct VmaVulkanFunctions
{
/// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr;
/// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr;
PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
//以下省略---------------------------------
}
这是vma定义的结构体,用来规定每一个Vulkan函数对应的函数指针,在其中,指定下vkGetInstanceProcAddr与vkGetDeviceProcAddr两个函数,vma就会自动把其他的vulkan函数给获取出来了。
3 全面赋值:
你甚至可以自己手动去填写上方的哪个结构体,直接为每一个vulkan的函数指定其函数指针。
二、初始化
在程序开始的时候:
1 初始化vulkan,生成VkPhysicalDevice,VkDevice与VkInstance等对象。
2 填写VmaAllocatorCreateInfo,随后使用vmaCreateAllocator创建一个VmaAllocator
VmaAllocatorCreateInfo allocatorInfo = {};
allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2;
allocatorInfo.physicalDevice = physicalDevice;
allocatorInfo.device = device;
allocatorInfo.instance = instance;
VmaAllocator allocator;
vmaCreateAllocator(&allocatorInfo, &allocator);
如上所示,你需要指定你使用的vulkan版本,当然你也可以通过VmaAllocatorCreateInfo::flags 指定你要开启的vma扩展(这个我们后面详细讲解),否则vma会默认使用vulkan的1.0内核并且无扩展开启。
三、资源分配小案例
当你想生成一个buffer或者image:
- 填写VkBufferCreateInfo/VkImageCreateInfo结构体;
- 填写VmaAllocationCreateInfo结构体。
- 调用vmaCreateBuffer/vmaCreateImage来获取内存,并且完成vkImage/vkBuffer与相应内存的绑定操作(Bind):
VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufferInfo.size = 65536;
bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
VkBuffer buffer;
VmaAllocation allocation;
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
别忘了在你不需要这块内存的时候,对它进行回收哦:
vmaDestroyBuffer(allocator, buffer, allocation);
vmaDestroyAllocator(allocator);
总结
以上就是今天的内容,大家对于vulkan的学习,也可以参考我出品的vulkan系列教程,下面给大家贴出链接。