Skip to content

Commit d380a45

Browse files
committed
Change to VSM
1 parent 637330c commit d380a45

File tree

362 files changed

+2514
-133198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

362 files changed

+2514
-133198
lines changed

bookcontents/chapter-02/chapter-02.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ Of course, you just need to add a new variable in the `eng.properties` file (loc
634634
vkValidate=true
635635
```
636636

637-
And that's all! As you can see, we have to write lots of code just to set up the Vulkan instance.You can see now why Vulkan is considered an explicit API. A whole chapter passed, and we can't even clear the screen. So, contain your expectations, since in the next chapters we will continue writing lots of code required to set up everything.
637+
And that's all! As you can see, we have to write lots of code just to set up the Vulkan instance. You can see now why Vulkan is considered an explicit API. A whole chapter passed, and we can't even clear the screen. So, contain your expectations, since in the next chapters we will continue writing lots of code required to set up everything.
638638
It will take some time to draw something, so please be patient. The good news is that when everything is set up, adding incremental features to draw more complex models or to support advanced techniques should require less amount of code. And if we do it correctly, we get a good understanding of Vulkan.
639639

640640
[Next chapter](../chapter-03/chapter-03.md)

bookcontents/chapter-03/chapter-03.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
- eng.properties.
2-
- EngCfg
3-
- Render
4-
- VkCtx
5-
- Device
6-
- PhysDevice
7-
- Queue
8-
- Surface
9-
101
# Chapter 03 - Physical, Logical devices and Surface
112

123
In this chapter we will progress in the definition of the Vulkan structures required to render a 3D scene. Specifically, we will setup the Physical and Logical devices and a Surface.

bookcontents/chapter-04/chapter-04.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ If the surface has already defined its extent, we pick that. If it is not define
168168
## Swap chain images
169169

170170
We have seen that the last step in `SwapChain` constructor is to create image views. What are image views? We would need images to render to, but in the specific case of
171-
the swap chain these images have already been created when we created the swap chain. Therefore, why not just retrieve them ? Why we need image views?
171+
the swap chain these images have already been created when we created the swap chain. Therefore, why not just retrieve them? Why we need image views?
172172
In Vulkan we cannot just use the images, we need an indirect element to access them. This element is called Image View and basically it states how the image will be accessed. Before going on, it is important to precisely define the concepts involved in handling images in Vulkan. In order to have an image that can be accessed by shaders in Vulkan we need:
173173

174174
- A `Buffer` which contains the raw data of the image, its contents. A `Buffer` is just a linear array of data.
@@ -209,9 +209,9 @@ public class SwapChain {
209209
}
210210
```
211211

212-
The first thing we do is retrieve the **actual** number of images that our swap chain has. But wait, we had already specified this when we created the swap chain, why we do need to retrieve that again? The answer is that we created the swap chain with a desired number of images, but the Vulkan driver may have returned a different number. This is why we need to call the `vkGetSwapchainImagesKHR` function to retrieve the number of images. This is whey we used the length of the returned image views to the the `numImages` attribute.
212+
The first thing we do is retrieve the **actual** number of images that our swap chain has. But wait, we had already specified this when we created the swap chain, why we do need to retrieve that again? The answer is that we created the swap chain with a desired number of images, but the Vulkan driver may have returned a different number. This is why we need to call the `vkGetSwapchainImagesKHR` function to retrieve the number of images. This is whey we used the length of the returned image views to the `numImages` attribute.
213213
After that, we call that same function again to retrieve the handles to the images themselves.
214-
Now we iterate over the images to create new `ImageView` instances. The `ImageView` class encapsulates the creation and disposal of Vulkan image views. Since the parameters to properly set up image views can be quite lengthy, it defines a build helper class, as an inner class, using a fluent like API style. This way we will increase readability of the code tha constructs images, and we will be able to add support for new parameters without breaking existing code.
214+
Now we iterate over the images to create new `ImageView` instances. The `ImageView` class encapsulates the creation and disposal of Vulkan image views. Since the parameters to properly set up image views can be quite lengthy, it defines a build helper class, as an inner class, using a fluent like API style. This way we will increase readability of the code that constructs images, and we will be able to add support for new parameters without breaking existing code.
215215

216216
```java
217217
...
@@ -310,7 +310,7 @@ In order to create a Image View we need to fill up a `VkImageViewCreateInfo` str
310310
- `format`: The format of the image. In this case we just use the format of the underlying swap chain images.
311311
- `subresourceRange`: This parameter allow us to select a specific range of the underlying image. We can select a specific set of mipmap levels or layers (in the case of array of layers). In this case, we can control the maximum mipmap level (through the `mipLevels`argument), and we stick to 1 layer. Regarding the aspects, for this specific case, we will get the color aspect (for example there are some other aspect for depth images).
312312

313-
With the `VkImageViewCreateInfo` structure filled up, we just need to call the `vkCreateImageView` to get a handle to the the Image View. The rest of the class is just some *getters* to access main attributes and the `cleanup` method to free the resources.
313+
With the `VkImageViewCreateInfo` structure filled up, we just need to call the `vkCreateImageView` to get a handle to the Image View. The rest of the class is just some *getters* to access main attributes and the `cleanup` method to free the resources.
314314

315315
```java
316316
...
@@ -335,7 +335,7 @@ public class ImageView {
335335
}
336336
```
337337

338-
Going back to the `Swapchain`class we need also to create a `cleanup` method to free the resources and som other *getters*:
338+
Going back to the `Swapchain`class we need also to create a `cleanup` method to free the resources and some other *getters*:
339339
```java
340340
public class SwapChain {
341341
...
@@ -385,7 +385,7 @@ public class VkCtx {
385385
}
386386
```
387387

388-
We have also modified also the `EngineProperties` class to read a new property to configure the usage of vsync and to define de requested number of images for the swap chain:
388+
We have also modified also the `EngineProperties` class to read a new property to configure the usage of vsync and to define the requested number of images for the swap chain:
389389

390390
```java
391391
public class EngCfg {
@@ -422,6 +422,6 @@ vsync=true
422422
...
423423
```
424424

425-
Ant that is all by now, we are still getting a plain white window, but soon we will be abele to render something.
425+
Ant that is all by now, we are still getting a plain white window, but soon we will be able to render something.
426426

427427
[Next chapter](../chapter-05/chapter-05.md)

bookcontents/chapter-05/chapter-05.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ public class CmdBuffer {
164164
}
165165
```
166166

167-
We have two methods to start recording, the first one, which receives no parameter is a convenience method which can be used for primary command buffers. The next one receives an InheritanceInfo instance which is required for secondary buffers. To start recording we need to create a `VkCommandBufferBeginInfo` structure and invoke the `vkBeginCommandBuffer` function. If we are submitting a short lived command, we can signal that using the flag `VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT`. In this case, the driver may not try to optimize that command buffer since it will only be used one time. Secondary buffers need to be instructed which render pass and frame buffer they will need to use since a render pass wil not be started when recording. We need to fill up the `VkCommandBufferInheritanceInfo` structure filled up with the color and depth attachments formats (more on this later). Secondary command buffers can be integrated into primary command buffers by executing the `vkCmdExecuteCommands` function.
167+
We have two methods to start recording, the first one, which receives no parameter is a convenience method which can be used for primary command buffers. The next one receives an `InheritanceInfo` instance which is required for secondary buffers. To start recording we need to create a `VkCommandBufferBeginInfo` structure and invoke the `vkBeginCommandBuffer` function. If we are submitting a short lived command, we can signal that using the flag `VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT`. In this case, the driver may not try to optimize that command buffer since it will only be used one time. Secondary buffers need to be instructed which render pass and frame buffer they will need to use since a render pass wil not be started when recording. We need to fill up the `VkCommandBufferInheritanceInfo` structure filled up with the color and depth attachments formats (more on this later). Secondary command buffers can be integrated into primary command buffers by executing the `vkCmdExecuteCommands` function.
168168

169169
The next methods are the usual `cleanup` for releasing resources, to finalize the recording and another one to get the command buffer Vulkan instance.
170170

@@ -210,7 +210,7 @@ prior to showing that method we need to introduce new concepts.
210210

211211
Prior to progress in rendering something on the screen, we need to address a fundamental topic in Vulkan: synchronization. In Vulkan we will be in charge of properly control the synchronization of the resources. This imposes certain complexity but allows us to have a full control about how operations will be done. In this section we well address two main mechanisms involved in Vulkan synchronization: semaphores and fences. There are some other elements such as barriers or events, we will explain them once we first use them.
212212

213-
Fences are the mechanisms used in Vulkan to synchronize operations between the GPU and the CPU (our application). Semaphores are used to synchronize operations inside the GPU (GPU to GPU synchronization), and are frequently used to synchronize queue submissions. These elements are used to block the execution until a signal is performed. Once a signal is received execution is resumed. It is important to note, that signalling is always done in the GPU side. In the case of fences, our application can block (waiting in the CPU) until the GPU signals that execution can go on, but we cannot trigger the signalling from the CPU. In the case of semaphores, since they are internal to the GPU, waiting can only happen in the GPU.
213+
Fences are the mechanisms used in Vulkan to synchronize operations between the GPU and the CPU (our application). Semaphores are used to synchronize operations inside the GPU (GPU to GPU synchronization), and are frequently used to synchronize queue submissions. These elements are used to block the execution until a signal is performed. Once a signal is received execution is resumed. It is important to note, that signaling is always done in the GPU side. In the case of fences, our application can block (waiting in the CPU) until the GPU signals that execution can go on, but we cannot trigger the signaling from the CPU. In the case of semaphores, since they are internal to the GPU, waiting can only happen in the GPU.
214214

215215
Before using these elements, we will define some classes to manage them. We will firs start with the `Semaphore` class:
216216

@@ -250,7 +250,7 @@ public class Semaphore {
250250
}
251251
```
252252

253-
In this case we are modelling what is called a binary semaphore, which can be in two states: signaled and un-signaled. When you create a semaphore is in an un-signaled state. When we submit command we can specify a semaphore to be signaled when the commands complete. If later on, we record some other commands or perform some operation that require those previous commands to be completed, we use the same semaphore to wait. The cycle is like this, the semaphore in the first step is un-signaled, when the operations are completed get signaled and then, commands that are waiting can continue (we say that these second step activities are waiting for the semaphore to be signaled). Creating a semaphore is easy, just define a `VkSemaphoreCreateInfo` and call the `vkCreateSemaphore` function. The rest of the methods are the usual ones, one for cleaning up the resources and another one to get the semaphore handle.
253+
In this case we are modeling what is called a binary semaphore, which can be in two states: signaled and un-signaled. When you create a semaphore is in an un-signaled state. When we submit command we can specify a semaphore to be signaled when the commands complete. If later on, we record some other commands or perform some operation that require those previous commands to be completed, we use the same semaphore to wait. The cycle is like this, the semaphore in the first step is un-signaled, when the operations are completed get signaled and then, commands that are waiting can continue (we say that these second step activities are waiting for the semaphore to be signaled). Creating a semaphore is easy, just define a `VkSemaphoreCreateInfo` and call the `vkCreateSemaphore` function. The rest of the methods are the usual ones, one for cleaning up the resources and another one to get the semaphore handle.
254254

255255
Now it's turn for the `Fence`class:
256256

@@ -299,7 +299,7 @@ public class Fence {
299299
}
300300
```
301301

302-
As in the `Semaphore` class, the `Fence` class is also very simple. We also need to fill up an initialization structure named `VkFenceCreateInfo`. In this case, we can set (through a constructor argument), if the fence should be already signaled when created or not. Besides the cleaning method and the one for getting the handle we have one method called `fenceWait` which waits waits for the fence to be signaled (waits in the CPU the signal raised by the GPU). We have another one named `reset` which resets the fence to unsignaled state by calling the `vkResetFences` function.
302+
As in the `Semaphore` class, the `Fence` class is also very simple. We also need to fill up an initialization structure named `VkFenceCreateInfo`. In this case, we can set (through a constructor argument), if the fence should be already signaled when created or not. Besides the cleaning method and the one for getting the handle we have one method called `fenceWait` which waits waits for the fence to be signaled (waits in the CPU the signal raised by the GPU). We have another one named `reset` which resets the fence to un-signaled state by calling the `vkResetFences` function.
303303

304304
![Synchronization](rc05-yuml-02.svg)
305305

@@ -385,7 +385,7 @@ which provides additional information.
385385

386386
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.
387387
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
388+
`renderCompleteSemphs`. When presenting the acquired swap chain image we will use the proper index `renderCompleteSemphs[i]` when calling the `vkQueuePresentKHR` function
389389
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.
390390

391391
- In Frame `#0`:
@@ -403,7 +403,7 @@ so presentation cannot start until render work has been finished. The issue here
403403
- 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.
404404

405405
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
406+
we will 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
407407
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
408408
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
409409
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
@@ -489,7 +489,7 @@ Remember that fences are the way to synchronize between GPU and CPU. When we wil
489489
- *Record commands A*: Once we have passed the fence, we can start recording commands in the command buffer associated to the current frame. But Why having two sets of
490490
command "A" and "B" ? The reason for that is that we will have commands that do not depend on the specific swap chain image that we need to acquire ("A commands") and
491491
commands that will perform operations over a specific image view ("B commands"). We can start recording the first step prior to acquiring the swap chain image.
492-
- *Acquire image*: We need to acquire the next swap chain image which will be used to render. In this chapter we will noy have "A commands" yet however.
492+
- *Acquire image*: We need to acquire the next swap chain image which will be used to render. In this chapter we will not have "A commands" yet however.
493493
- *Record commands B*: Already explained.
494494
- *Submit commands*: Just submit the commands to a graphical queue.
495495
- *Present Image*.
@@ -621,7 +621,7 @@ public class SwapChain {
621621
}
622622
```
623623

624-
In order to acquire a image we need to call the function `vkAcquireNextImageKHR`. The parameters for this function are:
624+
In order to acquire an image we need to call the function `vkAcquireNextImageKHR`. The parameters for this function are:
625625
- `device`: The handle to the Vulkan logical device.
626626
- `swapchain`: The handle to the Vulkan swap chain.
627627
- `timeout`: It specifies the maximum time to get blocked in this call (in nanoseconds). If the value is greater than `0` and we are not able to get an image in that time, we will get a `VK_TIMEOUT` error. In our case, we just want to block indefinitely.

0 commit comments

Comments
 (0)