There are several ways to handle synchronization in Vulkan. This is how I understand it:
In my case I have two command buffers. And I want the second command buffer to execute after the first one.
submitInfo.pCommandBuffers = &firstCommandBuffer; vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); // wait for first command buffer to finish submitInfo.pCommandBuffers = &secondCommandBuffer; vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
What kind of synchronization is best for this? If I use vkQueueWaitIdle(queue)),
is that the same thing as using a fence or should I use event or semaphores for this?
If I send multiple commandbuffer to the queue at the same time:
std::vector<VkCommandBuffer> submitCmdBuffers = { firstCommandBuffer, secondCommandBuffer }; submitInfo.commandBufferCount = submitCmdBuffers.size(); submitInfo.pCommandBuffers = submitCmdBuffers.data();
Is there still a way to synchronize between the first and the second one?
Command buffers are executed by submitting them on one of the device queues, like the graphics and presentation queues we retrieved. Each command pool can only allocate command buffers that are submitted on a single type of queue.
A fence has a similar purpose, in that it is used to synchronize execution, but it is for ordering the execution on the CPU, otherwise known as the host. Simply put, if the host needs to know when the GPU has finished something, we use a fence. Similar to semaphores, fences are either in a signaled or unsignaled state.
Queues in Vulkan are an “execution port” for GPUs. Every GPU has multiple queues available, and you can even use them at the same time to execute different command streams. Commands submitted to separate queues may execute at once.
The first command buffer is rendering object with the depth test turned on. The second command buffer is rendering outlines of meshes with the depth test turned off. Because it has to be on top of the other objects.
For this case, what you need rather depends on what those command buffers are.
If those are secondary command buffers executed within the same render pass instance, then you don't need any synchornization. Not unless you are manually reading from the depth texture in the secondary command buffer. Why?
Because section 2.2.1's API Ordering protects you. Depth testing and depth writing within a render-pass instance will always proceed in API order. So later commands, whether in the same CB or a different one, will be ordered with regard to depth testing/writing.
However, if you need to read that depth buffer from the shader or your command buffers are in different render pass instances, then you need explicit synchronization via an event.
In this case, the stage mask for the vkCmdSetEvent
command should be the stage that writes the depth value. This could be EARLY_FRAGMENT_TESTS_BIT
or LATE_FRAGMENT_TESTS_BIT
. To be safe, use both. However, since you're probably updating the same color buffer, you also need the COLOR_ATTACHMENT_OUTPUT_BIT
stage. Insert this command at the end of the first command buffer (or after all the depth writing is done).
For the vkCmdWaitEvent
, you want to wait on the pipeline stages that need it. In your case, this is again the fragment tests and color attachment. But if a shader stage is going to read the depth, you also need that stage in the wait command.
Since memory is involved, your vkCmdWaitEvent
will also need to use a memory dependency on the depth and color buffers.
Really though, all of this complexity is why you should try to put these command buffers in the same render pass instance if at all possible. The only reason you would be unable to do so is if you needed to read from the depth buffer in a shader.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With