Vulkan系列教程—VMA教程(九)—虚拟分配器


前言

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

Vulkan学习群:594715138

CSDN课程链接《Vulkan原理与实战—铸造渲染核武器—基石篇》


Virtual Allocator

VMA有一个额外的特性,其核心的分配算法通过一套叫做“Virtual Allocator”来导出给用户使用。这个功能是说,通常使用VMA我们都碰不到Pool当中的内存块儿(blocks),但是现在我们可以独立一块block进行操作了。当然,这样的内存块你可以拿来做各种需求,甚至跟Vulkan无关。

一、生成Virtual Block

为了使用这个功能,VMA并没有给到我们一个Allocator这样的对象。我们所需要做的就是创建一个单独的VmaVirtualBlock来代表(管理)我们想创建的每一个内存块(block)。

  1. 填写 VmaVirtualBlockCreateInfo结构体。
  2. 调用 vmaCreateVirtualBlock()来创建新的VmaVirtualBlock对象。

代码如下(示例):

VmaVirtualBlockCreateInfo blockCreateInfo = {
    
    };
blockCreateInfo.size = 1048576; // 1 MB
 
VmaVirtualBlock block;
VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);

二、分配Virtual Allocation

VmaVirtualBlock这个结构体,内部会追踪其分配出去的内存(Occupied Regions)与空闲的内存(Free Region),它与VMA核心的Allocator使用的是同一套分配代码。当你需要分配一个Allocation的时候,传入一个VkDeviceSize参数引用,这个量会被用作Offset(相对于当前这个Block)。
为了创建这样一个Allocation,我们需要:

  1. 填写 VmaVirtualAllocationCreateInfo结构体。
  2. 调用 vmaVirtualAllocate()函数。从其中获得VkDeviceSize offset作为本Allocation的起点。

代码如下(示例):

VmaVirtualAllocationCreateInfo allocCreateInfo = {
    
    };
allocCreateInfo.size = 4096; // 4 KB
 
VkDeviceSize allocOffset;
res = vmaVirtualAllocate(block, &allocCreateInfo, &allocOffset);
if(res == VK_SUCCESS)
{
    
    
    // Use the 4 KB of your memory starting at allocOffset.
}
else
{
    
    
    // Allocation failed - no space for it could be found. Handle this error!
}

三、内存释放(Deallocation)

当Allocation不再需要的时候,我们可以调用vmaVirtualFree()来释放内存。你只能够将需要释放的Offset传入(这个Offset就代表了这块内存了)。

当整个Block都不再需要了,你可以通过调用vmaDestroyVirtualBlock()来释放整个Block。但是你必须在释放Block之前,先将每一个Allocation都释放掉。如果你不想一个个手动释放Allocation,那就需要调用 vmaClearVirtualBlock()来释放本Block当中的所有Allocation(这个特性在普通的VMA分配器中是没有的)。

代码如下(示例):

vmaVirtualFree(block, allocOffset);
vmaDestroyVirtualBlock(block)

四、分配携带参数(Allocation parameters)

你可以使用vmaSetVirtualAllocationUserData()来向已经分配的Allocation中设置用户参数。其默认的参数是null。与之前讲过的用户参数相同,它可以被传入各种各样的用户信息。

代码如下(示例):

struct CustomAllocData
{
    
    
    std::string m_AllocName;
};
CustomAllocData* allocData = new CustomAllocData();
allocData->m_AllocName = "My allocation 1";
vmaSetVirtualAllocationUserData(block, allocOffset, allocData);

这个指针可以在后面被获取到,通过调用 vmaGetVirtualAllocationInfo(),相关信息会被填写到 VmaVirtualAllocationInfo这个结构当中。注意!当你新new了一个对象,把它传进去作为用户参数,可别忘了在Allocation被释放后,手动释放自己的这块对象内存哦。

代码如下(示例):

VmaVirtualAllocationInfo allocInfo;
vmaGetVirtualAllocationInfo(block, allocOffset, &allocInfo);
delete (CustomAllocData*)allocInfo.pUserData;
 
vmaVirtualFree(block, allocOffset);

五、内存对齐与单位(Alignment and units)

使用Bytes(字节)来作为Size跟Offset的单位,看上去很自然。如果有一个Allocation,需要被对齐到一个数字上(比如4Byte),你可以使用一个可选参数来设置,即 VmaVirtualAllocationCreateInfo::alignment

代码如下(示例):

VmaVirtualAllocationCreateInfo allocCreateInfo = {
    
    };
allocCreateInfo.size = 4096; // 4 KB
allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B
 
VkDeviceSize allocOffset;
res = vmaVirtualAllocate(block, &allocCreateInfo, &allocOffset);

六、静态信息(Statistics)

你可以通过 vmaCalculateVirtualBlockStats()来获取一个Block的静态信息。这个函数会填充 VmaStatInfo这个结构体—就像VMA通常使用的一样。

代码如下(示例):


VmaStatInfo statInfo;
vmaCalculateVirtualBlockStats(block, &statInfo);
printf("My virtual block has %llu bytes used by %u virtual allocations\n",
    statInfo.usedBytes, statInfo.allocationCount);

你也可以通过vmaBuildVirtualBlockStatsString()获取到一个Block的所有信息,并且以JSON形式给到。拿到的String必须在后面通过 vmaFreeVirtualBlockStatsString()释放掉。

七、额外考量(Additional considerations)

Virtual Allocator”这一组功能,是在一个单独的Block上操作的。用户需要自己维护所有的Blocks,当一个Block不足够分配的时候还需要做新的Block,删除空白的Block以及决策一个新的Allocation分配到底优先选取哪一个Block。

系统也给到了一些可选的分配算法,就好像用户自定义内存池里面讲述的。你可以在 VmaVirtualBlockCreateFlagBits 里面找到各种Flags。你可以在Custom Memory Pool(用户自定义内存池)这一章找到算法解释。

总结

以上就是今天的内容,大家对于vulkan的学习,也可以参考我出品的vulkan系列教程,下面给大家贴出链接。
请大家移步CSDN站内进行学习:

CSDN课程链接《Vulkan原理与实战—铸造渲染核武器—基石篇》

猜你喜欢

转载自blog.csdn.net/weixin_50523841/article/details/122502705