如何更新vulkan中每一帧的纹理?

时间:2016-11-13 14:03:46

标签: vulkan

正如我的问题标题所说,我想要为每一帧更新纹理。

我有个主意: 使用波纹管配置创建VkImage作为纹理缓冲区:
 initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
usage= VK_IMAGE_USAGE_SAMPLED_BIT

它的内存类型为VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT

在绘制循环中:

第一帧:

  1. 将texure数据映射到VkImage(使用vkMapMemory)。
  2. VkImage布局从VK_IMAGE_LAYOUT_PREINITIALIZED更改为VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
  3. 使用此VkImage作为纹理缓冲区。
  4. 第二帧:

    第一帧后布局为VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,我可以直接将下一个texure数据映射到此VkImage而不更改它的布局吗?如果我不能这样做,我可以将这个布局更改为VkImage到?

    在vkspec 11.4中它说:

      

    a。中使用的新布局   转换不得为VK_IMAGE_LAYOUT_UNDEFINED或VK_IMAGE_LAYOUT_PREINITIALIZED

    所以,我无法将布局更改回_PREINITIALIZED 任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:10)

对于您的情况,您不需要LAYOUT_PREINITIALIZED。这只会使你的代码复杂化(迫使你为第一帧提供单独的代码)。

LAYOUT_PREINITIALIZED确实是一个非常特殊的布局,仅用于图像生命的开始。它对静态纹理更有用。

LAYOUT_UNDEFINED开始,当需要从CPU端写入图像时使用LAYOUT_GENERAL

我提议这个方案:

berfore渲染循环

  1. 使用VkImage
  2. 创建UNDEFINED

    第1帧到第N帧(又名渲染循环)

    1. 将图片转换为GENERAL
    2. 同步(可能与VkFence
    3. 映射图像,编写图像,取消图像(weell,映射和解组可能在渲染循环之外)
    4. 同步(可能隐式完成)
    5. 将图片转换为您需要的任何布局
    6. 做你的渲染和诸如此类的
    7. 从1开始。
    8. 这是一个天真的实现,但应该足以满足普通的爱好用途。

      可以实现双缓冲访问 - 例如,用于CPU访问的VkBuffer和用于GPU访问的VkImage。并且必须为数据切换完成VkCmdCopy*

      它并不比上述方法复杂得多,并且可以带来一些性能优势(如果您需要在项目阶段使用这些方法)。您通常希望将资源放在设备本地内存中,这通常也不是主机可见。

      它会像:

      berfore渲染循环

      1. 使用VkBuffer内存b创建UNDEFINED HOST_VISIBLE并映射
      2. 使用由VkImage内存支持的i创建UNDEFINED DEVICE_LOCAL
      3. ib之间准备同步原语:例如如果转移在同一队列中,则可以使用两个信号量或事件或障碍
      4. 第1帧到第N帧(又名渲染循环)

        bi上的操作可能非常分离(甚至可以在不同的队列中),所以:

        b

        1. b转换为GENERAL
        2. 与CPU同步(可能等待VkFencevkQueueIdle
        3. 无效(如果不连贯),写入,刷新(如果不连贯)
        4. 与GPU同步(如果在提交队列之前,则隐式完成)
        5. b转换为TRANSFER
        6. 同步以确保i未被使用(可能正在等待VkSemaphore
        7. i转换为TRANSFER
        8. vkCmdCopy*b
        9. i
        10. 同步以宣传我已完成i(可能发出信号VkSemaphore
        11. 从1开始。
        12. (2处的栅栏和6.处的信号量必须预先通知或跳过第一帧才能工作)

          i

          1. 同步以确保i可以免费使用(可能等待VkSemaphore
          2. i转换为所需的
          3. 进行渲染
          4. 同步以宣传我已完成i(可能发出信号VkSemaphore
          5. 从1开始。

答案 1 :(得分:2)

这里有很多问题。

首先:

  

创建一个VkImage作为纹理缓冲区

没有这样的事情。相当于OpenGL缓冲区纹理的是Vulkan 缓冲区视图。这不使用任何类型的VkImageVkBufferView没有图像布局。

其次,假设您正在使用某种VkImage,您已经识别出布局问题。除非纹理位于GENERAL布局中,否则无法修改纹理后面的内存。所以你必须强制转换到那个,等到转换命令实际完成执行,然后进行修改。

第三,Vulkan在执行时是异步的,与OpenGL不同,它不会隐藏这一点。当您想要更改它时,着色器仍然可以访问该图像。所以通常,你需要对这些东西加倍缓冲。

在第1帧上,设置图像1的数据,然后使用它进行渲染。在第2帧,您设置图像2的数据,然后使用它进行渲染。在第3帧,您覆盖图像1的数据(使用事件确保GPU实际完成第1帧)。

或者,您可以使用暂存缓冲区来使用双缓冲而无需CPU等待。也就是说,不是直接写入图像,而是写入主机可见内存。然后使用vkCmdCopyBufferToImage命令将该数据复制到映像中。这样,在发送数据之前,CPU不必等待事件或围栏以确保图像处于GENERAL布局。

而BTW,Vulkan不是OpenGL。内存映射始终是持久的;如果您要每帧都映射一段内存,就没有理由取消映射内存。