Even when preallocating swapchain-image-count semaphores, it could in theory happen
that a semaphore was not yet set back to UNSIGNALED state before re-using it when no
device/queue waitIdle() is used between frames.
While for the acquireNextImageKHR semaphore it is possible to guarantee the UNSIGNALED
state by waiting for the command buffer submit fence from some older frame (ring buffer approach),
there is no elegant way for the semaphores used for signaling submit completion or finished image
ownership transfer to a separate present queue.
So I was wondering what is the best solution. Is it expensive to just create new semaphores every frame? Currently my workaround is to preallocate a bit more than “swapchain-image-count”
semaphores and rely on the command buffer fences to throttle when too many frames pile up.
If vkQueuePresentKHR would offer a fence that could be used to check if a frame was actually
presented on screen that would also allow to guarantee that all semaphores from a past frame
are ready to be re-used.
That’s exactly my point in the title, in contrast to acquireNextImageKHR() the presentKHR() function does NOT accept a fence. I would love to use a fence so there is no “unwillingness” but a lack of possibility.
It’s not a problem. I could live with that workaround but always try to avoid workarounds so when looking some years later into code there are less quirks to recap.
Afaik acquireNextImageKHR and presentKHR only accept semaphores of type VK_SEMAPHORE_TYPE_BINARY.
Yes, this is basically what my workaround implicitly relies on (the queue submit fences which
are throttling when “swapchain-image-count”-frames have piled up). But maybe I will make it
more explicit by using acquired fences.
Something that makes the whole thing a bit tricky is that acquireNextImageKHR could return any
RANDOM swapchain image index so you only know the next index after calling it while you have
to select a “free” semaphore index before calling it.
What’s the difference between checking if present has finished vs. checking if acquire has finished? You mention at the end that it’s “a bit tricky” because you can’t pair an image index with a semaphore, but all that means is that you need N+1 semaphores in a simple pool. When you acquire an image, you pick an unused semaphore out of the pool. When you test the acquire-fence, you put the presented semaphore back into the pool.
What I did was a modification to my ring buffer helper classes used for command pools. When an image is acquired I scan the buffer for old entries with the same image index and mark the entries (containing the set of frame semaphores) as free. And next() scans for the next entry which is free. Will probably rename it to pool now rather than ring buffer. And yeah the size is N+1 in order to work.