正如我的问题标题所说,我想要为每一帧更新纹理。
我有个主意:
使用波纹管配置创建VkImage
作为纹理缓冲区:
initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
usage= VK_IMAGE_USAGE_SAMPLED_BIT
它的内存类型为VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
VkImage
(使用vkMapMemory
)。 VkImage
布局从VK_IMAGE_LAYOUT_PREINITIALIZED
更改为VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
。 VkImage
作为纹理缓冲区。第一帧后布局为VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
,我可以直接将下一个texure数据映射到此VkImage
而不更改它的布局吗?如果我不能这样做,我可以将这个布局更改为VkImage
到?
在vkspec 11.4中它说:
a。中使用的新布局 转换不得为VK_IMAGE_LAYOUT_UNDEFINED或VK_IMAGE_LAYOUT_PREINITIALIZED
所以,我无法将布局更改回_PREINITIALIZED
任何帮助将不胜感激。
答案 0 :(得分:10)
对于您的情况,您不需要LAYOUT_PREINITIALIZED
。这只会使你的代码复杂化(迫使你为第一帧提供单独的代码)。
LAYOUT_PREINITIALIZED
确实是一个非常特殊的布局,仅用于图像生命的开始。它对静态纹理更有用。
从LAYOUT_UNDEFINED
开始,当需要从CPU端写入图像时使用LAYOUT_GENERAL
。
我提议这个方案:
berfore渲染循环
VkImage
UNDEFINED
醇>
第1帧到第N帧(又名渲染循环)
GENERAL
VkFence
)这是一个天真的实现,但应该足以满足普通的爱好用途。
可以实现双缓冲访问 - 例如,用于CPU访问的VkBuffer
和用于GPU访问的VkImage
。并且必须为数据切换完成VkCmdCopy*
。
它并不比上述方法复杂得多,并且可以带来一些性能优势(如果您需要在项目阶段使用这些方法)。您通常希望将资源放在设备本地内存中,这通常也不是主机可见。
它会像:
berfore渲染循环
VkBuffer
内存b
创建UNDEFINED
HOST_VISIBLE
并映射VkImage
内存支持的i
创建UNDEFINED
DEVICE_LOCAL
i
和b
之间准备同步原语:例如如果转移在同一队列中,则可以使用两个信号量或事件或障碍第1帧到第N帧(又名渲染循环)
b
和i
上的操作可能非常分离(甚至可以在不同的队列中),所以:
b
:
b
转换为GENERAL
VkFence
或vkQueueIdle
)b
转换为TRANSFER
i
未被使用(可能正在等待VkSemaphore
)i
转换为TRANSFER
vkCmdCopy*
到b
i
i
(可能发出信号VkSemaphore
)(2处的栅栏和6.处的信号量必须预先通知或跳过第一帧才能工作)
i
:
i
可以免费使用(可能等待VkSemaphore
)i
转换为所需的i
(可能发出信号VkSemaphore
)答案 1 :(得分:2)
这里有很多问题。
首先:
创建一个VkImage作为纹理缓冲区
没有这样的事情。相当于OpenGL缓冲区纹理的是Vulkan 缓冲区视图。这不使用任何类型的VkImage
。 VkBufferView
没有图像布局。
其次,假设您正在使用某种VkImage
,您已经识别出布局问题。除非纹理位于GENERAL
布局中,否则无法修改纹理后面的内存。所以你必须强制转换到那个,等到转换命令实际完成执行,然后进行修改。
第三,Vulkan在执行时是异步的,与OpenGL不同,它不会隐藏这一点。当您想要更改它时,着色器仍然可以访问该图像。所以通常,你需要对这些东西加倍缓冲。
在第1帧上,设置图像1的数据,然后使用它进行渲染。在第2帧,您设置图像2的数据,然后使用它进行渲染。在第3帧,您覆盖图像1的数据(使用事件确保GPU实际完成第1帧)。
或者,您可以使用暂存缓冲区来使用双缓冲而无需CPU等待。也就是说,不是直接写入图像,而是写入主机可见内存。然后使用vkCmdCopyBufferToImage
命令将该数据复制到映像中。这样,在发送数据之前,CPU不必等待事件或围栏以确保图像处于GENERAL
布局。
而BTW,Vulkan不是OpenGL。内存映射始终是持久的;如果您要每帧都映射一段内存,就没有理由取消映射内存。