为什么在已经使用信号量的情况下每个交换链命令缓冲区都需要 VkFence?
Why is a VkFence necessary for every swapchain command buffer when already using semaphores?
我为交换链使用了 3 个图像,每个交换链图像使用了一个 VkCommandBuffer
(CB)。 GPU 同步是通过两个信号量完成的,一个用于 presentation_finished
,一个用于 rendering_finished
。当前模式是 VK_PRESENT_MODE_MAILBOX_KHR
(quick overview).
现在,当我 运行 我的示例没有等待任何 CB 栅栏时,验证层会在第二次使用任何交换链图像时立即报告此错误:
Calling vkBeginCommandBuffer()
on active CB before it has completed. You must check CB fence before this call.
乍一看这对我来说似乎是合理的,因为处理来自 CB 的命令可能还没有完成。但是我越想越觉得这根本不应该发生。
我目前的理解是,当 vkAcquireImageKHR
return 是一个特定的图像索引时,它意味着图像 returned 必须完成渲染。
这是因为我将 rendering_finished
信号量传递给 vkQueueSubmit
以在渲染完成时发出信号,并传递给 vkQueuePresentKHR
以等待它在呈现图像之前发出信号。
VkQueuePresentInfoKHR
的规范说:
pWaitSemaphores
, if not VK_NULL_HANDLE, is an array of VkSemaphore
objects with waitSemaphoreCount
entries, and specifies the semaphores to wait for before issuing the present request
意思是:我将永远不会呈现任何未完成渲染的图像,因此一旦呈现图像,相关的CB就不能再被使用。
第二个信号量 presentation_finished
由 vkAqcuireImageKHR
发出信号并传递给相同的 vkQueueSubmit
(开始渲染)。这意味着任何图像的渲染都不会早于演示引擎允许的时间开始。
总结:在图像渲染完成之前,vkQueuePresentKHR
不会发出当前请求,并且 vkAcquireImageKHR
会阻塞,直到图像可用,并且永远不会 return当前获取的图像。
我错过了什么使围栏成为必要的?
我提供了一个最小的代码示例,其中仅包含概念上重要的部分来说明问题。
VkImage[] swapchain_images;
VkCommandBuffer[] command_buffers;
VkSemaphore rendering_finished;
VkSemaphore presentation_finished;
void RenderLoop()
{
/* Acquire an image from the swapchain. Block until one is available.
Signal presentation_finished when we are allowed to render into the image */
int index;
vkAcquireImageKHR(device, swapchain, UINT64_MAX, presentation_finished, nullptr, &index);
/* (...) Frambuffer creation, etc. */
/* Begin CB: The command pool is flagged to reset the command buffer on reuse */
VkCommandBuffer cb = command_buffers[index];
vkBeginCommandBuffer(cb, ...);
/* (...) Trivial rendering of a single color image */
/* End CB */
vkEndCommandBuffer(cb);
/* Queue the rendering and wait for presentation_finished.
When rendering is finished, signal rendering_finished.
The VkSubmitInfo will have these important members set among others:
.pWaitSemaphores = &presentation_finished;
.pSignalSemaphores = &rendering_finished;
*/
vkQueueSubmit(render_queue, &submit_info);
/* Submit the presentation request as soon as the rendering_finished
semaphore gets signalled
The VkPresentInfoKHR will have these important members set among others:
.pWaitSemaphores = &rendering_finished;
*/
vkQueuePresentKHR(present_queue, &present_info);
}
在将 CB 提交到渲染队列并在再次使用该 CB 之前等待它时插入栅栏显然可以解决问题,但是 - 正如所解释的那样 - 似乎是多余的。
vkAcquireNextImageKHR
允许 return 图像仍然是正在进行的异步操作的目标 and/or 源。这意味着您无法保证命令缓冲区在重用时可用。 将额外的、不同的命令缓冲区加入队列以写入获取的图像是正确的,只要这些命令被配置为等待 presentation_finished
信号量;但要安全地重用该命令缓冲区,您必须等待传递给 vkQueueSubmit
.
的围栏
请参阅 Vulkan 规范中的 section 29.6. WSI Swapchain 和 KHR 扩展:
An application can acquire use of a presentable image with vkAcquireNextImageKHR. After acquiring a presentable image and before modifying it, the application must use a synchronization primitive to ensure that the presentation engine has finished reading from the image. The application can then transition the image’s layout, queue rendering commands to it, etc. Finally, the application presents the image with vkQueuePresentKHR, which releases the acquisition of the image.
的注释
When successful, vkAcquireNextImageKHR acquires a presentable image that the application can use, and sets pImageIndex to the index of that image. The presentation engine may not have finished reading from the image at the time it is acquired, so the application must use semaphore and/or fence to ensure that the image layout and contents are not modified until the presentation engine reads have completed.
[...]
As mentioned above, the presentation engine may be asynchronous with respect to the application and/or logical device. vkAcquireNextImageKHR may return as soon as it can identify which image will be acquired, and can guarantee that semaphore and fence will be signaled by the presentation engine; and may not successfully return sooner. The application uses timeout to specify how long vkAcquireNextImageKHR waits for an image to become acquired.
这表明 vkAcquireNextImageKHR
不需要阻塞呈现操作,并且不需要传递地阻塞呈现操作本身正在等待的图形命令。
我为交换链使用了 3 个图像,每个交换链图像使用了一个 VkCommandBuffer
(CB)。 GPU 同步是通过两个信号量完成的,一个用于 presentation_finished
,一个用于 rendering_finished
。当前模式是 VK_PRESENT_MODE_MAILBOX_KHR
(quick overview).
现在,当我 运行 我的示例没有等待任何 CB 栅栏时,验证层会在第二次使用任何交换链图像时立即报告此错误:
Calling
vkBeginCommandBuffer()
on active CB before it has completed. You must check CB fence before this call.
乍一看这对我来说似乎是合理的,因为处理来自 CB 的命令可能还没有完成。但是我越想越觉得这根本不应该发生。
我目前的理解是,当 vkAcquireImageKHR
return 是一个特定的图像索引时,它意味着图像 returned 必须完成渲染。
这是因为我将 rendering_finished
信号量传递给 vkQueueSubmit
以在渲染完成时发出信号,并传递给 vkQueuePresentKHR
以等待它在呈现图像之前发出信号。
VkQueuePresentInfoKHR
的规范说:
pWaitSemaphores
, if not VK_NULL_HANDLE, is an array ofVkSemaphore
objects withwaitSemaphoreCount
entries, and specifies the semaphores to wait for before issuing the present request
意思是:我将永远不会呈现任何未完成渲染的图像,因此一旦呈现图像,相关的CB就不能再被使用。
第二个信号量 presentation_finished
由 vkAqcuireImageKHR
发出信号并传递给相同的 vkQueueSubmit
(开始渲染)。这意味着任何图像的渲染都不会早于演示引擎允许的时间开始。
总结:在图像渲染完成之前,vkQueuePresentKHR
不会发出当前请求,并且 vkAcquireImageKHR
会阻塞,直到图像可用,并且永远不会 return当前获取的图像。
我错过了什么使围栏成为必要的?
我提供了一个最小的代码示例,其中仅包含概念上重要的部分来说明问题。
VkImage[] swapchain_images;
VkCommandBuffer[] command_buffers;
VkSemaphore rendering_finished;
VkSemaphore presentation_finished;
void RenderLoop()
{
/* Acquire an image from the swapchain. Block until one is available.
Signal presentation_finished when we are allowed to render into the image */
int index;
vkAcquireImageKHR(device, swapchain, UINT64_MAX, presentation_finished, nullptr, &index);
/* (...) Frambuffer creation, etc. */
/* Begin CB: The command pool is flagged to reset the command buffer on reuse */
VkCommandBuffer cb = command_buffers[index];
vkBeginCommandBuffer(cb, ...);
/* (...) Trivial rendering of a single color image */
/* End CB */
vkEndCommandBuffer(cb);
/* Queue the rendering and wait for presentation_finished.
When rendering is finished, signal rendering_finished.
The VkSubmitInfo will have these important members set among others:
.pWaitSemaphores = &presentation_finished;
.pSignalSemaphores = &rendering_finished;
*/
vkQueueSubmit(render_queue, &submit_info);
/* Submit the presentation request as soon as the rendering_finished
semaphore gets signalled
The VkPresentInfoKHR will have these important members set among others:
.pWaitSemaphores = &rendering_finished;
*/
vkQueuePresentKHR(present_queue, &present_info);
}
在将 CB 提交到渲染队列并在再次使用该 CB 之前等待它时插入栅栏显然可以解决问题,但是 - 正如所解释的那样 - 似乎是多余的。
vkAcquireNextImageKHR
允许 return 图像仍然是正在进行的异步操作的目标 and/or 源。这意味着您无法保证命令缓冲区在重用时可用。 将额外的、不同的命令缓冲区加入队列以写入获取的图像是正确的,只要这些命令被配置为等待 presentation_finished
信号量;但要安全地重用该命令缓冲区,您必须等待传递给 vkQueueSubmit
.
请参阅 Vulkan 规范中的 section 29.6. WSI Swapchain 和 KHR 扩展:
的注释An application can acquire use of a presentable image with vkAcquireNextImageKHR. After acquiring a presentable image and before modifying it, the application must use a synchronization primitive to ensure that the presentation engine has finished reading from the image. The application can then transition the image’s layout, queue rendering commands to it, etc. Finally, the application presents the image with vkQueuePresentKHR, which releases the acquisition of the image.
When successful, vkAcquireNextImageKHR acquires a presentable image that the application can use, and sets pImageIndex to the index of that image. The presentation engine may not have finished reading from the image at the time it is acquired, so the application must use semaphore and/or fence to ensure that the image layout and contents are not modified until the presentation engine reads have completed.
[...]
As mentioned above, the presentation engine may be asynchronous with respect to the application and/or logical device. vkAcquireNextImageKHR may return as soon as it can identify which image will be acquired, and can guarantee that semaphore and fence will be signaled by the presentation engine; and may not successfully return sooner. The application uses timeout to specify how long vkAcquireNextImageKHR waits for an image to become acquired.
这表明 vkAcquireNextImageKHR
不需要阻塞呈现操作,并且不需要传递地阻塞呈现操作本身正在等待的图形命令。