Skip to content

Commit a8d6b8d

Browse files
committed
Editing pass, minor code changes (not backported)
1 parent e0635cf commit a8d6b8d

File tree

16 files changed

+32
-23
lines changed

16 files changed

+32
-23
lines changed

guide/src/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ To fill that gap, this guide has the following goals:
88
- Focus on keeping it simple and straightforward, _not_ on performance
99
- Develop a basic but dynamic rendering foundation
1010

11-
To reiterate, the focus is _not on performance_, it is on a quick introduction to the current standard multi-platform graphics API while utilizing the modern paradigms and tools (at the time of writing). Even disregarding potential performance gains, Vulkan has a better and modern design and ecosystem than OpenGL, eg: there is no global state machine, parameters are passed by filling structs with meaningful member variable names, multi-threading is largely trivial (yes, it is actually easier to do on Vulkan than OpenGL), there are a comprehensive set of validation layers to catch misuse which can be enabled without _any_ changes to application code, etc.
11+
To reiterate, the focus is _not on performance_, it is on a quick introduction to the current standard multi-platform graphics API while utilizing the modern paradigms and tools (at the time of writing). Even disregarding potential performance gains, Vulkan has a better and more modern design and ecosystem than OpenGL, eg: there is no global state machine, parameters are passed by filling structs with meaningful member variable names, multi-threading is largely trivial (yes, it is actually easier to do on Vulkan than OpenGL), there are a comprehensive set of validation layers to catch misuse which can be enabled without _any_ changes to application code, etc.
1212

1313
## Target Audience
1414

guide/src/getting_started/validation_layers.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ The area of Vulkan that apps interact with: the loader, is very powerful and fle
44

55
![Vulkan Loader](high_level_loader.png)
66

7-
As [suggested](https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/main/docs/khronos_validation_layer.md#vkconfig) by the Khronos Group, we recommend using [Vulkan Configurator (GUI)](https://github.com/LunarG/VulkanTools/tree/main/vkconfig_gui) for validation layers. This application is shipped with the Vulkan SDK, just keep it running while developing Vulkan applications, and ensure it is setup to inject validation layers into all detected applications, with Synchronization Validation enabled. This approach provides a lot of flexibility at runtime, including the ability to have VkConfig break the debugger on encountering an error, and also eliminates the need for validation layer specific code in the applications.
7+
As [suggested](https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/main/docs/khronos_validation_layer.md#vkconfig) by the Khronos Group, the guide strongly recommends using [Vulkan Configurator (GUI)](https://github.com/LunarG/VulkanTools/tree/main/vkconfig_gui) for validation layers. It is included in the Vulkan SDK, just keep it running while developing Vulkan applications, and ensure it is setup to inject validation layers into all detected applications, with Synchronization Validation enabled. This approach provides a lot of flexibility at runtime, including the ability to have VkConfig break the debugger on encountering an error, and also eliminates the need for validation layer specific code in the applications.
88

9-
> Note: modify your development (or desktop) environment's `PATH` (or use `LD_LIBRARY_PATH` on supported systems) to make sure the SDK's binaries are visible first.
9+
> Note: modify your development (or desktop) environment's `PATH` (or use `LD_LIBRARY_PATH` on supported systems) to make sure the SDK's binaries (shared libraries) are visible first.
10+
11+
![Vulkan Configurator](./vkconfig_gui.png)
152 KB
Loading

guide/src/initialization/device.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Vulkan Device
22

3-
A [Vulkan Device](https://registry.khronos.org/vulkan/specs/latest/man/html/VkDevice.html) is a logical instance of a Physical Device, and will the primary interface for everything Vulkan now onwards. [Vulkan Queues](https://registry.khronos.org/vulkan/specs/latest/man/html/VkQueue.html) are owned by the Device, we will need one from the queue family stored in the `Gpu`, to submit recorded command buffers. We also need to explicitly declare all features we want to use, eg [Dynamic Rendering](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_dynamic_rendering.html) and [Synchronization2](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_synchronization2.html).
3+
A [Vulkan Device](https://registry.khronos.org/vulkan/specs/latest/man/html/VkDevice.html) is a logical instance of a Physical Device, and will the primary interface for everything Vulkan now onwards. [Vulkan Queues](https://registry.khronos.org/vulkan/specs/latest/man/html/VkQueue.html) are owned by the Device, we will need one from the queue family stored in the `Gpu` to submit recorded command buffers. We also need to explicitly declare all features we want to use, eg [Dynamic Rendering](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_dynamic_rendering.html) and [Synchronization2](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_synchronization2.html).
44

55
Setup a `vk::QueueCreateInfo` object:
66

guide/src/initialization/glfw_window.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# GLFW Window
22

3-
We will use GLFW (3.4) for windowing and related events. The library - like all external dependencies - is configured and added to the build tree in `ext/CMakeLists.txt`. `GLFW_INCLUDE_VULKAN` is defined for all consumers, to enable GLFW's Vulkan related functions (known as **Window System Integration (WSI)**). GLFW 3.4 supports Wayland on Linux, and by default it builds backends for both X11 and Wayland. For this reason it will need the development headers for both platforms (and some other Wayland/CMake dependencies) to configure/build successfully. A particular backend can be requested at runtime if desired via `GLFW_PLATFORM`.
3+
We will use GLFW (3.4) for windowing and related events. The library - like all external dependencies - is configured and added to the build tree in `ext/CMakeLists.txt`. `GLFW_INCLUDE_VULKAN` is defined for all consumers, to enable GLFW's Vulkan related functions (known as **Window System Integration (WSI)**). GLFW 3.4 supports Wayland on Linux, and by default it builds backends for both X11 and Wayland. For this reason it will need the development packages for [both platforms](https://www.glfw.org/docs/latest/compile_guide.html#compile_deps_wayland) (and some other Wayland/CMake dependencies) to configure/build successfully. A particular backend can be requested at runtime if desired via `GLFW_PLATFORM`.
44

5-
Although it is quite feasible to have multiple windows in a Vulkan-GLFW application, that is out of scope of this guide. So for our purposes GLFW (the library) and a single window are a monolithic unit - initialized and destroyed together. This can be encapsulated in a `std::unique_ptr` with a custom deleter, especially since GLFW returns an opaque pointer (`GLFWwindow*`).
5+
Although it is quite feasible to have multiple windows in a Vulkan-GLFW application, that is out of scope for this guide. For our purposes GLFW (the library) and a single window are a monolithic unit - initialized and destroyed together. This can be encapsulated in a `std::unique_ptr` with a custom deleter, especially since GLFW returns an opaque pointer (`GLFWwindow*`).
66

77
```cpp
88
// window.hpp

guide/src/initialization/instance.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Instead of linking to Vulkan (via the SDK) at build-time, we will load Vulkan at runtime. This requires a few adjustments:
44

5-
1. In the CMake script `VK_NO_PROTOTYPES` is defined, which turns API function declarations into function pointers
5+
1. In the CMake ext target `VK_NO_PROTOTYPES` is defined, which turns API function declarations into function pointers
66
1. In `app.cpp` this line is added to the global scope: `VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE`
77
1. Before and during initialization `VULKAN_HPP_DEFAULT_DISPATCHER.init()` is called
88

@@ -76,6 +76,12 @@ If this line or equivalent is not visible in the logs, re-check your Vulkan Conf
7676
INFO | LAYER: Insert instance layer "VK_LAYER_KHRONOS_validation"
7777
```
7878

79+
For instance, if `libVkLayer_khronos_validation.so` / `VkLayer_khronos_validation.dll` is not visible to the app / loader, you'll see a line similar to:
80+
81+
```
82+
INFO | LAYER: Requested layer "VK_LAYER_KHRONOS_validation" failed to load.
83+
```
84+
7985
Congratulations, you have successfully initialized a Vulkan Instance!
8086

8187
> Wayland users: seeing the window is still a long way off, these VkConfig/validation logs are your only feedback for now.

guide/src/initialization/scoped_waiter.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class Scoped {
3535
Deleter{}(m_t);
3636
}
3737

38-
[[nodiscard]] auto get() const -> Type const& { return m_t; }
39-
[[nodiscard]] auto get() -> Type& { return m_t; }
38+
[[nodiscard]] constexpr auto get() const -> Type const& { return m_t; }
39+
[[nodiscard]] constexpr auto get() -> Type& { return m_t; }
4040

4141
private:
4242
Type m_t{};

guide/src/initialization/swapchain.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ get_surface_format(std::span<vk::SurfaceFormatKHR const> supported)
6464
}
6565
```
6666

67-
An sRGB format is preferred because that is what the screen's color space is in. This is indicated by the fact that the only core [Color Format](https://registry.khronos.org/vulkan/specs/latest/man/html/VkColorSpaceKHR.html) is `vk::ColorSpaceKHR::eVkColorspaceSrgbNonlinear`, which specifies support for the images in sRGB color space.
67+
An sRGB format is preferred because that is what the screen's color space is in. This is indicated by the fact that the only core [Color Space](https://registry.khronos.org/vulkan/specs/latest/man/html/VkColorSpaceKHR.html) is `vk::ColorSpaceKHR::eVkColorspaceSrgbNonlinear`, which specifies support for the images in sRGB color space.
6868

6969
The constructor can now be implemented:
7070

guide/src/rendering/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Rendering
22

3-
This section implements Render Sync, the Swapchain loop, performs Swapchain image layout transitions, and introduces Dynamic Rendering.
3+
This section implements Render Sync, the Swapchain loop, performs Swapchain image layout transitions, and introduces [Dynamic Rendering](https://docs.vulkan.org/samples/latest/samples/extensions/dynamic_rendering/README.html). Originally Vulkan only supported [Render Passes](https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/02_Graphics_pipeline_basics/03_Render_passes.html), which are quite verbose to setup, require somewhat confusing subpass dependencies, and are ironically _less_ explicit: they can perform implicit layout transitions on their framebuffer attachments. They are also tightly coupled to Graphics Pipelines, you need a separate pipeline object for each Render Pass, even if they are identical in all other respects. This RenderPass/Subpass model was primarily beneficial for GPUs with tiled renderers, and in Vulkan 1.3 Dynamic Rendering was promoted to the core API (previously it was an extension) as an alternative to using Render Passes.

guide/src/rendering/dynamic_rendering.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void App::transition_for_render(vk::CommandBuffer const command_buffer) const {
102102
}
103103
```
104104
105-
Create an Rendering Attachment Info using the acquired image as the color target. We use a red clear color, make sure the Load Op clears the image, and Store Op stores the results (currently just the cleared image). Set up a Rendering Info object with the color attachment and the entire image as the render area. Finally, execute the render:
105+
Create a Rendering Attachment Info using the acquired image as the color target. We use a red clear color, make sure the Load Op clears the image, and Store Op stores the results (currently just the cleared image). Set up a Rendering Info object with the color attachment and the entire image as the render area. Finally, execute the render:
106106
107107
```cpp
108108
void App::render(vk::CommandBuffer const command_buffer) {

guide/src/rendering/swapchain_loop.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
One part of rendering in the main loop is the Swapchain loop, which at a high level comprises of these steps:
44

5-
1. Acquire a Swapchain Image (and its view)
5+
1. Acquire a Swapchain Image
66
1. Render to the acquired Image
77
1. Present the Image (this releases the image back to the Swapchain)
88

@@ -12,16 +12,17 @@ There are a few nuances to deal with, for instance:
1212

1313
1. Acquiring (and/or presenting) will sometimes fail (eg because the Swapchain is out of date), in which case the remaining steps need to be skipped
1414
1. The acquire command can return before the image is actually ready for use, rendering needs to be synchronized to only start after the image is ready
15+
1. Similarly, presentation needs to be synchronized to only occur after rendering has completed
1516
1. The images need appropriate Layout Transitions at each stage
1617

17-
Additionally, the number of swapchain images can vary, whereas the engine should use a fixed number of _virtual frames_: 2 for double buffering, 3 for triple (more is usually overkill). It's also possible for the main loop to acquire the same image before a previous render command has finished (or even started), if the Swapchain is using Mailbox Present Mode. While FIFO will block until the oldest submitted image is available (also known as vsync), we should still synchronize and wait until the acquired image has finished rendering.
18+
Additionally, the number of swapchain images can vary, whereas the engine should use a fixed number of _virtual frames_: 2 for double buffering, 3 for triple (more is usually overkill). More info is available [here](https://docs.vulkan.org/samples/latest/samples/performance/swapchain_images/README.html#_double_buffering_or_triple_buffering). It's also possible for the main loop to acquire the same image before a previous render command has finished (or even started), if the Swapchain is using Mailbox Present Mode. While FIFO will block until the oldest submitted image is available (also known as vsync), we should still synchronize and wait until the acquired image has finished rendering.
1819

1920
## Virtual Frames
2021

21-
All the dynamic resources used during the rendering of a frame comprise a virtual frame. The application has a fixed number of virtual frames which it cycles through on each render pass. Each frame will be associated with a `vk::Fence` which will be waited on before rendering to it again. It will also have a pair of `vk::Semaphore`s to synchronize the acquire, render, and present calls on the GPU (we don't need to wait for them in the code). Lastly, there will be a Command Buffer per virtual frame, where all rendering commands for that frame (including layout transitions) will be recorded.
22+
All the dynamic resources used during the rendering of a frame comprise a virtual frame. The application has a fixed number of virtual frames which it cycles through on each render pass. For synchronization, each frame will be associated with a [`vk::Fence`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkFence.html) which will be waited on before rendering to it again. It will also have a pair of [`vk::Semaphore`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkSemaphore.html)s to synchronize the acquire, render, and present calls on the GPU (we don't need to wait for them on the CPU side / in C++). For recording commands, there will be a [`vk::CommandBuffer`](https://docs.vulkan.org/spec/latest/chapters/cmdbuffers.html) per virtual frame, where all rendering commands for that frame (including layout transitions) will be recorded.
2223

2324
## Image Layouts
2425

25-
Vulkan Images have a property known as Image Layout. Most operations on images require them to be in certain specific layouts, requiring transitions before (and after). A layout transition conveniently also functions as a Pipeline Barrier (think memory barrier on the GPU), enabling us to synchronize operations before and after the transition.
26+
Vulkan Images have a property known as [Image Layout](https://docs.vulkan.org/spec/latest/chapters/resources.html#resources-image-layouts). Most operations on images and their subresources require them to be in certain specific layouts, requiring transitions before (and after). A layout transition conveniently also functions as a Pipeline Barrier (think memory barrier on the GPU), enabling us to synchronize operations before and after the transition.
2627

2728
Vulkan Synchronization is arguably the most complicated aspect of the API, a good amount of research is recommended. Here is an [article explaining barriers](https://gpuopen.com/learn/vulkan-barriers-explained/).

guide/src/shader_objects/glsl_to_spir_v.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# GLSL to SPIR-V
22

3-
Shaders work in NDC space: -1 to +1 for X and Y. We set up a triangle's coordinates and output that in the vertex shader save it to `src/glsl/shader.vert`:
3+
Shaders work in NDC space: -1 to +1 for X and Y. We output a triangle's coordinates in a new vertex shader and save it to `src/glsl/shader.vert`:
44

55
```glsl
66
#version 450 core

guide/src/shader_objects/locating_assets.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Locating Assets
22

3-
Before we can use shaders (and thus graphics pipelines), we need to load them as asset/data files. To do that correctly, first the asset directory needs to be located. There are a few ways to go about this, we will use the approach of looking for a particular subdirectory, starting from the working directory and walking up the parent directory tree. This enables `app` in any project/build subdirectory to locate `assets/` in the various examples below:
3+
Before we can use shaders, we need to load them as asset/data files. To do that correctly, first the asset directory needs to be located. There are a few ways to go about this, we will use the approach of looking for a particular subdirectory, starting from the working directory and walking up the parent directory tree. This enables `app` in any project/build subdirectory to locate `assets/` in the various examples below:
44

55
```
66
.
@@ -40,7 +40,7 @@ Add a helper function to locate the assets dir, and assign `m_assets_dir` to its
4040
auto ret = path / dir_name_v;
4141
if (fs::is_directory(ret)) { return ret; }
4242
}
43-
std::println("[lvk] Warning: could not locate 'assets' directory");
43+
std::println("[lvk] Warning: could not locate '{}' directory", dir_name_v);
4444
return fs::current_path();
4545
}
4646

guide/src/shader_objects/shader_program.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ static constexpr auto flags_v = AlphaBlend | DepthTest;
167167
std::uint8_t flags{flags_v};
168168
```
169169

170-
There is one more piece of pipeline state needed: vertex input. We will consider this to be constant per shader and take it in the constructor:
170+
There is one more piece of pipeline state needed: vertex input. We will consider this to be constant per shader and store it in the constructor:
171171

172172
```cpp
173173
// shader_program.hpp

src/app.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace {
2020
auto ret = path / dir_name_v;
2121
if (fs::is_directory(ret)) { return ret; }
2222
}
23-
std::println("[lvk] Warning: could not locate 'assets' directory");
23+
std::println("[lvk] Warning: could not locate '{}' directory", dir_name_v);
2424
return fs::current_path();
2525
}
2626

src/scoped.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ class Scoped {
3030
Deleter{}(m_t);
3131
}
3232

33-
[[nodiscard]] auto get() const -> Type const& { return m_t; }
34-
[[nodiscard]] auto get() -> Type& { return m_t; }
33+
[[nodiscard]] constexpr auto get() const -> Type const& { return m_t; }
34+
[[nodiscard]] constexpr auto get() -> Type& { return m_t; }
3535

3636
private:
3737
Type m_t{};

0 commit comments

Comments
 (0)