Skip to content

Commit 5f72a43

Browse files
authored
Fixups for Rendering (#4)
* Guide: remove outdated `ScopedWaiter` from `Device` * Add more comments, fixup indents in guide (#3) * Add more comments, fixup indents in guide
1 parent 8447590 commit 5f72a43

File tree

15 files changed

+432
-381
lines changed

15 files changed

+432
-381
lines changed

guide/src/getting_started/class_app.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
// app.hpp
77
namespace lvk {
88
class App {
9-
public:
10-
void run();
9+
public:
10+
void run();
1111
};
1212
} // namespace lvk
1313

1414
// app.cpp
1515
namespace lvk {
1616
void App::run() {
17-
// TODO
17+
// TODO
1818
}
1919
} // namespace lvk
2020
```
@@ -26,14 +26,14 @@ void App::run() {
2626
```cpp
2727
// main.cpp
2828
auto main() -> int {
29-
try {
30-
lvk::App{}.run();
31-
} catch (std::exception const& e) {
32-
std::println(stderr, "PANIC: {}", e.what());
33-
return EXIT_FAILURE;
34-
} catch (...) {
35-
std::println("PANIC!");
36-
return EXIT_FAILURE;
37-
}
29+
try {
30+
lvk::App{}.run();
31+
} catch (std::exception const& e) {
32+
std::println(stderr, "PANIC: {}", e.what());
33+
return EXIT_FAILURE;
34+
} catch (...) {
35+
std::println("PANIC!");
36+
return EXIT_FAILURE;
37+
}
3838
}
3939
```

guide/src/initialization/device.md

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,62 @@ A [Vulkan Device](https://registry.khronos.org/vulkan/specs/latest/man/html/VkDe
55
Setup a `vk::QueueCreateInfo` object:
66

77
```cpp
8-
auto queue_ci = vk::DeviceQueueCreateInfo{};
9-
// since we use only one queue, it has the entire priority range, ie, 1.0
10-
static constexpr auto queue_priorities_v = std::array{1.0f};
11-
queue_ci.setQueueFamilyIndex(m_gpu.queue_family)
12-
.setQueueCount(1)
13-
.setQueuePriorities(queue_priorities_v);
8+
auto queue_ci = vk::DeviceQueueCreateInfo{};
9+
// since we use only one queue, it has the entire priority range, ie, 1.0
10+
static constexpr auto queue_priorities_v = std::array{1.0f};
11+
queue_ci.setQueueFamilyIndex(m_gpu.queue_family)
12+
.setQueueCount(1)
13+
.setQueuePriorities(queue_priorities_v);
1414
```
1515
1616
Setup the core device features:
1717
1818
```cpp
19-
auto enabled_features = vk::PhysicalDeviceFeatures{};
20-
enabled_features.fillModeNonSolid = m_gpu.features.fillModeNonSolid;
21-
enabled_features.wideLines = m_gpu.features.wideLines;
22-
enabled_features.samplerAnisotropy = m_gpu.features.samplerAnisotropy;
23-
enabled_features.sampleRateShading = m_gpu.features.sampleRateShading;
19+
// nice-to-have optional core features, enable if GPU supports them.
20+
auto enabled_features = vk::PhysicalDeviceFeatures{};
21+
enabled_features.fillModeNonSolid = m_gpu.features.fillModeNonSolid;
22+
enabled_features.wideLines = m_gpu.features.wideLines;
23+
enabled_features.samplerAnisotropy = m_gpu.features.samplerAnisotropy;
24+
enabled_features.sampleRateShading = m_gpu.features.sampleRateShading;
2425
```
2526

2627
Setup the additional features, using `setPNext()` to chain them:
2728

2829
```cpp
29-
auto sync_feature = vk::PhysicalDeviceSynchronization2Features{vk::True};
30-
auto dynamic_rendering_feature =
31-
vk::PhysicalDeviceDynamicRenderingFeatures{vk::True};
32-
sync_feature.setPNext(&dynamic_rendering_feature);
30+
// extra features that need to be explicitly enabled.
31+
auto sync_feature = vk::PhysicalDeviceSynchronization2Features{vk::True};
32+
auto dynamic_rendering_feature =
33+
vk::PhysicalDeviceDynamicRenderingFeatures{vk::True};
34+
// sync_feature.pNext => dynamic_rendering_feature,
35+
// and later device_ci.pNext => sync_feature.
36+
// this is 'pNext chaining'.
37+
sync_feature.setPNext(&dynamic_rendering_feature);
3338
```
3439
3540
Setup a `vk::DeviceCreateInfo` object:
3641
3742
```cpp
38-
auto device_ci = vk::DeviceCreateInfo{};
39-
static constexpr auto extensions_v =
40-
std::array{VK_KHR_SWAPCHAIN_EXTENSION_NAME};
41-
device_ci.setPEnabledExtensionNames(extensions_v)
42-
.setQueueCreateInfos(queue_ci)
43-
.setPEnabledFeatures(&enabled_features)
44-
.setPNext(&sync_feature);
43+
auto device_ci = vk::DeviceCreateInfo{};
44+
// we only need one device extension: Swapchain.
45+
static constexpr auto extensions_v =
46+
std::array{VK_KHR_SWAPCHAIN_EXTENSION_NAME};
47+
device_ci.setPEnabledExtensionNames(extensions_v)
48+
.setQueueCreateInfos(queue_ci)
49+
.setPEnabledFeatures(&enabled_features)
50+
.setPNext(&sync_feature);
4551
```
4652

4753
Declare a `vk::UniqueDevice` member after `m_gpu`, create it, and initialize the dispatcher against it:
4854

4955
```cpp
50-
m_device = m_gpu.device.createDeviceUnique(device_ci);
51-
VULKAN_HPP_DEFAULT_DISPATCHER.init(*m_device);
56+
m_device = m_gpu.device.createDeviceUnique(device_ci);
57+
// initialize the dispatcher against the created Device.
58+
VULKAN_HPP_DEFAULT_DISPATCHER.init(*m_device);
5259
```
5360

5461
Declare a `vk::Queue` member (order doesn't matter since it's just a handle, the actual Queue is owned by the Device) and initialize it:
5562

5663
```cpp
57-
static constexpr std::uint32_t queue_index_v{0};
58-
m_queue = m_device->getQueue(m_gpu.queue_family, queue_index_v);
64+
static constexpr std::uint32_t queue_index_v{0};
65+
m_queue = m_device->getQueue(m_gpu.queue_family, queue_index_v);
5966
```

guide/src/initialization/glfw_window.md

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Although it is quite feasible to have multiple windows in a Vulkan-GLFW applicat
88
// window.hpp
99
namespace lvk::glfw {
1010
struct Deleter {
11-
void operator()(GLFWwindow* window) const noexcept;
11+
void operator()(GLFWwindow* window) const noexcept;
1212
};
1313

1414
using Window = std::unique_ptr<GLFWwindow, Deleter>;
@@ -19,32 +19,32 @@ using Window = std::unique_ptr<GLFWwindow, Deleter>;
1919

2020
// window.cpp
2121
void Deleter::operator()(GLFWwindow* window) const noexcept {
22-
glfwDestroyWindow(window);
23-
glfwTerminate();
22+
glfwDestroyWindow(window);
23+
glfwTerminate();
2424
}
2525
```
2626
2727
GLFW can create fullscreen and borderless windows, but we will stick to a standard window with decorations. Since we cannot do anything useful if we are unable to create a window, all other branches throw a fatal exception (caught in main).
2828
2929
```cpp
3030
auto glfw::create_window(glm::ivec2 const size, char const* title) -> Window {
31-
static auto const on_error = [](int const code, char const* description) {
32-
std::println(stderr, "[GLFW] Error {}: {}", code, description);
33-
};
34-
glfwSetErrorCallback(on_error);
35-
if (glfwInit() != GLFW_TRUE) {
36-
throw std::runtime_error{"Failed to initialize GLFW"};
37-
}
38-
// check for Vulkan support.
39-
if (glfwVulkanSupported() != GLFW_TRUE) {
40-
throw std::runtime_error{"Vulkan not supported"};
41-
}
42-
auto ret = Window{};
43-
// tell GLFW that we don't want an OpenGL context.
44-
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
45-
ret.reset(glfwCreateWindow(size.x, size.y, title, nullptr, nullptr));
46-
if (!ret) { throw std::runtime_error{"Failed to create GLFW Window"}; }
47-
return ret;
31+
static auto const on_error = [](int const code, char const* description) {
32+
std::println(stderr, "[GLFW] Error {}: {}", code, description);
33+
};
34+
glfwSetErrorCallback(on_error);
35+
if (glfwInit() != GLFW_TRUE) {
36+
throw std::runtime_error{"Failed to initialize GLFW"};
37+
}
38+
// check for Vulkan support.
39+
if (glfwVulkanSupported() != GLFW_TRUE) {
40+
throw std::runtime_error{"Vulkan not supported"};
41+
}
42+
auto ret = Window{};
43+
// tell GLFW that we don't want an OpenGL context.
44+
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
45+
ret.reset(glfwCreateWindow(size.x, size.y, title, nullptr, nullptr));
46+
if (!ret) { throw std::runtime_error{"Failed to create GLFW Window"}; }
47+
return ret;
4848
}
4949
```
5050

@@ -53,35 +53,35 @@ auto glfw::create_window(glm::ivec2 const size, char const* title) -> Window {
5353
Declare it as a private member:
5454

5555
```cpp
56-
private:
57-
glfw::Window m_window{};
56+
private:
57+
glfw::Window m_window{};
5858
```
5959

6060
Add some private member functions to encapsulate each operation:
6161

6262
```cpp
63-
void create_window();
63+
void create_window();
6464

65-
void main_loop();
65+
void main_loop();
6666
```
6767

6868
Implement them and call them in `run()`:
6969

7070
```cpp
7171
void App::run() {
72-
create_window();
72+
create_window();
7373

74-
main_loop();
74+
main_loop();
7575
}
7676

7777
void App::create_window() {
78-
m_window = glfw::create_window({1280, 720}, "Learn Vulkan");
78+
m_window = glfw::create_window({1280, 720}, "Learn Vulkan");
7979
}
8080

8181
void App::main_loop() {
82-
while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) {
83-
glfwPollEvents();
84-
}
82+
while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) {
83+
glfwPollEvents();
84+
}
8585
}
8686
```
8787

guide/src/initialization/gpu.md

Lines changed: 59 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -14,82 +14,81 @@ We wrap the actual Physical Device and a few other useful objects into `struct G
1414
constexpr auto vk_version_v = VK_MAKE_VERSION(1, 3, 0);
1515

1616
struct Gpu {
17-
vk::PhysicalDevice device{};
18-
vk::PhysicalDeviceProperties properties{};
19-
vk::PhysicalDeviceFeatures features{};
20-
std::uint32_t queue_family{};
17+
vk::PhysicalDevice device{};
18+
vk::PhysicalDeviceProperties properties{};
19+
vk::PhysicalDeviceFeatures features{};
20+
std::uint32_t queue_family{};
2121
};
2222

2323
[[nodiscard]] auto get_suitable_gpu(vk::Instance instance,
24-
vk::SurfaceKHR surface) -> Gpu;
24+
vk::SurfaceKHR surface) -> Gpu;
2525
```
2626
2727
The implementation:
2828
2929
```cpp
3030
auto lvk::get_suitable_gpu(vk::Instance const instance,
31-
vk::SurfaceKHR const surface) -> Gpu {
32-
auto const supports_swapchain = [](Gpu const& gpu) {
33-
static constexpr std::string_view name_v =
34-
VK_KHR_SWAPCHAIN_EXTENSION_NAME;
35-
static constexpr auto is_swapchain =
36-
[](vk::ExtensionProperties const& properties) {
37-
return properties.extensionName.data() == name_v;
38-
};
39-
auto const properties = gpu.device.enumerateDeviceExtensionProperties();
40-
auto const it = std::ranges::find_if(properties, is_swapchain);
41-
return it != properties.end();
42-
};
43-
44-
auto const set_queue_family = [](Gpu& out_gpu) {
45-
static constexpr auto queue_flags_v =
46-
vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eTransfer;
47-
for (auto const [index, family] :
48-
std::views::enumerate(out_gpu.device.getQueueFamilyProperties())) {
49-
if ((family.queueFlags & queue_flags_v) == queue_flags_v) {
50-
out_gpu.queue_family = static_cast<std::uint32_t>(index);
51-
return true;
52-
}
53-
}
54-
return false;
55-
};
56-
57-
auto const can_present = [surface](Gpu const& gpu) {
58-
return gpu.device.getSurfaceSupportKHR(gpu.queue_family, surface) ==
59-
vk::True;
60-
};
61-
62-
auto fallback = Gpu{};
63-
for (auto const& device : instance.enumeratePhysicalDevices()) {
64-
auto gpu = Gpu{.device = device, .properties = device.getProperties()};
65-
if (gpu.properties.apiVersion < vk_version_v) { continue; }
66-
if (!supports_swapchain(gpu)) { continue; }
67-
if (!set_queue_family(gpu)) { continue; }
68-
if (!can_present(gpu)) { continue; }
69-
gpu.features = gpu.device.getFeatures();
70-
if (gpu.properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
71-
return gpu;
72-
}
73-
// keep iterating in case we find a Discrete Gpu later.
74-
fallback = gpu;
75-
}
76-
if (fallback.device) { return fallback; }
77-
78-
throw std::runtime_error{"No suitable Vulkan Physical Devices"};
31+
vk::SurfaceKHR const surface) -> Gpu {
32+
auto const supports_swapchain = [](Gpu const& gpu) {
33+
static constexpr std::string_view name_v =
34+
VK_KHR_SWAPCHAIN_EXTENSION_NAME;
35+
static constexpr auto is_swapchain =
36+
[](vk::ExtensionProperties const& properties) {
37+
return properties.extensionName.data() == name_v;
38+
};
39+
auto const properties = gpu.device.enumerateDeviceExtensionProperties();
40+
auto const it = std::ranges::find_if(properties, is_swapchain);
41+
return it != properties.end();
42+
};
43+
44+
auto const set_queue_family = [](Gpu& out_gpu) {
45+
static constexpr auto queue_flags_v =
46+
vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eTransfer;
47+
for (auto const [index, family] :
48+
std::views::enumerate(out_gpu.device.getQueueFamilyProperties())) {
49+
if ((family.queueFlags & queue_flags_v) == queue_flags_v) {
50+
out_gpu.queue_family = static_cast<std::uint32_t>(index);
51+
return true;
52+
}
53+
}
54+
return false;
55+
};
56+
57+
auto const can_present = [surface](Gpu const& gpu) {
58+
return gpu.device.getSurfaceSupportKHR(gpu.queue_family, surface) ==
59+
vk::True;
60+
};
61+
62+
auto fallback = Gpu{};
63+
for (auto const& device : instance.enumeratePhysicalDevices()) {
64+
auto gpu = Gpu{.device = device, .properties = device.getProperties()};
65+
if (gpu.properties.apiVersion < vk_version_v) { continue; }
66+
if (!supports_swapchain(gpu)) { continue; }
67+
if (!set_queue_family(gpu)) { continue; }
68+
if (!can_present(gpu)) { continue; }
69+
gpu.features = gpu.device.getFeatures();
70+
if (gpu.properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
71+
return gpu;
72+
}
73+
// keep iterating in case we find a Discrete Gpu later.
74+
fallback = gpu;
75+
}
76+
if (fallback.device) { return fallback; }
77+
78+
throw std::runtime_error{"No suitable Vulkan Physical Devices"};
7979
}
8080
```
8181

8282
Finally, add a `Gpu` member in `App` and initialize it after `create_surface()`:
8383

8484
```cpp
85-
create_surface();
86-
select_gpu();
87-
// ...
88-
85+
create_surface();
86+
select_gpu();
8987

88+
// ...
9089
void App::select_gpu() {
91-
m_gpu = get_suitable_gpu(*m_instance, *m_surface);
92-
std::println("Using GPU: {}",
93-
std::string_view{m_gpu.properties.deviceName});
90+
m_gpu = get_suitable_gpu(*m_instance, *m_surface);
91+
std::println("Using GPU: {}",
92+
std::string_view{m_gpu.properties.deviceName});
9493
}
9594
```

0 commit comments

Comments
 (0)