Vulkan Cookbook 第四章 2 为缓冲区分配和绑定内存对象

为缓冲区分配和绑定内存对象

译者注:示例代码点击此处

在Vulkan中缓冲区没有自己的内存。为了能够在我们的应用程序中使用缓冲区并在里面储存数据,需要分配一个内存对象并将其绑定都一个缓冲区。

怎么做...

1.获取创建逻辑设备的物理设备句柄。将其存储在名为physical_device的VkPhysicalDevice类型变量中。
2.创建名为physical_device_memory_properties的VkPhysicalDeviceMemoryProperties类型变量。
3.调用vkGetPhysicalDeviceMemoryProperties( physical_device, &physical_device_memory_properties ),为其提供物理设备句柄和指向VkPhysicalDeviceMemoryProperties变量的指针。此调用将储存物理的内存参数(堆数,大小和类型)。
4.获取从物理设备创建的逻辑设备句柄。将句柄储存在名为logical_device的VkDevice类型变量中。
5.获取由名为buffer的VkBuffer类型变量表示的已创建缓冲区句柄。
6.创建名为memory_requirements的VkMemoryRequirements类型变量。
7.获取需要用于缓冲区的内存参数。通过调用vkGetBufferMemoryRequirements( logical_device, buffer, &memory_requirements ),并在第一个参数中提供逻辑设备句柄,在第二个参数中提供创建的缓冲区句柄,以及第三个参数指向memory_requirements变量的指针。
8.创建一个名为memory_object的VkDeviceMemory类型变量,它将表示创建的缓冲区的内存对象,并为其分配VK_NULL_HANDLE值。
9.创建名为memory_properties的VkMemoryPropertyFlagBits类型变量。存储额外选择。
10.遍历由physical_device_memory_properties变量的memoryTypeCount成员表示的可用物理设备的内存类型。对于遍历的每个成员请执行一下步骤:
    1.确保memory_requirements的memoryTypeBits设置了由名为type变量表示的标记。
    2.确保memory_properties变量在physical_device_memory_properties变量的memoryTypes数组的索引类型处具有内存类型propertyFlags成员相同的位。
    3.如果第一点和第二点不成立,则继续迭代循环。
    4.创建名为buffer_memory_allocate_info的VkMemoryAllocateInfo类型变量,并为其成员分配以下值:
      ·sType为VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
      ·pNext为nullptr
      ·allocationSize为memory_requirements.size
      ·memoryTypeIndex为type
    5.调用vkAllocateMemory( logical_device, &buffer_memory_allocate_info, nullptr, &memory_object ),为此提供逻辑设备句柄、指向buffer_memory_allocate_info变量的指针、nullptr值以及指向memory_object变量的指针。
    6.通过检查调用返回的值是否等于VK_SUCCESS确保调用成功,并停止迭代循环。
11.通过检查memory_object变量是否不等于VK_NULL_HANDLE,确保循环内的内存对象分配成功。
12.调用vkBindBufferMemory( logical_device, buffer, memory_object, 0),为其提供逻辑logical_device、buffer、memory_object和0值。
13.检查返回值等于VK_SUCCESS,确保调用成功。

这个怎么运作...

要为缓冲区(或一般的内存对象)分配内存对象,我们需要知道给定物理设备上可用的内存类型,以及有多少内存。这是通过调用vkGetPhysicalDeviceMemoryProperties()函数来完成的如下所示:

VkPhysicalDeviceMemoryProperties physical_device_memory_properties; 
vkGetPhysicalDeviceMemoryProperties( physical_device, &physical_device_memory_properties );

接下来,我们需要知道给定缓冲区需要多少内存空间(内存可能需要大于缓冲区的大小)以及与之兼容的内存类型。所有这些都存在VkMemoryRequirements类型的变量中。

VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements( logical_device, buffer, &memory_requirements );

接下来我们需要检查哪个内存类型对应缓冲区的内存要求:

memory_object = VK_NULL_HANDLE;
for( uint32_t type = 0; type < physical_device_memory_properties.memoryTypeCount; ++type ) {
    if( (memory_requirements.memoryTypeBits & (1 << type)) &&
      ((physical_device_memory_properties.memoryTypes[type].propertyFlags & memory_properties) == memory_properties) ) {

        VkMemoryAllocateInfo buffer_memory_allocate_info = {
            VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,   // VkStructureType    sType
            nullptr,                                  // const void       * pNext
            memory_requirements.size,                 // VkDeviceSize       allocationSize
            type                                      // uint32_t           memoryTypeIndex
        };

        VkResult result = vkAllocateMemory( logical_device, &buffer_memory_allocate_info, nullptr, &memory_object );
        if( VK_SUCCESS == result ) {
            break;
        }
    }
}

在这里,我们迭代所有可用的内存类型,并检查给定的类型是否可以用于我们的缓冲区。我们还可以请求一些需要满足的额外内存属性。 例如如果想直接从我们的应用程序(从CPU)上传数据,则必须支持内存映射在这种情况下,我们需要使用主机可见(host-visible)的内存类型。

译者注:要注意的是缓冲区(buffer)是通过逻辑设备级函数创建的,我们通过逻辑设备再次询问缓冲区的内存需求,然后要跟特定物理设备所支持的内存属性相匹配。通过上面的代码和解释分析来看,该物理设备必须是创建了该缓冲区的逻辑设备的物理设备。既然该物理设备必然是创建特定逻辑设备的那个物理设备,我之前想可能可以直接通过逻辑设备获取内存相关的属性,然后再和缓冲区的内存需求做比较,后来发现貌似并不可以,看来只能通过物理设备获取内存的相关属性再和buffer需求进行比较,这样做应该是为了性能考虑。因为我们不需要每次绑定缓冲区的时候都去调用查询内存属性,而是在获取初始化物理设备的时候就保存了其内存属性的相关信息。

当我们找到合适的内存类型时,可以使用它来分配内存对象,停止循环之后确保正确分配内存,接下来将它绑定到我们的缓冲区:

if( VK_NULL_HANDLE == memory_object ) {
  std::cout << "Could not allocate memory for a buffer." << std::endl; 
  return false;
}

VkResult result = vkBindBufferMemory( logical_device, buffer, memory_object, 0 ); 
if( VK_SUCCESS != result ) {
  std::cout << "Could not bind memory object to a buffer." << std::endl;
  return false; 
}
return true;

在绑定期间,我们制定内存偏移量以及其他参数。这准许我们绑定不在内存对象开头的内存的一部分。可以(并且应该)使用offset参数将内存对象的多个独立部分绑定到多个缓冲区。
从现在开始,缓冲区可以在我们的应用程序中使用。

还有更多...

此节内容显示如何将内存对象分配和绑定到缓冲区。但一般来说,我们不应该为每个缓冲区使用单独的内存对象。应该分配更大的内存对象,并将它们的一部分用于多个缓冲区。

在本文中,我们还通过调用vkGetPhysicalDeviceMemoryProperties()函数获取了物理设备可用内存类型的参数。但总的来说,为了提高应用程序的性能,不需要每次想要分配内存对象时都调用它。在我们选择了将用于逻辑设备的物理设备(请参阅第一章实例与设备中的创建逻辑设备),我们只需要调用此函数一次。

猜你喜欢

转载自blog.csdn.net/qq_19473837/article/details/84642499
今日推荐