Vulkan系列教程—VMA教程—快速上手VMA

Posted 赵新政

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vulkan系列教程—VMA教程—快速上手VMA相关的知识,希望对你有一定的参考价值。

文章目录


前言

VMA(Vulkan Memory Allocator),是AMD提供的Vulkan内存分配管理器,那么Vulkan的内存分配为何要使用VMA这种内存分配器呢?原因就在于其显存的分配次数是有限的(比如4096次),那么我们就需要分配一整块显存,然后自己使用offset以及size来进行分割使用,这个过程冗长繁杂,而且容易出错,那么VMA就成为了我们管理Vulkan内存的首要选择


提示:本文为系列系列教程,定时更新,请大家关注。如果需要深入学习Vulkan的同学,可以点击课程链接:

腾讯课堂:《Vulkan原理与实战—铸造渲染核武器—基石篇》

网易课堂:《Vulkan原理与实战—铸造渲染核武器—基石篇》

Vulkan学习群:594715138


# 一、工程安装

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函数对应的函数指针,在其中,指定下vkGetInstanceProcAddrvkGetDeviceProcAddr两个函数,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系列教程,下面给大家贴出链接。

腾讯课堂:《Vulkan原理与实战—铸造渲染核武器—基石篇》

网易课堂:《Vulkan原理与实战—铸造渲染核武器—基石篇》

以上是关于Vulkan系列教程—VMA教程—快速上手VMA的主要内容,如果未能解决你的问题,请参考以下文章

Vulkan系列教程—VMA教程—Debugging

Vulkan系列教程—VMA教程—Debugging

Vulkan系列教程—VMA教程—Debugging

Vulkan系列教程—VMA教程—用户数据

Vulkan系列教程—VMA教程—用户数据

Vulkan系列教程—VMA教程—用户数据