首页 > 解决方案 > 使用 Vulkan 和 vkInvalidateMappedMemoryRanges 同步将内存从 GPU 传输到 CPU?

问题描述

在 Vulkan 中,当我想将 GPU 的一些内存传输回 CPU 时,我认为最有效的方法是将数据写入具有 flags 的内存中VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT

问题1:这个假设正确吗?

(可用内存属性标志的完整列表可以在 Vulkan 的VkMemoryPropertyFlagBits文档中找到)

为了获取最新数据,我必须使用vkInvalidateMappedMemoryRanges使内存无效,对吗?

问题 #2: 期间发生了什么vkInvalidateMappedMemoryRanges?这只是memcpy来自一些内部缓存还是可能是一个更长的过程?

问题#3:如果这可能需要更长的时间(即它不是一个简单的memcpy),那么我可能应该有一些可能与它的完成同步,对吧?但是,vkInvalidateMappedMemoryRanges不提供任何同步参数。实际上,我的问题是:如果我必须同步它,我该如何同步它?

标签: vulkan

解决方案


问题1:这个假设正确吗?

可能不是,但这取决于您的平台是否支持替代方案。对于 GPU->CPU 传输,实际上有三个选项:

1. HOST_VISIBLE

这种类型对主机可见并保证是一致的,但不会缓存在主机上。CPU 读取将非常慢,但如果您只读取少量数据,这可能没问题(并且可能比发出便宜vkInvalidateMappedMemoryRanges(),并且如果您从不希望再次触摸它,则几乎没有将数据流式传输到 CPU 缓存中中央处理器)。

2. HOST_VISIBLE | 主机缓存

这种类型对主机可见并被缓存,但不能保证是一致的(如果你不手动强制一致性,CPU 和 GPU 可能会在同一地址看到不同的东西)。对于这种类型的内存,您必须vkInvalidateMappedMemoryRanges()在 GPU 写入之后和 CPU 读取之前(或vkFlushMappedRange()用于其他方向)使用,以确保一个处理器可以看到另一个处理器写入的内容,否则您可能会读取过时的数据。

一旦进入缓存,数据访问将很快,您可以从 CPU 端数据获取技巧(例如显式预加载和缓存预取)中受益,但您将为无效操作支付开销。

3. HOST_VISIBLE | 主机缓存 | HOST_COHERENT

最后,您拥有主机缓存和连贯的内存类型,如果您要在 CPU 上进行高带宽读取,那么这两种类型都可以为您提供最好的选择。硬件自动提供一致性实现,因此无需无效,但不能保证在所有平台上都可用。对于 CPU 上的批量数据读取,我希望这在可用的情况下是最有效的。

值得注意的是,所有分配都没有“最佳”内存设置。不要将主机缓存或主机一致性内存用于您从未期望传输回 CPU 的内容(内存一致性在功率或内存性能方面并不是免费的)。

问题 #2:在 vkInvalidateMappedMemoryRanges 期间发生了什么?这只是来自某些内部缓存的 memcpy 还是可能是一个更长的过程?

如果您有不连贯的记忆,那么它会尽一切努力使它们连贯。通常,这意味着使 CPU 缓存中可能包含数据的陈旧副本的缓存行无效(丢弃),以确保 CPU 的后续读取看到 GPU 实际写入的版本。

问题#3:如果这可能需要更长的时间(即它不是一个简单的memcpy),那么我可能应该有一些可能与它的完成同步,对吧?

不可以。失效是 CPU 端的操作,因此需要 CPU 时间才能完成,并且在操作完成时 CPU 很忙。通常,您可以通过使用连贯内存来完全避免这样做。


推荐阅读