内存需求

缓冲创建后实际还没有给它分配任何内存。分配缓冲内存前,我们需要调用vkGetBufferMemoryRequirements函数获取缓冲的内存需求。

VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, vertexBuffer, &memRequirements);

vkGetBufferMemoryRequirements函数返回的VkMemoryRequirements 结构体有下面这三个成员变量:

  • size:缓冲需要的内存的字节大小,它可能和bufferInfo.size的值不同。

  • alignment:缓冲在实际被分配的内存中的开始位置。它的值依赖于bufferInfo.usage和bufferInfo.flags。

  • memoryTypeBIts:指示适合该缓冲使用的内存类型的位域。

显卡可以分配不同类型的内存作为缓冲使用。不同类型的内存所允许进行的操作以及操作的效率有所不同。我们需要结合自己的需求选择最合适的内存类型使用。我们添加一个叫做findMemoryType的函数来做件事:

uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {

}

首先,我们使用vkGetPhysicalDeviceMemoryProperties函数查询物理设备可用的内存类型:

VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);

vkGetPhysicalDeviceMemoryProperties函数返回的VkPhysicalDeviceMemoryProperties结构体包含了memoryTypes和memoryHeaps两个数组成员变量。memoryHeaps数组成员变量中的每个元素是一种内存来源,比如显存以及显存用尽后的位于主内存种的交换空间。现在,我们暂时不关心这些内存的来源。

遍历数组,查找缓冲可用的内存类型:

for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
    if (typeFilter & (1 << i)) {
        return i;
    }
}

throw std::runtime_error("failed to find suitable memory type!");

typeFilter参数用于指定我们需要的内存类型的位域。我们只需要遍历可用内存类型数组,检测每个内存类型是否满足我们需要即可(相应位域为1)。

我们需要位域满足VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT(用于从CPU写入数据)和VK_MEMORY_PROPERTY_HOST_COHERENT_BIT(后面介绍原因)的内存类型。

修改代码,检测我们满足我们需要的内存类型:

for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
    if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
        return i;
    }
}

由于我们不只一个需要的内存属性,所以仅仅检测位与运算的结果是否非0是不够的,还需要检测它是否与我们需要的属性的位域完全相同。