While learning Vulkan, I frequently run into code like this:
vk::raii::Semaphore imageAvailable = device.createSemaphore(...);
vk::raii::Semaphore renderingFinished = device.createSemaphore(...);
while (shouldRun) {
// Platform loop
auto [result, imageIndex] = swapchain.acquireNextImage(UINT64_MAX, *imageAvailable, {});
queue.submit(
vk::SubmitInfo(*imageAvailable, waitDstStage, *commandBuffers[imageIndex],
*renderingFinished), {});
queue.presentKHR(vk::PresentInfoKHR(*renderingFinished, *swapchain, imageIndex));
}
In this example, we ask the presentation engine to provide us with the index of the next available image and signal imageAvailable
when we the image comes available for us to use. We then submit our pre-recorded command buffer which draws to its corresponding image (for simplicity, I record one command buffer per swapchain image at initialization and draw the same thing every frame) and require that imageAvailable
become signaled before execution (reaches waitDstStage
), and ask that renderingFinished
be signaled when execution completes. Finally, we hand the image back to the presentation engine and ask it to present the image once renderingFinished
becomes signaled.
Does this potentially exhibit undefined behavior due to improper synchronization?
If I understand the spec, I can imagine a scenario in which two submit
calls are simultaneously waiting on imageAvailable
: None of the calls in the main loop above is required to block at all... and so it could potentially execute two calls to vkQueueSubmit
, both waiting on imageAvailable
, before imageAvailable
has been signalled. In my head, when it does become signaled, both batches would execute, and one would potentially access an image before the presentation engine has released it to us. However, in theory, this can't happen:
If 'semaphore' is not 'VK_NULL_HANDLE', the semaphore must be unsignaled, with no signal or wait operations pending. It will become signaled when the application can use the image.
After the first call to vkQueueSubmit
, the semaphore could have a wait operation pending. At the second call, the wait operation could still be pending. This would result in undefined behavior, and my Vulkan program is at fault for any application misbehavior.
Firstly, am I to understand that I may treat vkQueuePresentKHR
as a "command" (or at least executing commands) within a queue, and I can apply synchronization rules to it? Secondly, am I correct in assuming that I require additional synchronization in the above code to avoid potentially invoking undefined behavior?