在 Vulkan 中,当我想将 GPU 的一些内存传输回 CPU 时,我认为最有效的方法是将数据写入具有标志的内存中VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT。
问题 1:这个假设正确吗?
(可用内存属性标志的完整列表可以在VkMemoryPropertyFlagBits 的Vulkan 文档中找到)
为了获取最新数据,我必须使用vkInvalidateMappedMemoryRanges使内存无效,对吗?
问题 2: 引擎盖下发生了vkInvalidateMappedMemoryRanges什么?这只是memcpy来自某个内部缓存还是可能是一个更长的过程?
问题 3:如果这可能需要更长的时间(即它不是一个简单的memcpy),那么我可能应该有一些可能与它的完成同步,对吗?但是,vkInvalidateMappedMemoryRanges不提供任何同步参数。实际上,我的问题是:如果我必须同步它,我该如何同步它?
问题 1:这个假设正确吗?
可能不是,但这取决于您的平台是否支持替代方案。对于 GPU->CPU 传输,实际上有三个选项:
此类型对主机可见并保证一致,但不会缓存在主机上。CPU 读取会非常慢,但是如果您只读回少量数据,这可能没问题(并且可能比发出 更便宜vkInvalidateMappedMemoryRanges(),并且如果您不希望再次接触它,那么将数据流式传输到 CPU 缓存中几乎没有意义CPU)。
此类型对主机可见并已缓存,但不能保证一致(如果您不手动强制执行一致性,CPU 和 GPU 可能会在同一地址看到不同的内容)。对于这种类型的内存,您必须vkInvalidateMappedMemoryRanges()在 GPU 写入之后和 CPU 读取之前(或vkFlushMappedRange()其他方向)使用,以确保一个处理器可以看到另一个写入的内容,否则您可能会读取过时的数据。
一旦进入缓存,数据访问将很快,您可以从 CPU 端数据获取技巧(例如显式预加载和缓存预取)中受益,但您将为无效操作支付开销。
最后,您有主机缓存和一致的内存类型,如果您要在 CPU 上进行高带宽读取,那么这两种类型都能为您提供最佳选择。硬件自动提供一致性实现,因此无需失效,但不能保证在所有平台上都可用。对于 CPU 上的批量数据读取,我希望在可用的情况下这是最有效的。
值得注意的是,所有分配都没有“最佳”内存设置。不要将主机缓存或主机一致性内存用于您从不希望传输回 CPU 的内容(内存一致性在功率或内存性能方面不是免费的)。
问题 2:在 vkInvalidateMappedMemoryRanges 期间发生了什么?这只是来自某个内部缓存的 memcpy 还是可能是一个更长的过程?
如果您有非连贯的记忆,那么它会做任何需要使它们连贯的事情。通常,这意味着使 CPU 缓存中的缓存行无效(丢弃),其中可能包含数据的陈旧副本,确保 CPU 的后续读取看到 GPU 实际写入的版本。
问题 #3:如果这可能需要更长的时间(即它不是简单的 memcpy),那么我可能应该有一些可能与它的完成同步,对吗?
不可以。Invalidation 是 CPU 端的操作,因此需要 CPU 时间才能完成,并且在操作完成时 CPU 很忙。通常,您可以通过使用相干内存来完全避免这样做的需要。