无法与渲染并行获取和呈现?

时间:2016-06-23 03:52:53

标签: vulkan

注意:我是自学的Vulkan,对现代OpenGL知之甚少。

阅读Vulkan规范,我可以看到非常好的信号量,它们允许命令缓冲区和交换链同步。这就是我所理解的一种简单(但我认为效率低下)的做事方式:

  1. 使用vkAcquireNextImageKHR获取图片,发出sem_post_acq
  2. 信号
  3. 使用以下命令构建命令缓冲区(或使用预构建):
    • 将图片从VK_IMAGE_LAYOUT_UNDEFINED
    • 过渡的图像障碍
    • 呈现
    • 将图片转换为VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
    • 的图片障碍
  4. 提交到队列,等待片段阶段的sem_post_acq并发信号sem_pre_present
  5. vkQueuePresentKHR等待sem_pre_present
  6. 这里的问题是命令缓冲区中的图像障碍必须知道它们正在转换哪个图像,这意味着{1}必须在知道如何构建命令缓冲区(或者预构建的命令缓冲区)之前返回提交)。但vkAcquireNextImageKHR可能会睡眠很多(因为演示引擎很忙而且没有免费图像)。另一方面,命令缓冲区的提交本身就很昂贵,更重要的是,片段之前的所有阶段都可以在不知道最终结果将被渲染到哪个图像的情况下运行。

    从理论上讲,在我看来,如下所示的方案可以实现更高程度的并行性:

    1. 使用以下命令构建命令缓冲区(或使用预构建):
      • 将图片从vkAcquireNextImageKHR
      • 过渡的图像障碍
      • 呈现
      • 将图片转换为VK_IMAGE_LAYOUT_UNDEFINED
      • 的图片障碍
    2. 提交到队列,等待片段阶段的VK_IMAGE_LAYOUT_PRESENT_SRC_KHR并发信号sem_post_acq
    3. 使用sem_pre_present获取图片,发出vkAcquireNextImageKHR
    4. 信号
    5. sem_post_acq等待vkQueuePresentKHR
    6. 在理论上,这将允许管道一直执行到片段着色器,同时我们等待sem_pre_present。这不起作用的唯一原因是既不能告诉命令缓冲区稍后将确定该图像(具有适当的同步),也不可能向演示引擎询问特定图像。

      我的第一个问题是:我的分析是否正确?如果是这样,在Vulkan中根本不可能进行这样的优化,为什么不呢?

      我的第二个问题是:如果您能告诉vkAcquireNextImageKHR您想要获取哪个特定图像并自行迭代,那么它是否更有意义?这样,您可以提前知道要请求的图像,并相应地构建和提交命令缓冲区。

2 个答案:

答案 0 :(得分:3)

就像尼科尔所说,你可以独立于将要呈现的图像来记录第二个。

但是,您可以更进一步,提前记录所有swpachain图像的命令缓冲区,并从所获取的图像中选择要提交的正确缓冲区。

这种类型的重用确实需要额外考虑,因为所使用的所有内存范围都被烘焙到命令缓冲区中。但在许多情况下,所需的渲染命令实际上不会将第一帧更改为下一帧,只使用了一小部分数据。

所以这样一个帧的顺序是:

vkAcquireNextImageKHR(vk.dev, vk.swap, 0, vk.acquire, VK_NULL_HANDLE, &vk.image_ind);
vkWaitForFences(vk.dev, 1, &vk.fences[vk.image_ind], true, ~0);

engine_update_render_data(vk.mapped_staging[vk.image_ind]);

VkSubmitInfo submit = build_submit(vk.acquire, vk.rend_cmd[vk.image_ind], vk.present);
vkQueueSubmit(vk.rend_queue, 1, &submit, vk.fences[vk.image_ind]);

VkPresentInfoKHR present = build_present(vk.present, vk.swap, vk.image_ind);
vkQueuePresentKHR(vk.queue, &present);

虽然这不允许条件渲染但是gpu通常足够快以允许一些几何体在帧外渲染而没有任何明显的延迟。因此,在播放器到达必须显示新几何体的加载区之前,您可以保持这些命令缓冲区的活动。

答案 1 :(得分:2)

您的整个问题基于这样的假设,即如果没有特定的交换链图像,您无法执行任何命令缓冲区构建工作。这根本不是真的。

首先,您始终可以构建辅助命令缓冲区;提供VkFramebuffer仅仅是礼貌,而非要求。如果您想使用Vulkan来提高CPU性能,这一点非常重要。毕竟,能够并行构建命令缓冲区是Vulkan的卖点之一。对于你来说,只创建一个对于注重表现的应用来说是一种浪费。

在这种情况下,只有主命令缓冲区需要实际图像。

第二,谁说你将大部分渲染到可呈现的图像?如果你正在进行延迟渲染,那么你的大部分东西都会被写入延迟缓冲区。即使是色调映射,SSAO等后处理效果也可能会对中间缓冲区进行。

最糟糕的情况是,您始终可以渲染到自己的图像。然后,您构建一个命令缓冲区,其中只有内容是从您的图像到可呈现的图像副本。

  

片段之前的所有阶段都可以在不知道最终结果将被渲染到哪个图像的情况下运行。

您假设硬件在顶点处理和光栅化之间有严格的分离。这仅适用于基于图块的硬件。

直接渲染器只为每个渲染命令执行从上到下的整个管道。它们不会将变换后的顶点数据存储在大缓冲区中。它只是流向下一步。因此,如果“片段阶段”必须等待信号量,那么您可以假设所有其他阶段在等待时也将处于空闲状态。

  如果你能告诉vkAcquireNextImageKHR你想要获取哪个特定的图像,并自己迭代它们,那会不会更有意义?

没有。实施将无法决定下一个给你的图像。这正是您必须要求图像的原因:这样实现可以自己确定哪个图像对您来说是安全的。

此外,规范中还有一种特定的语言,即您提供的信号量和/或事件不仅必须是无信号的,而且不能有任何未完成的操作等待它们。为什么呢?

因为vkAcquireNextImageKHR 失败。如果你在队列中有一些操作正在等待一个永远不会被触发的信号量,那将导致巨大的问题。您必须先成功获取,然后提交基于信号量的工作。

一般来说,如果您经常无法及时获得可呈现的图像,则需要延长交换链的使用时间。毕竟,这就是拥有多个缓冲区的重点。