将数据从缓冲区复制到图像
译者注:示例代码点击此处
对于图像,我们可以绑定从不同内存类型分配的内存对象。但我们只能从的应用程序直接映射和更新主机可见内存。当想要更新使用设备本地内存的图像内存时。我们需要从缓冲区中复制数据。
怎么做...
1.获取命令缓冲区的句柄并将其存储在名为command_buffer的VkCommandBuffer类型变量中。确保命令缓冲区已处于记录状态(请参阅第3章,命令缓冲区和同步中的开始命令缓冲区记录操作内容)。
2.获取将从中复制数据的缓冲区。将其句柄存储在名为source_buffer的VkBuffer类型变量中。
3.获取要复制数据的图像。使用名为destination_image的VkImage类型的变量表示此图像。
4.创建一个名为image_layout的VkImageLayout类型的变量,其中将存储图像的当前布局。
5.创建名为regions的std::vector<VkBufferImageCopy>类型变量。元素数量是我们希望复制的所有区域数量。为每个元素成员指定以下值:
·bufferOffset从缓冲区内存的起始处偏移。
·bufferRowLength表示缓冲区单行的数据长度,如果数据紧密打包(根据目标图像大小)则为0值。
·bufferImageHeight表示储存在缓冲区中的虚构图像的高度,如果缓冲区的数据紧密打包(根据目标图像大小)则0值。
·使用一下值初始化imageSubresource成员:
·aspectMask为图像方面(颜色,深度或模板)
·mipLevel为更新的mipmap级别的编号(索引)
·baseArrayLayer为更新的第一个数组层的编号
·layerCount为更新的数组层数
·imageOffset为更新的图像子区域的初始化偏移(以纹素为单位)
·imageExtent为图像的大小(尺寸)
6.调用vkCmdCopyBufferToImage( command_buffer, source_buffer, destination_image, image_layout, static_cast<uint32_t>(regions.size()), ®ions[0] )。使用command_buffer、source_buffer、destination_image、image_layout变量、regions向量中的元素数量和指向它的第一个元素的指针。
这个怎么运作...
在缓冲区和图像之间复制数据是通过命令缓冲区完成的,如下操作:
if( regions.size() > 0 ) {
vkCmdCopyBufferToImage( command_buffer, source_buffer, destination_image, image_layout, static_cast<uint32_t>(regions.size()), regions.data() );
}
我们需要知道图像数据如何在缓冲区内布局,以便正确上传图像的内存。需要提供一个内存偏移量(从缓冲区内存的开头)、数据行的长度以及缓冲区中的数据高度。这准许驱动正确地寻址储存器并将缓冲区的内存复制到图像中。我们还可以为长度和高度提供0,这意味着缓冲区包含紧密打包的数据,它对应于目标图像的尺寸。
我们还需要提供有关数据传输操作目标的信息。这涉及定义x,y和z维度的图像原点(从纹理的左上角偏移),数据将被复制到的mipmap级别和数组的基础层以及将要覆盖的层数、还需指定目标图像的尺寸。
所有前面的参数都是通过VkBufferImageCopy元素数组指定的。我们可以同时提供许多区域并复制不连续的内存范围。
在具有由物理设备公开的多种不同内存类型的硬件体系系统结构上,建议对呈现期间使用的资源使用设备本地专用内存。这种内存通常比主机可见内存更快。主机可见内存应该只用于暂存资源,这些资源用于CPU(我们的应用程序)上传数据或下载数据。
在只有一个内存类型(设备本地和主机可见)的体系结构中,我们不需要为数据上传而烦恼中间阶段资源。但是,所提出的方法仍然有效,并且可以跨不同的执行环境统一应用程序的行为,这可以使我们的应用程序的维护更容易。
在这两种情况下,可以使用它来进行设备-本地内存传输数据,设备-本地内存(通常)无法映射,这是通过本节内容中的描述的复制操作实现的。我们都可以轻松转移残存内存的资源并在应用程序中访问它。
提示:必须使用VK_BUFFER_USAGE_TRANSFER_SRC_BIT用法创建可以复制数据的缓冲区。
必须使用VK_BUFFER_USAGE_TRANSFER_DST_BIT用法创建我们传输数据的图像。在传输操作之前还需要将图像布局转换为VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL。
在我们将数据传输到图像之前,必须改变其内存布局,只能将数据复制到当前内存布局为VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL的图像。我们也可以使用VK_IMAGE_LAYOUT_GENERAL布局,但由于性能较低,不建议使用。
因此,在我们将数据复制到图像之前,应该设置一个内存屏障,将图像的内存访问类型应从当前进行的类型更改为VK_ACCESS_TRANSFER_WRITE_BIT。还应执行从当前布局到VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL布局的布局转换。在完成将数据复制到图像并且完成需要做的目的之后,我们应该设置另一个内存屏障,这次,我们应该从内存访问类型从VK_ACCESS_TRANSFER_WRITE_BIT更改为与使用图像目的相对应的访问类型。还应该将图像布局从VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL转换为与图像的下一次使用的兼容布局(请参阅设置图像内存屏障内容)。没有这些屏障,不仅数据传输操作可能无效,而且数据可能在图像上执行的其他操作中不可见。
如果作为数据源的缓冲区用于其他目的,我们还应为其设置内存屏障,并在传输操作之前和之后执行类似的内存访问类型更改。但由于缓冲区是数据源,我们在第一个屏障中设置VK_ACCESS_TRANSFER_READ_BIT访问类型。它可以用改变图像参数的相同管线屏障来完成。有关更多详细信息请参阅设置缓冲区内存屏障内容。