* Update tutorial for command buffer re-recording
Means the code now calls recordCommandBuffer every frame instead of ahead of time
Many changes to the structure of chapters 14 & 15, with small changes to subsequent
chapters.
Primary focus was shifting everything away from 'swapchain image count' to
MAX_FRAMES_IN_FLIGHT. This has caused a lot of chapter 15 to need to be rewritten.
Such as: Introducing fences alongside semaphores and not later; Waiting on a fence
at the start of the frame before introducing acquireNextImage; consolidating the
Frames In Flight concepts to apply to command buffers, fences, and semaphores at
the same time.
Chapter 14 saw command buffer allocation reduced to 1 at a time. This allows the
concept of Frames in Flight to not need introduction before having a triangle
drawing on the screen.
* Update introduction to semaphores and fences
Greatly improve the descriptions of semaphores and fences before their
introduction into the code. Provide examples with psuedo code.
By using max_frames_in_flight command buffers & semaphores, we dont
need to keep track of previously sumitted frames' fences and wait
on them "just in case". This removes a lot of the confusion I had
when I first was trying to understand the vulkan update loop.
* Remove accidental code changes
Un-comment vkDeviceWaitIdle and remove resizing of imagesInFlight (which was removed)
* Use uint32_t instead of size_t for currentFrame
* Address typos and fixup changes for re-recording command buffers PR
* Add description of fences needing explicit resetting while semaphores are automatic
* Elaborate on why 2 frames in flight are chosen
* Fix deadlock in resizing from resetting the fence too early
Because acquiring the swapchain image index may cause drawFrame to return early,
it was possible to cause the next vkWaitForFences to deadlock. By delaying fence
resetting till after acquiring, it prevents the deadlock.
When doing the [validation layers tutorial](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Validation_layers), compiling and running the example produced the following error message:
```
validation layer: libSPIRV-Tools-opt.so: cannot open shared object file: No such file or directory
```
and no other validation layer output. After some Googling, I discovered that installing `spirv-tools` fixes the above error message, and correctly produces the expected warning messages:
```
VUID-VkDebugUtilsMessengerCreateInfoEXT-flags-zerobitmask(ERROR / SPEC): msgNum: 0 - vkCreateDebugUtilsMessengerEXT: parameter pCreateInfo->flags must be 0. The Vulkan spec states: flags must be 0 (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkDebugUtilsMessengerCreateInfoEXT-flags-zerobitmask) Objects: 1 [0] 0, type: 0, name: NULL
validation layer: OBJ ERROR : For VkInstance 0x559e3f3bc200[], VkDebugUtilsMessengerEXT 0x10000000001[] has not been destroyed. The Vulkan spec states: All child objects created using instance must have been destroyed prior to destroying instance (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyInstance-instance-00629)validation layer: OBJ ERROR : For VkInstance 0x559e3f3bc200[], VkDebugUtilsMessengerEXT 0x10000000001[] has not been destroyed. The Vulkan spec states: All child objects created using instance must have been destroyed prior to destroying instance (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyInstance-instance-00629)
```
So this should be added to the Linux development environment setup instructions.
## System info
(let me know if additional details are required)
Ubuntu 20.04.1 LTS, 64-bit
```
└─ $ ▶ uname -a
Linux considerable-shouting 5.4.0-53-generic #59-Ubuntu SMP Wed Oct 21 09:38:44 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
└─ $ ▶ apt list --installed | grep vulkan
libvulkan-dev/focal,now 1.2.131.2-1 amd64 [installed]
libvulkan1/focal,now 1.2.131.2-1 amd64 [installed,automatic]
mesa-vulkan-drivers/focal-updates,now 20.0.8-0ubuntu1~20.04.1 amd64 [installed,automatic]
vulkan-tools/focal,now 1.2.131.1+dfsg1-1 amd64 [installed]
vulkan-validationlayers-dev/focal,now 1.2.131.2-1 amd64 [installed]
vulkan-validationlayers/focal,now 1.2.131.2-1 amd64 [installed,automatic]
```
Following along with the tutorial, I noticed that the GLFW Homebrew package had been renamed from `glfw3` to `glfw`.
Additionally, the latest available version already has the necessary Vulkan support, so it is no longer necessary to install using `--HEAD`.
The existing model had a permissive license at the time this tutorial
was written, but the artist has since started selling the model so I've
decided to switch to a different one to respect their wishes.
Rationale:
* "Default initialization" actually means not initializing the object, which would leave it uninitialized. Using `= {}` is "aggregate initialization" syntax, which leads to "value initialization". Directly using `{}` is just "value initialization".
* `std::endl` is overused and can be a source of performance issues, as it forces the output buffer to be flushed. `\n` is - most of the times - what you need. Just doing my part in trying to avoid overuse of `std::endl`.
`<functional>` is not used anywhere. There's no such thing as a *"lambda function"* in C++ - we have *lambda expressions* which are a core language feature that does **not** require use of the `<functional>` header, and we have `std::function` which is a general-purpose callable object wrapper that **supports** closures generated by *lambda expressions*, but it is not directly related to them. `std::function` is a heavyweight abstraction and it is **not** the type of a closure generated by a *lambda expression* - the latter is an anonymous unique type generated by the compiler.
"Automatic resource management" is nothing new to modern C++, as it can be implemented through basic RAII usage by creating a `struct` with a simple constructor and destructor. While it is true that it is possible to use `std::unique_ptr` and `std::shared_ptr` for Vulkan object management, that is overkill unless unique or shared ownership semantics are actualy needed. The first choice should be a basic wrapper struct, as it has the least overhead and least flexibility.