You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
vkCheck(vkQueueSubmit2(vkQueue, submitInfo, fenceHandle), "Failed to submit command to queue");
325
324
}
326
325
}
327
326
...
@@ -367,7 +366,7 @@ Prior to progress on how we render graphics, we will analyze first how what we w
367
366
command buffer and a command pool, of course having a queue to submit the commands and we will need something to do with the image views of the `SwapChain`.
368
367
So, let's just create one instance of each and that's it, right? Well, it turns out that is not so easy. We need to make sure that the image we are rendering into
369
368
is not in use. Ok, so let's then use a fence and a semaphore to prevent that, and that's all, right? Again, it is not so easy. Remember when we talked about the
370
-
`SwapChain` class? We created several image views. We want to be able to perform operations in the CPU while the GPU is working, this is why we tried to use
369
+
`SwapChain` class? We created several image views. We want to be able to perform operations in the CPU while the GPU is working, this is why we tried to use
371
370
triple buffering. So we need to have several resources while processing each frame. How many of them? At first, you may think that you need as many as image views
372
371
have the `SwapChain`image views. The reality, however, is that you do not need as many, with just two is enough. The reason is that we do not want the CPU to wait for
373
372
the GPU to prevent having latency. We will refer to this number as frames in flight, which shall not be confused with total swap chain image views.
@@ -384,6 +383,32 @@ public class VkUtils {
384
383
There is an excellent resource [here](https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/03_Drawing/03_Frames_in_flight.html)
385
384
which provides additional information.
386
385
386
+
So then just create as many semaphores and fences as frames in flight an that's all, right? Again is not so easy!. We need to take care with swap chain image presentation.
387
+
We will use a semaphore when submitting the work to a queue to be signaled after all the work has been done. We will use an array of semaphores for that, called
388
+
`renderCompleteSemphs`. When presenting the acquired swap chain image we wil use the proper index `renderCompleteSemphs[i]` when calling the `vkQueuePresentKHR` function
389
+
so presentation cannot start until render work has been finished. The issue here is that this will be an asynchronous call that can be processed later on. Imagine that we created as the `renderCompleteSemphs` array is size to contain as many instances as flights in frame, let's say `2` and we will have `3` swap chain images.
390
+
391
+
- In Frame `#0`:
392
+
- We acquire the first swap chain image (with an index equal to `0`).
393
+
- We submit the work using `renderCompleteSemphs[0]`.
394
+
- We present the swap chain image (that is, call the `vkQueuePresentKHR` function using `renderCompleteSemphs[0]` sempahore).
395
+
- In Frame `#1`:
396
+
- We acquire the next swap chain image (with an index equal to `1`).
397
+
- We submit the work using `renderCompleteSemphs[1]`.
398
+
- We present the swap chain image (that is, call the `vkQueuePresentKHR` function using `renderCompleteSemphs[1]` sempahore).
399
+
- Everything is ok up to this point.
400
+
- In Frame `#2`:
401
+
- We acquire the next swap chain image (with an index equal to `2`).
402
+
- We have just `2` instances of `renderCompleteSemphs`, so we need to use `renderCompleteSemphs[0]`.
403
+
- The issue here is that presentation for Frame `#0` may not have finished, and thus `renderCompleteSemphs[0]` may still be in use. We may have a synchronization issue here.
404
+
405
+
But, shouldn't fences help us prevent us from this issue? The answer is now, fences will be used when submitting work to a queue. Therefore, if we wait for a fence,
406
+
we wil be sure that previous work associated to the same frame in flight index will have finished. The issue is with presentation, when presenting the swap chain
407
+
image we just only pass a semaphore to wait to be signaled when the render work is finished, but we cannot signal when the presentation will be finished. Therefore, fence
408
+
wait does not know anything about presentation state, The solution for this is to have as many render complete semaphores as swap chain images. The rest of synchronization
409
+
elements and per-frame elements just need to be sized to the maximum number of flight, because they are concerned to just render activities, presentation is not involved
410
+
at all.
411
+
387
412
Let's go now to the `Render` class and see the new attributes that we will need (showing the changes in the constructor and the `cleanup` method):
- An array of command pools, whose size will be equal to the maximum number of flights in flight.
446
474
- An array of command buffers, one per frame in flight where will be recording the commands for each of them.
447
-
- Synchronization instances, semaphores and fences, as well, as many as frames in flight.
475
+
- Synchronization instances, semaphores and fences, as well, as many as frames in flight with the exception of the semaphores that will be signaled when the render process
476
+
is completed.
448
477
- One one `Queue.GraphicsQueue` since we can use them in multiple frames. Synchronization wil be controlled by fences and semaphores.
449
478
- You will see to new classes that have not been defined yet: `Queue.PresentQueue` (which will be used to present swap chain images)
450
479
and `ScnRender` (to actually render a scene). We will see their definition later on.
@@ -518,7 +547,7 @@ The `render` loop performs the following actions:
518
547
- We then call to `recordingStart` which resets the command pool and sets the command buffer in recording mode. Remember that we will not be resetting the command
519
548
buffers but the pool. After this step we could start recording "A commands".
520
549
- In our case, since we do not have "A commands" yet", we just acquire next swap chain image. We will see the implementation later on, but this method returns
521
-
the index of the image acquired (it may not be just the next image index). The `imageAqSemphs` array is the semaphore used to synchronize image acquisitions.
550
+
the index of the image acquired (it may not be just the next image index). The `imageAqSemphs` array is the semaphore used to synchronize image acquisition
522
551
When the image is acquired, this semaphore will be signaled. Any operation depending on this image to be acquired, can use this semaphore as a blocking mechanism.
523
552
- If the `acquireNextImage` returns a negative value, this will mean that the operation failed. This could be because the window has been resized. By now, we just return.
524
553
- Then we can record "B commands" which we will do by calling `scnRender.render(vkCtx, cmdBuffer, imageIndex);`
@@ -530,7 +559,7 @@ The `submit` method is defined like this:
@@ -560,8 +589,10 @@ since we depend on swap chain image view, we want to make sure that the image ha
560
589
-`signalSemphs`: It holds a list of semaphores that will be signaled when all the commands have finished. Remember that we use semaphores for GPU-GPU synchronization. In this case, we are submitting the semaphore used in the swap chain presentation. This will provoke that the image cannot be presented until the commands have finished, that is, until
561
590
render has finished. This is why we use the `VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT`, all the commands need to have finalized their journey through the pipeline.
562
591
563
-
Finally, we use the current `Fence` instance, this way we block the CPU from resetting command buffers that are still in use.
592
+
Please notice that we have different array sizes for the image acquisition semaphores and the render complete semaphores. Later one (`renderCompleteSemphs`) will need to be
593
+
in accessed with the swap chain acquire image index, while the first one (`imageAqSemphs`) will just need frame in flight index.
564
594
595
+
Finally, we use the current `Fence` instance, this way we block the CPU from resetting command buffers that are still in use.
565
596
566
597
Let's start now by reviewing the missing methods in `SwapChain` class. First method is `acquireNextImage`:
0 commit comments