2020/10/14 在文章最后增加了一条Vulkan内存分配信息
2020/10/15 修改原先说明当使用Vulkan 1.2时VMA的行为
2020/10/20 修改结构图,增加VmaDeviceMemoryBlock、VmaBlockMetadata和VmaBlockMetadata_Generic类的结构图
目前正在使用Vulkan搭建引擎,目前处于起步阶段,打算写得差不多了开源出来。当设计VkBuffer、VkImage和VkMemory时当时考虑到自己写一个内存管理分配器,手里也没有什么好的参考材料,查阅Vulkan资料时偶然之间查阅到AMD公司有一个开源的Vulkan内存管理项目:Vulkan® Memory Allocator (VMA)就打算研究研究并将其加入到引擎中,顺便将研究结果分享一下,目前还在同步研究中。
目前VMA并没有包括Vulkan中所有的内存相关操作,比如Transfer,导入导出外部内存等。VMA实现了最常见的内存相关管理。
VMA的在线文档可能要梯子(我是没打开),你可以从VMA的Github中获取文档。
VMA文档说最新版本的VMA支持Vulkan1.0和Vulkan 1.1,我看了一下源码Vulkan1.2也行(内部用的Vulkan1.1,Vulkan1.2内存标准没啥变化)
说明:
1.(修改“Vulkan1.2内存标准没啥变化”这句话)当使用Vulkan 1.2时在创建 VmaAllocator 时可以使用 VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT 用于指定VMA内部获取设备内存地址。VMA内部其实是使用在低于Vulkan 1.1版本为扩展而Vulkan 1.2中成为核心标准的 VK_KHR_buffer_device_address扩展,Vulkan 1.2 标准中为调用如下函数获取:
// Provided by VK_VERSION_1_2
uint64_t vkGetDeviceMemoryOpaqueCaptureAddress(
VkDevice device,
const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo
);
如果在创建VmaAllocator时传入的 VmaAllocatorCreateInfo::vulkanApiVersion 为Vulkan 1.2 版本的话,如果你使用的Vulkan版本小于Vulkan 1.2的话(该版本为vulkan_core.h中声明的Vulkan版本),不出意外会收到异常提示框,该异常的断言在VmaAllocator的构造函数内定义如下:
#if VMA_VULKAN_VERSION < 1002000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
{
VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
}
#endif
VMA_VULKAN_VERSION 的声明如下:
#if !defined(VMA_VULKAN_VERSION)
#if defined(VK_VERSION_1_2)
#define VMA_VULKAN_VERSION 1002000
#elif defined(VK_VERSION_1_1)
#define VMA_VULKAN_VERSION 1001000
#else
#define VMA_VULKAN_VERSION 1000000
#endif
#endif
相关资料:
AMD 还写了DX12的内存管理项目(真贴心)
D3D12 Memory Allocator (D3D12MA)
由于内存管理算是一个复杂的问题,如果感觉自己对于内存分配之类的掌握比较薄弱的话推荐先看一遍侯捷 C++内存管理 从平地到万丈高楼。有关很多内存管理共性,比如union、定点内存分配之类的话题讲的很清楚(不得不说侯捷老师真是太牛逼了)。
目前画了个结构图,仅供参考。
- VkDeviceMemory在VmaDeviceMemoryBlock中
- 在VMA中要进行内存分配(GPU端的分配),在经过一系列分配筛选后最后会落入如下函数中
VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
{
/*...*/
// VULKAN CALL vkAllocateMemory.
VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
/*...*/
}