文章目录
前言
本文为
Vulkan® Memory Allocator
系列系列教程,定时更新,请大家关注。如果需要深入学习Vulkan的同学,可以点击课程链接,学习链接
Vulkan学习群:594715138
CSDN课程链接
:
《Vulkan原理与实战—铸造渲染核武器—基石篇》
Virtual Allocator
VMA有一个额外的特性,其核心的分配算法通过一套叫做“Virtual Allocator”来导出给用户使用。这个功能是说,通常使用VMA我们都碰不到Pool当中的内存块儿(blocks),但是现在我们可以独立一块block进行操作了。当然,这样的内存块你可以拿来做各种需求,甚至跟Vulkan无关。
一、生成Virtual Block
为了使用这个功能,VMA并没有给到我们一个Allocator这样的对象。我们所需要做的就是创建一个单独的
VmaVirtualBlock
来代表(管理)我们想创建的每一个内存块(block)。
-
填写
VmaVirtualBlockCreateInfo
结构体。 -
调用
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,我们需要:
-
填写
VmaVirtualAllocationCreateInfo
结构体。 -
调用
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原理与实战—铸造渲染核武器—基石篇》