@@ -56,28 +56,28 @@ executed asynchronously. The function calls will return before the operations
5656are actually finished and the order of execution is also undefined. That is
5757unfortunate, because each of the operations depends on the previous one
5858finishing. Thus we need to explore which primitives we can use to achieve
59- the desired ordering.
59+ the desired ordering.
6060
6161### Semaphores
6262
6363A semaphore is used to add order between queue operations. Queue operations
6464refer to the work we submit to a queue, either in a command buffer or from
6565within a function as we will see later. Examples of queues are the graphics
6666queue and the presentation queue. Semaphores are used both to order work inside
67- the same queue and between different queues.
67+ the same queue and between different queues.
6868
6969There happens to be two kinds of semaphores in Vulkan, binary and timeline.
7070Because only binary semaphores will be used in this tutorial, we will not
7171discuss timeline semaphores. Further mention of the term semaphore exclusively
7272refers to binary semaphores.
7373
7474A semaphore is either unsignaled or signaled. It begins life as unsignaled. The
75- way we use a semaphore to order queue operations is by providing the same
76- semaphore as a 'signal' semaphore in one queue operation and as a 'wait'
75+ way we use a semaphore to order queue operations is by providing the same
76+ semaphore as a 'signal' semaphore in one queue operation and as a 'wait'
7777semaphore in another queue operation. For example, lets say we have semaphore S
7878and queue operations A and B that we want to execute in order. What we tell
7979Vulkan is that operation A will 'signal' semaphore S when it finishes executing,
80- and operation B will 'wait' on semaphore S before it begins executing. When
80+ and operation B will 'wait' on semaphore S before it begins executing. When
8181operation A finishes, semaphore S will be signaled, while operation B wont
8282start until S is signaled. After operation B begins executing, semaphore S
8383is automatically reset back to being unsignaled, allowing it to be used again.
@@ -123,7 +123,7 @@ host save the file to disk, as the memory transfer has completed.
123123Pseudo-code for what was described:
124124```
125125VkCommandBuffer A = ... // record command buffer with the transfer
126- VkFence F = ... // create the fence
126+ VkFence F = ... // create the fence
127127
128128// enqueue A, start work immediately, signal F when done
129129vkQueueSubmit(work: A, fence: F)
@@ -136,20 +136,20 @@ save_screenshot_to_disk() // can't run until the transfer has finished
136136Unlike the semaphore example, this example * does* block host execution. This
137137means the host won't do anything except wait until execution has finished. For
138138this case, we had to make sure the transfer was complete before we could save
139- the screenshot to disk.
139+ the screenshot to disk.
140140
141141In general, it is preferable to not block the host unless necessary. We want to
142142feed the GPU and the host with useful work to do. Waiting on fences to signal
143143is not useful work. Thus we prefer semaphores, or other synchronization
144- primitives not yet covered, to synchronize our work.
144+ primitives not yet covered, to synchronize our work.
145145
146146Fences must be reset manually to put them back into the unsignaled state. This
147147is because fences are used to control the execution of the host, and so the
148148host gets to decide when to reset the fence. Contrast this to semaphores which
149149are used to order work on the GPU without the host being involved.
150150
151151In summary, semaphores are used to specify the execution order of operations on
152- the GPU while fences are used to keep the CPU and GPU in sync with each-other.
152+ the GPU while fences are used to keep the CPU and GPU in sync with each-other.
153153
154154### What to choose?
155155
@@ -168,8 +168,8 @@ the current contents of the command buffer while the GPU is using it.
168168
169169We'll need one semaphore to signal that an image has been acquired from the
170170swapchain and is ready for rendering, another one to signal that rendering has
171- finished and presentation can happen, and a fence to make sure only one frame
172- is rendering at a time.
171+ finished and presentation can happen, and a fence to make sure only one frame
172+ is rendering at a time.
173173
174174Create three class members to store these semaphore objects and fence object:
175175
@@ -232,9 +232,8 @@ Creating the semaphores and fence follows the familiar pattern with
232232
233233``` c++
234234if (vkCreateSemaphore(device, &semaphoreInfo, nullptr , &imageAvailableSemaphore) != VK_SUCCESS ||
235- vkCreateSemaphore (device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) ||
236- vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence) != VK_SUCCESS){
237-
235+ vkCreateSemaphore (device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS ||
236+ vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence) != VK_SUCCESS) {
238237 throw std::runtime_error("failed to create semaphores!");
239238}
240239```
@@ -264,7 +263,7 @@ void drawFrame() {
264263```
265264
266265The ` vkWaitForFences ` function takes an array of fences and waits on the host
267- for either any or all of the fences to be signaled before returning. The
266+ for either any or all of the fences to be signaled before returning. The
268267` VK_TRUE ` we pass here indicates that we want to wait for all fences, but in
269268the case of a single one it doesn't matter. This function also has a timeout
270269parameter that we set to the maximum value of a 64 bit unsigned integer,
@@ -280,11 +279,11 @@ Before we can proceed, there is a slight hiccup in our design. On the first
280279frame we call `drawFrame()`, which immediately waits on `inFlightFence` to
281280be signaled. `inFlightFence` is only signaled after a frame has finished
282281rendering, yet since this is the first frame, there are no previous frames in
283- which to signal the fence! Thus `vkWaitForFences()` blocks indefinitely,
282+ which to signal the fence! Thus `vkWaitForFences()` blocks indefinitely,
284283waiting on something which will never happen.
285284
286285Of the many solutions to this dilemma, there is a clever workaround built into
287- the API. Create the fence in the signaled state, so that the first call to
286+ the API. Create the fence in the signaled state, so that the first call to
288287`vkWaitForFences()` returns immediately since the fence is already signaled.
289288
290289To do this, we add the `VK_FENCE_CREATE_SIGNALED_BIT` flag to the `VkFenceCreateInfo`:
@@ -489,7 +488,7 @@ presentInfo.pWaitSemaphores = signalSemaphores;
489488The first two parameters specify which semaphores to wait on before presentation
490489can happen, just like ` VkSubmitInfo ` . Since we want to wait on the command buffer
491490to finish execution, thus our triangle being drawn, we take the semaphores
492- which will be signalled and wait on them, thus we use ` signalSemaphores ` .
491+ which will be signalled and wait on them, thus we use ` signalSemaphores ` .
493492
494493
495494``` c++
0 commit comments