Vulkan synchronization problem


Recently I got back to abandoned little project with Vulkan. In general I wanted to create simple yet useful renderer for my small experiments. I tried to run my project and..

vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x160000000016) is being signaled by VkQueue 0x14decdb38, but it may still be in use by VkSwapchainKHR 0x20000000002.

This error means the semaphore I am using to signal the completion of rendering work is somehow being reused or signaled a second time before the presentation system (the Swapchain) has finished waiting on its first signal from a previous frame.

What doest it really mean? It was long ago when I was playing with Vulkan last time. Lets remind ourselves how synchronization in Vulkan works(or at least how I remember it).

Understanding Synchronization Steps

Vulkan uses three main types of synchronization primitives, each operating on a different boundary:

PrimitiveBoundaryPurpose (What waits for what)
Fences (VkFence)CPU-GPUThe CPU waits for the GPU to finish a specific submission.
Semaphores(VkSemaphore)GPU-GPUThe GPU waits for a step on the GPU (like image acquisition) to finish before starting another GPU step (like rendering).
Events (VkEvent)GPU-GPUFine-grained synchronization within a command buffer. (Less common in basic rendering loops).

Frame Synchronization Flow Clarification

In a typical Vulkan rendering loop with N frames in flight, the synchronization uses both Fences and Semaphores:

1. CPU Waits for InFlightFence (CPU-GPU Sync)
  • Action: At the start of frame i, the CPU calls vkWaitForFences on the InFlightFence[i(modN)].
  • Purpose: This is to ensure that the resources (command buffer, uniform buffers, descriptors) for the current frame index are no longer being used by the GPU from a previous frame. It prevents the CPU from overwriting data the GPU is still reading.
  • Ready When: The GPU finished all work submitted in the previous frame that used this index.( We mean here filling up command buffer and using it to render all the data to our target image)
2. GPU Acquires Next Image (GPU-GPU Sync, Part 1)
  • Action: The CPU calls vkAcquireNextImageKHR.
  • Synchronization: It takes two parameters:
    • ImageAvailableSemaphore: The Swapchain uses this semaphore. When the image is ready for rendering, the Swapchain signals this semaphore.
  • Purpose: To get the index of the next available Swapchain image and get a semaphore that will be signaled when that image is safe to render to.
3. GPU Queues Rendering Work (GPU-GPU Sync, Part 2)
  • Action: The CPU records the rendering commands and calls vkQueueSubmit.
  • Synchronization: This is the most complex step:
    • Wait Semaphores (Input): The submission waits for the ImageAvailableSemaphore (from step 2) to be signaled. This ensures rendering doesn’t start until the canvas is ready.
    • Signal Semaphores (Output): The submission signals the RenderingDoneSemaphore. This semaphore will be signaled after all rendering commands are fully completed.
    • Fence (Output): The submission is associated with the InFlightFence[i(modN)]. The fence is signaled when all work in this submission is finished.
  • Purpose: To submit the command buffer to the GPU, making the GPU wait for the image, and setting up signals for when rendering is complete (semaphore) and when the frame slot is free (fence).
4. GPU Presents Image (GPU-GPU Sync, Part 3)
  • Action: The CPU calls vkQueuePresentKHR.
  • Synchronization: The Present call waits for the RenderingDoneSemaphore (from step 3).
  • Purpose: To tell the Swapchain to display the rendered image on the screen, but only after the RenderingDoneSemaphore has been signaled by the rendering queue.

The True Meaning of the Error

The error, which involves the signal semaphore being “still in use by VkSwapchainKHR,” strongly suggest that  rendering completion semaphore (the one I signal in vkQueueSubmit and wait for in vkQueuePresentKHR) is being used more than once per frame, or that I am using the same semaphore for multiple frames without respecting its lifecycle.

The Problem with Semaphores

A VkSemaphore is a single-use object.

  • It is designed to be signaled once by a source operation (rendering queue) and waited upon once by a destination operation (present queue).
  • If I signal it twice, or signal it again for the next frame before the Present operation (which waits on it) for the previous frame has completed, I will run into undefined behavior, which the validation layer detects.

My case:

Not Enough Semaphores: I do allow N frames in flight, so I have:

  • N InFlightFences
  • N ImageAvailableSemaphores (Signaled by Acquire)
  • N RenderingDoneSemaphores (Signaled by Submit, waited on by Present)

    The Fix: But! I have triple buffering and I do have 2 frames inflight support. So instead of having 2 RenderingDoneSemaphores I need 3. Instead of taking one using current frame index I use index from acquired image from swap chain. So in my case two previous frames might still be in present stage, we can start next frame because our in flight fence is free to go but we cannot use semaphores for those two frames still waiting for present, we need a third one and now it should work just fine.


    Leave a Reply

    Your email address will not be published. Required fields are marked *