我可以随意重用主机缓冲区内存吗?还是应该每帧重新映射它



我的应用程序有一个VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT缓冲区和一个将内存上传到VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT缓冲区的永久命令缓冲区。

关于这个设置,我有两个问题。这是问题1,问题2是单独的。


为了获得更好的性能,资源(缓冲区、缓冲存储器、内存映射、命令缓冲区等(在主循环之外分配。在主循环(每帧(中,我唯一要做的就是用vkQueueSubmit()触发命令缓冲区,它将数据从主机内存传输到设备本地内存。我拿了几个重要的";快捷方式";关于文学(每个人都从经典的Vulkan教程开始(。通过直接写入stagingMemory,我不需要单独的内存,也不需要memcpy(),而且在循环之外执行大部分操作更像是一种快捷方式。这是伪代码:

void* stagingMemory;
vkMapMemory(logicalDevice, stagingBufferMemory, 0, size, 0, &stagingMemory);
while (running)
{
// write directly into stagingMemory by fiddling with pointers and offsets
if (its_time_to_update_ubo_on_device)
{
VkQueueSubmit(...) // transfer stagingBufferMemory to device-local buffer
}
}
// only on exit
vkUnmapMemory(logicalDevice, stagingBufferMemory);

这是有效的,我理解这是高性能的,因为我最小化了实例化(如SubmitInfo和命令缓冲区(和其他一些操作。但我想知道从长远来看这是否安全。当内存压力触发虚拟内存页面被调出到磁盘时会发生什么?这种情况会发生吗?或者stagingMemory安全吗?

引起我怀疑的是,我一直读到一种非常不同的方法,比如:

while (running)
{
// write to memory (not staging memory!)
void* stagingMemory;
vkMapMemory(logicalDevice, stagingBufferMemory, 0, size, 0, &stagingMemory);
memcpy(stagingMemory, memory, size);
vkUnmapMemory(logicalDevice, stagingBufferMemory);
if (its_time_to_update_ubo_on_device)
{
SubmitInfo info {}; // re-initialize every time anew
VkQueueSubmit(... info ...) // upload to device-local memory
}
}

这种优化程度较低的方法只是出于说教的原因,还是防止了我还没有想到的问题,并会在以后毁掉一切?

我是在做nVidia博客文章中描述的固定主机内存,还是这仍然不同?

当内存压力触发虚拟内存页面被分页到磁盘时会发生什么?

嗯。。。事实上,这不是一件会发生的事情。

虚拟页面从来都不是"虚拟页面";调出";;只有物理存储被调出。虚拟地址范围下的存储可以被调出,但实际的虚拟地址是可以的。

也许您认为Vulkan必须确保与映射范围相关联的物理页面不会被调出,以免DMA操作无法完成。这不是武尔坎转移行动的运作方式。他们不要求在传输期间映射内存(也不要求在传输之前取消映射。Vulkan不在乎(。因此,对于Vulkan来说,是否有一些虚拟地址范围绑定到存储器并不重要;在内部,它可能使用实际的物理地址进行DMA操作。

如果GPU需要该内存范围不一直被调出,那么无论是否映射,它都需要它。如果GPU对它的分页很满意,并且会在从/到它的任何DMA操作之前将它分页回来,那么这与正在映射的内存无关。

简而言之,你的问题是不合理的:保持它的映射与否不会影响记忆压力。它唯一可能影响的是程序使用的虚拟内存地址。在64位程序时代,这确实是一种学术性的东西。除非您认为要分配2^48字节的存储空间。

因此,取消映射内存的唯一原因(除了在即将删除内存时(是,如果您正在编写一个32位应用程序,并且需要小心虚拟地址空间,则您知道,除非您对其进行映射,否则该实现不会将虚拟地址分配给CPU可访问的内存(实现可以随时为其提供虚拟地址空间(。

最新更新