Vulkan Cookbook 第四章 11 映射、更新和取消映射主机可见内存

映射、更新和取消映射主机可见内存

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

对于渲染期间使用的图像和缓冲区,建议绑定位于图像硬件(设备本地内存)上的内存。这会产生最好的表现。但我们不能直接访问这样的内存,需要使用中间(暂存)资源来调节GPU(设备)和CPU(主机)之间的数据传输。

另一方面,暂存资源需要使用主机可见的内存。要将数据上传到此类内存或从中读取数据,我们需要对其进行映射。

怎么做...

1.获取已创建的逻辑设备句柄并将其存储在名为logical_device的VkDevice类型变量中。
2.选择在具有VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT属性的内存类型上分配的内存对象。将内存对象的句柄储存在名为memory_object的VkDeviceMemory类型变量中。
3.选择应映射和更新的内存区域。将内存对象的内存开头的偏移量(以字节为单位)存储在名为offset的VkDeviceSize类型变量中。
4.选择要复制到内存对象的选定区域的数据大小。使用名为data_size的VkDeviceSize类型变量表示数据大小。
5.准备应该复制到内存对象的数据。并使用它来初始名为data的void*类型的变量。
6.创建一个名为pointer的void *类型型变量。它将包含指向映射内存范围的指针。
7.调用vkMapMemory( logical_device, memory_object, offset, data_size, 0, &local_pointer )进行内存映射,提供逻辑设备句柄和内存对象的句柄,偏离内存的起始点和我们想要映射的区域的大小(以字节为单位),0值和指向local_pointer变量的指针。
8.检查返回的值是否等于VK_SUCCESS来确保调用成功。
9.将准备好的数据复制到指针变量指向的内存中。它可以通过以下调用完成:std::memcpy( local_pointer, data, data_size )。
10.创建一个名为memory_ranges的std::vector<VkMappedMemoryRange>类型变量。向每个元素初始化一下值:
    ·sType为VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE
    ·pNext为nullptr
    ·memory为memory_object
    ·offset为每个范围的偏移量
    ·size为每个范围的尺寸
11.通知驱动程序内存的哪些部分已更改。通过调用vkFlushMappedMemoryRanges( logical_device, static_cast<uint32_t>(memory_ranges.size()), &memory_ranges[0] )来完成此操作,为其提供logical_device变量、要更改内存的范围数量(memory_ranges向量中的元素数量)和指向memory_ranges向量的第一个元素的指针。
12.确保调用返回VK_SUCCESS值刷新成功。
13.要取消映射内存,请调用vkUnmapMemory( logical_device, memory_object )。

这个怎么运作...

映射内存是将数据上载到Vulkan资源的最简单方法。在映射期间我们指定应该映射哪个内存部分(从内存对象的开头偏移和映射范围的大小):

VkResult result;
void * local_pointer;
result = vkMapMemory( logical_device, memory_object, offset, data_size, 0, &local_pointer ); 
if( VK_SUCCESS != result ) {
  std::cout << "Could not map memory object." << std::endl;
  return false; 
}

映射为我们提供了指向所请求的内存部分的指针。我们可以使用这个指针,就像在典型的C++应用程序中使用的其他指针一样。是否从这样的内存写入或读取数据没有限制。下面将数据从应用程序赋值到内存对象:

std::memcpy( local_pointer, data, data_size );

当我们更新映射的内存范围时,需要通知驱动程序内存内容已被修改,上传的数据可能不会立即对提交给队列的其他操作可见。通知CPU(主机)执行的内存数据修改称为刷新。为此我们准备了一个更新的内存范围列表,不需要覆盖整个映射内存:


std::vector<VkMappedMemoryRange> memory_ranges = { 
  {
  VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 
  nullptr,
  memory_object,
  offset,
  data_size
  } 
};

vkFlushMappedMemoryRanges( logical_device, static_cast<uint32_t>(memory_ranges.size()), &memory_ranges[0] ); 
if( VK_SUCCESS != result ) {
  std::cout << "Could not flush mapped memory." << std::endl;
  return false; 
}

当我们完成处理映射内存之后,可以取消映射它。内存映射不应该影响我们应用程序的性能,可以在我们的应用程序的整个生命周期中保留获取的指针。但是我们应该在关闭应用程序并销毁所有资源之释放它(unmap):

if( unmap ) {
  vkUnmapMemory( logical_device, memory_object );
} else if( nullptr != pointer ) { 
  *pointer = local_pointer;
}

return true;

猜你喜欢

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