3

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

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

  1. vkAcquireNextImageKHR用, 信号获取图像sem_post_acq
  2. 使用以下命令构建命令缓冲区(或使用预构建):
    • 将图像从其过渡离开的图像屏障VK_IMAGE_LAYOUT_UNDEFINED
    • 使成为
    • 将图像过渡到的图像屏障VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
  3. 提交到队列,等待sem_post_acq片段阶段和信令sem_pre_present
  4. vkQueuePresentKHR等待sem_pre_present

这里的问题是命令缓冲区中的图像屏障必须知道它们正在转换哪个图像,这意味着vkAcquireNextImageKHR必须在知道如何构建命令缓冲区(或提交哪个预先构建的命令缓冲区)之前返回。但vkAcquireNextImageKHR可能会睡很多(因为演示引擎很忙并且没有免费图像)。另一方面,命令缓冲区的提交本身是昂贵的,更重要的是,片段之前的所有阶段都可以运行,而无需知道最终结果将渲染到哪个图像。

从理论上讲,在我看来,像以下这样的方案将允许更高程度的并行性:

  1. 使用以下命令构建命令缓冲区(或使用预构建):
    • 将图像从其过渡离开的图像屏障VK_IMAGE_LAYOUT_UNDEFINED
    • 使成为
    • 将图像过渡到的图像屏障VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
  2. 提交到队列,等待sem_post_acq片段阶段和信令sem_pre_present
  3. vkAcquireNextImageKHR用, 信号获取图像sem_post_acq
  4. vkQueuePresentKHR等待sem_pre_present

从理论上讲,这将允许管道一直执行到片段着色器,而我们等待vkAcquireNextImageKHR. 这不起作用的唯一原因是既不能告诉命令缓冲区该图像将在以后确定(通过适当的同步),也不能向演示引擎询问特定图像。


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


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

4

2 回答 2

4

就像 Nicol 所说的,您可以记录次要图像,而与将要渲染到的图像无关。

但是,您可以更进一步,提前记录所有 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 通常足够快,可以在帧外渲染某些几何图形而不会出现任何明显的延迟。因此,在玩家到达必须显示新几何图形的加载区域之前,您可以保持这些命令缓冲区处于活动状态。

于 2016-06-23T10:31:34.627 回答
3

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

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

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

其次,谁说你将把大部分的渲染都呈现给可呈现的图像?如果您正在执行延迟渲染,您的大部分内容将被写入延迟缓冲区。甚至像色调映射、SSAO 等后处理效果也可能会对中间缓冲区进行。

在最坏的情况下,您始终可以渲染到自己的图像。然后你建立一个命令缓冲区,它的唯一内容是从你的图像复制到可呈现的图像。

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

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

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

如果您可以告诉 vkAcquireNextImageKHR 您想要获取哪个特定图像并自己遍历它们,那不是更有意义吗?

不,实施将无法决定下一个给您哪个图像。这正是您必须要求图像的原因:以便实现可以自行确定您拥有哪个图像是安全的。

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

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

一般来说,如果您经常无法及时获取漂亮的图像,则需要使交换链更长。毕竟,这就是拥有多个缓冲区的意义所在。

于 2016-06-23T04:50:00.127 回答