From 55de73a963d8f58b52f6d32f065debbddec8889f Mon Sep 17 00:00:00 2001 From: Ozan Yasin Dogan Date: Sun, 9 Aug 2026 00:17:12 +0300 Subject: [PATCH 1/2] Fix segfault on window close: let GLFW outlive the Vulkan objects `cleanup()` called `glfwDestroyWindow()` and `glfwTerminate()` while every `vk::raii` member was still alive. Those members belong to the application class, so they are destroyed only after `run()` returns - that is, after GLFW has already torn down the window system connection they still reference. `vkDestroySwapchainKHR` then marshals a request on freed Wayland proxies and the process dies during exit: #12 HelloTriangleApplication::~HelloTriangleApplication() #11 vk::raii::SwapchainKHR::~SwapchainKHR() #10 vk::raii::SwapchainKHR::clear() #4 libnvidia-glcore.so #3 wl_proxy_marshal_flags #0 libwayland-client.so.0 <- SEGV_MAPERR The pre-RAII tutorial destroyed every Vulkan object by hand at the end of `cleanup()`, so `glfwTerminate()` genuinely ran last. Converting to `vk::raii` removed those explicit calls but left the two GLFW calls behind, silently inverting the order. Ownership of the GLFW lifetime now belongs to a `GlfwGuard` member declared first in the class. Members are destroyed in reverse declaration order, so being first means it is destroyed last - after every Vulkan object, however many later chapters add. `glfwTerminate()` also destroys any windows still open, so the separate `glfwDestroyWindow()` call is no longer needed. Applied to all 34 affected chapters, plus 00_base_code so the pattern is correct from the very first chapter and never has to change again - which is what the Base Code chapter already promises the reader. Verified on Arch Linux / Wayland / KWin, NVIDIA 610.43.03, Vulkan 1.4.357: - all 304 build targets compile - 22 chapters that present a frame: launched and closed with a real compositor close request, all exit 0 (each exited 139 before) - 14_command_buffers (never presents, so its surface is never mapped and it cannot be closed by the compositor) checked by breaking out of the main loop instead: 3/3 SIGSEGV before, 3/3 clean after - no validation layer errors, and no coredumps from any fixed binary Refs: #138 --- attachments/00_base_code.cpp | 18 ++++++-- attachments/01_instance_creation.cpp | 18 ++++++-- attachments/02_validation_layers.cpp | 18 ++++++-- attachments/03_physical_device_selection.cpp | 18 ++++++-- attachments/04_logical_device.cpp | 18 ++++++-- attachments/05_window_surface.cpp | 18 ++++++-- attachments/06_swap_chain_creation.cpp | 18 ++++++-- attachments/07_image_views.cpp | 18 ++++++-- attachments/08_graphics_pipeline.cpp | 18 ++++++-- attachments/09_shader_modules.cpp | 18 ++++++-- attachments/10_fixed_functions.cpp | 18 ++++++-- attachments/12_graphics_pipeline_complete.cpp | 18 ++++++-- attachments/14_command_buffers.cpp | 18 ++++++-- attachments/15_hello_triangle.cpp | 18 ++++++-- attachments/16_frames_in_flight.cpp | 18 ++++++-- attachments/17_swap_chain_recreation.cpp | 18 ++++++-- attachments/19_vertex_buffer.cpp | 18 ++++++-- attachments/20_staging_buffer.cpp | 18 ++++++-- attachments/21_index_buffer.cpp | 18 ++++++-- attachments/22_descriptor_layout.cpp | 18 ++++++-- attachments/23_descriptor_sets.cpp | 18 ++++++-- attachments/24_texture_image.cpp | 18 ++++++-- attachments/25_sampler.cpp | 18 ++++++-- attachments/26_texture_mapping.cpp | 18 ++++++-- attachments/27_depth_buffering.cpp | 18 ++++++-- attachments/28_model_loading.cpp | 18 ++++++-- attachments/29_mipmapping.cpp | 18 ++++++-- attachments/30_multisampling.cpp | 18 ++++++-- attachments/31_compute_shader.cpp | 15 +++++-- attachments/32_ecosystem_utilities.cpp | 14 ++++++- attachments/33_vulkan_profiles.cpp | 17 +++++++- attachments/35_gltf_ktx.cpp | 14 ++++++- attachments/36_multiple_objects.cpp | 16 ++++++-- attachments/37_multithreading.cpp | 15 +++++-- attachments/38_ray_tracing.cpp | 15 +++++-- .../00_Setup/00_Base_code.adoc | 41 +++++++++++++++---- .../04_Swap_chain_recreation.adoc | 6 +-- 37 files changed, 544 insertions(+), 113 deletions(-) diff --git a/attachments/00_base_code.cpp b/attachments/00_base_code.cpp index 3b685670d..6cbc61408 100644 --- a/attachments/00_base_code.cpp +++ b/attachments/00_base_code.cpp @@ -25,6 +25,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every member below. Later + // chapters add Vulkan objects here that still reference the window system + // connection while they are destroyed, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; void initWindow() @@ -51,9 +63,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every other + // member. Terminating GLFW here would free the window system connection + // while later chapters' Vulkan objects are still alive. } }; diff --git a/attachments/01_instance_creation.cpp b/attachments/01_instance_creation.cpp index 4686f1b66..91743a3b0 100644 --- a/attachments/01_instance_creation.cpp +++ b/attachments/01_instance_creation.cpp @@ -28,6 +28,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -58,9 +70,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/02_validation_layers.cpp b/attachments/02_validation_layers.cpp index 2d5ea7f80..6aa47b08e 100644 --- a/attachments/02_validation_layers.cpp +++ b/attachments/02_validation_layers.cpp @@ -39,6 +39,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -71,9 +83,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/03_physical_device_selection.cpp b/attachments/03_physical_device_selection.cpp index 2fa960441..58d15c911 100644 --- a/attachments/03_physical_device_selection.cpp +++ b/attachments/03_physical_device_selection.cpp @@ -39,6 +39,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -77,9 +89,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/04_logical_device.cpp b/attachments/04_logical_device.cpp index a8c285a33..4c2d9b3de 100644 --- a/attachments/04_logical_device.cpp +++ b/attachments/04_logical_device.cpp @@ -40,6 +40,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -82,9 +94,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/05_window_surface.cpp b/attachments/05_window_surface.cpp index bb7b4bee7..360b82c17 100644 --- a/attachments/05_window_surface.cpp +++ b/attachments/05_window_surface.cpp @@ -39,6 +39,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -80,9 +92,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/06_swap_chain_creation.cpp b/attachments/06_swap_chain_creation.cpp index d6bb77244..811b51286 100644 --- a/attachments/06_swap_chain_creation.cpp +++ b/attachments/06_swap_chain_creation.cpp @@ -41,6 +41,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -88,9 +100,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/07_image_views.cpp b/attachments/07_image_views.cpp index a6cb5c23b..a78abe612 100644 --- a/attachments/07_image_views.cpp +++ b/attachments/07_image_views.cpp @@ -41,6 +41,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -89,9 +101,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/08_graphics_pipeline.cpp b/attachments/08_graphics_pipeline.cpp index 8705c2016..04eb1c0d8 100644 --- a/attachments/08_graphics_pipeline.cpp +++ b/attachments/08_graphics_pipeline.cpp @@ -41,6 +41,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -90,9 +102,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/09_shader_modules.cpp b/attachments/09_shader_modules.cpp index 5e9ed2b4c..8a27fe319 100644 --- a/attachments/09_shader_modules.cpp +++ b/attachments/09_shader_modules.cpp @@ -42,6 +42,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -91,9 +103,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/10_fixed_functions.cpp b/attachments/10_fixed_functions.cpp index 2998d3aee..6ca5afeba 100644 --- a/attachments/10_fixed_functions.cpp +++ b/attachments/10_fixed_functions.cpp @@ -42,6 +42,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -93,9 +105,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/12_graphics_pipeline_complete.cpp b/attachments/12_graphics_pipeline_complete.cpp index c8d7a47db..12192a4ee 100644 --- a/attachments/12_graphics_pipeline_complete.cpp +++ b/attachments/12_graphics_pipeline_complete.cpp @@ -42,6 +42,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -94,9 +106,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/14_command_buffers.cpp b/attachments/14_command_buffers.cpp index badf1d532..e201a6f85 100644 --- a/attachments/14_command_buffers.cpp +++ b/attachments/14_command_buffers.cpp @@ -42,6 +42,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -99,9 +111,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/15_hello_triangle.cpp b/attachments/15_hello_triangle.cpp index 91f0e5c80..580ec2146 100644 --- a/attachments/15_hello_triangle.cpp +++ b/attachments/15_hello_triangle.cpp @@ -42,6 +42,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -107,9 +119,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/16_frames_in_flight.cpp b/attachments/16_frames_in_flight.cpp index 189c11338..abde61ed2 100644 --- a/attachments/16_frames_in_flight.cpp +++ b/attachments/16_frames_in_flight.cpp @@ -43,6 +43,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -110,9 +122,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void createInstance() diff --git a/attachments/17_swap_chain_recreation.cpp b/attachments/17_swap_chain_recreation.cpp index fcd6df655..f5bc48bf5 100644 --- a/attachments/17_swap_chain_recreation.cpp +++ b/attachments/17_swap_chain_recreation.cpp @@ -43,6 +43,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -126,9 +138,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/19_vertex_buffer.cpp b/attachments/19_vertex_buffer.cpp index 77721ad6d..94a73edb6 100644 --- a/attachments/19_vertex_buffer.cpp +++ b/attachments/19_vertex_buffer.cpp @@ -67,6 +67,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -154,9 +166,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/20_staging_buffer.cpp b/attachments/20_staging_buffer.cpp index 64980f7f4..d1e4b5e4b 100644 --- a/attachments/20_staging_buffer.cpp +++ b/attachments/20_staging_buffer.cpp @@ -67,6 +67,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -154,9 +166,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/21_index_buffer.cpp b/attachments/21_index_buffer.cpp index 957b735a4..d0d61c17e 100644 --- a/attachments/21_index_buffer.cpp +++ b/attachments/21_index_buffer.cpp @@ -71,6 +71,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -161,9 +173,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/22_descriptor_layout.cpp b/attachments/22_descriptor_layout.cpp index 833553111..742e26cc3 100644 --- a/attachments/22_descriptor_layout.cpp +++ b/attachments/22_descriptor_layout.cpp @@ -82,6 +82,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -179,9 +191,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/23_descriptor_sets.cpp b/attachments/23_descriptor_sets.cpp index 881abc5c4..73d0df825 100644 --- a/attachments/23_descriptor_sets.cpp +++ b/attachments/23_descriptor_sets.cpp @@ -82,6 +82,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -184,9 +196,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/24_texture_image.cpp b/attachments/24_texture_image.cpp index b8d61315d..846f90e5d 100644 --- a/attachments/24_texture_image.cpp +++ b/attachments/24_texture_image.cpp @@ -85,6 +85,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -191,9 +203,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/25_sampler.cpp b/attachments/25_sampler.cpp index 0608f1727..2e16b966d 100644 --- a/attachments/25_sampler.cpp +++ b/attachments/25_sampler.cpp @@ -85,6 +85,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -195,9 +207,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/26_texture_mapping.cpp b/attachments/26_texture_mapping.cpp index 268111919..f7b0a46be 100644 --- a/attachments/26_texture_mapping.cpp +++ b/attachments/26_texture_mapping.cpp @@ -86,6 +86,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -196,9 +208,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/27_depth_buffering.cpp b/attachments/27_depth_buffering.cpp index dfb24250e..c4cbbc0f9 100644 --- a/attachments/27_depth_buffering.cpp +++ b/attachments/27_depth_buffering.cpp @@ -94,6 +94,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -211,9 +223,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/28_model_loading.cpp b/attachments/28_model_loading.cpp index fe4ada523..635914207 100644 --- a/attachments/28_model_loading.cpp +++ b/attachments/28_model_loading.cpp @@ -100,6 +100,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -218,9 +230,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/29_mipmapping.cpp b/attachments/29_mipmapping.cpp index d80a73cb1..849de674d 100644 --- a/attachments/29_mipmapping.cpp +++ b/attachments/29_mipmapping.cpp @@ -100,6 +100,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -219,9 +231,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/30_multisampling.cpp b/attachments/30_multisampling.cpp index 9a312fa2b..6698f903d 100644 --- a/attachments/30_multisampling.cpp +++ b/attachments/30_multisampling.cpp @@ -100,6 +100,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -225,9 +237,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/31_compute_shader.cpp b/attachments/31_compute_shader.cpp index 67ffe9435..90e3a17e3 100644 --- a/attachments/31_compute_shader.cpp +++ b/attachments/31_compute_shader.cpp @@ -79,6 +79,18 @@ class ComputeShaderApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -194,9 +206,6 @@ class ComputeShaderApplication void cleanup() const { - glfwDestroyWindow(window); - - glfwTerminate(); } void recreateSwapChain() diff --git a/attachments/32_ecosystem_utilities.cpp b/attachments/32_ecosystem_utilities.cpp index a0a4c2d2f..3a7a3c474 100644 --- a/attachments/32_ecosystem_utilities.cpp +++ b/attachments/32_ecosystem_utilities.cpp @@ -106,6 +106,18 @@ class HelloTriangleApplication private: AppInfo appInfo; + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -254,8 +266,6 @@ class HelloTriangleApplication void cleanup() const { - glfwDestroyWindow(window); - glfwTerminate(); } void recreateSwapChain() diff --git a/attachments/33_vulkan_profiles.cpp b/attachments/33_vulkan_profiles.cpp index 4b1268e57..0835a1d56 100644 --- a/attachments/33_vulkan_profiles.cpp +++ b/attachments/33_vulkan_profiles.cpp @@ -103,6 +103,18 @@ class HelloTriangleApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -255,8 +267,9 @@ class HelloTriangleApplication void cleanup() { - glfwDestroyWindow(window); - glfwTerminate(); + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/35_gltf_ktx.cpp b/attachments/35_gltf_ktx.cpp index 777010f40..d3d656af7 100644 --- a/attachments/35_gltf_ktx.cpp +++ b/attachments/35_gltf_ktx.cpp @@ -264,6 +264,18 @@ class VulkanApplication return 0; } #else + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; #endif @@ -394,8 +406,6 @@ class VulkanApplication #if PLATFORM_DESKTOP void cleanup() const { - glfwDestroyWindow(window); - glfwTerminate(); } #endif diff --git a/attachments/36_multiple_objects.cpp b/attachments/36_multiple_objects.cpp index 9afc90037..6545de521 100644 --- a/attachments/36_multiple_objects.cpp +++ b/attachments/36_multiple_objects.cpp @@ -318,6 +318,18 @@ class VulkanApplication return 0; } #else + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; #endif @@ -465,10 +477,6 @@ class VulkanApplication gameObject.uniformBuffersMapped.clear(); gameObject.descriptorSets.clear(); } - - // Clean up GLFW resources - glfwDestroyWindow(window); - glfwTerminate(); } #endif diff --git a/attachments/37_multithreading.cpp b/attachments/37_multithreading.cpp index aa443369d..736e05d4a 100644 --- a/attachments/37_multithreading.cpp +++ b/attachments/37_multithreading.cpp @@ -164,6 +164,18 @@ class MultithreadedApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -555,9 +567,6 @@ class MultithreadedApplication void cleanup() { stopThreads(); - - glfwDestroyWindow(window); - glfwTerminate(); } void createInstance() diff --git a/attachments/38_ray_tracing.cpp b/attachments/38_ray_tracing.cpp index e0843931c..d8f393990 100644 --- a/attachments/38_ray_tracing.cpp +++ b/attachments/38_ray_tracing.cpp @@ -129,6 +129,18 @@ class VulkanRaytracingApplication } private: + // Declared first, so it is destroyed last - after every vk::raii member below. + // The swapchain and surface still reference the window system connection when + // their destructors run, so glfwTerminate() has to outlive them. It also + // destroys any windows that are still open. + struct GlfwGuard + { + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -301,9 +313,6 @@ class VulkanRaytracingApplication void cleanup() const { - glfwDestroyWindow(window); - - glfwTerminate(); } void recreateSwapChain() diff --git a/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc b/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc index eeaf07029..b8df105cd 100644 --- a/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc +++ b/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc @@ -293,20 +293,47 @@ This code should be fairly self-explanatory. It loops and checks for events like pressing the X button until the user has closed the window. This is also the loop where we'll later call a function to render a single frame. -Once the window is closed, we need to clean up resources by destroying it and -terminating GLFW itself. This will be our first `cleanup` code: +Once the window is closed we have to release it and shut GLFW down again. It is +tempting to do that in `cleanup`, but that would be a mistake. From the next +chapter on this class holds Vulkan objects, and those objects still talk to the +window system while they are being destroyed. Member variables are destroyed +*after* `run` returns, so anything `cleanup` tears down is released too early - +the swap chain would end up being destroyed after the connection it still points +at is gone, which crashes on exit. + +Instead we let a small guard own the GLFW lifetime. Members are destroyed in +reverse declaration order, so declaring it *first* means it is destroyed *last*, +after every Vulkan object we add in later chapters: [,c++] ---- -void cleanup() { - glfwDestroyWindow(window); +class HelloTriangleApplication { + // ... + +private: + // Declared first, so it is destroyed last. + struct GlfwGuard { + ~GlfwGuard() { glfwTerminate(); } + } glfwGuard; + + GLFWwindow* window = nullptr; + + // ... +}; +---- + +`glfwTerminate` also destroys any windows that are still open, so we don't need +a separate `glfwDestroyWindow` call. That leaves `cleanup` empty for now: - glfwTerminate(); +[,c++] +---- +void cleanup() { } ---- -Note that in this tutorial, this is the last time we'll have to do anything -in the cleanup() function. This code will never need to change again. +Note that in this tutorial, this is the last time we'll have to think about +tearing GLFW down. Because the guard is declared first, it keeps working no +matter how many Vulkan objects the later chapters add. When you run the program now, you should see a window titled `Vulkan` show up until the application is terminated by closing the window. Now that we have the diff --git a/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc b/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc index 3a942cdc4..7da3b682f 100644 --- a/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc +++ b/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc @@ -66,12 +66,12 @@ void cleanupSwapChain() void cleanup() { cleanupSwapChain(); - - glfwDestroyWindow(window); - glfwTerminate(); } ---- +GLFW is still torn down by `glfwGuard`, which is destroyed after every Vulkan +member, so `cleanup` must not terminate it here. + Note that in `chooseSwapExtent` we already query the new window resolution to make sure that the swap chain images have the (new) right size, so there's no need to modify `chooseSwapExtent` (remember that we already had to use `glfwGetFramebufferSize` to get the resolution of the surface in pixels when creating the swap chain). That's all it takes to recreate the swap chain! From 8d7abf5166268d2074c6fa65e64b1077852d7855 Mon Sep 17 00:00:00 2001 From: Ozan Yasin Dogan Date: Mon, 10 Aug 2026 16:10:20 +0300 Subject: [PATCH 2/2] Initialize GLFW in the guard's constructor and check the result Follow-up to review feedback on #443. `glfwInit()` was called at the top of `initWindow()` and its return value discarded in every chapter, so a failed initialization surfaced later as a null window rather than as an error. Moving it into `GlfwGuard` gives the guard both ends of the GLFW lifetime and gives the failure somewhere to go: GlfwGuard() { if (!glfwInit()) { throw std::runtime_error("failed to initialize GLFW!"); } } The constructor runs during member initialization, before `run()` is entered, and every chapter already constructs the application object inside the `try` in `main()`, so the error is reported through the same path as every other `std::runtime_error`. If it throws, `~GlfwGuard()` never runs, so `glfwTerminate()` is correctly skipped after a failed init. Also in this change: - 34_android.cpp gains the same guard. Its PLATFORM_DESKTOP path creates a GLFW window but never called `glfwTerminate()` or `glfwDestroyWindow()`, so it leaked GLFW rather than destroying it out of order. Every chapter that owns a window now uses the guard. - `cleanup()` was left as bare empty braces in 31_compute_shader, 32_ecosystem_utilities, 35_gltf_ktx and 38_ray_tracing, while 28 other chapters carried a comment explaining where GLFW teardown had moved to. Those four now match. - The guard in 32_ecosystem_utilities moved above `AppInfo appInfo` so that "declared first" is literally true there. - 00_Base_code.adoc introduces the guard where GLFW initialization is taught, instead of reintroducing it in the cleanup section, and 04_Swap_chain_recreation.adoc drops `glfwInit()` from its initWindow snippet. Verified on Windows 11, MSVC 14.51, Vulkan 1.4.357, RTX 5080: - all 144 build targets compile with no new warnings - clang-format-diff.py over the diff against main reports no violations - every chapter built from a pristine main and from this branch, then closed with a real WM_CLOSE and its exit code recorded: 36/36 exit 0 on both sides. Windows does not fault on the original ordering, so this measures no regression rather than a fix on that platform. Refs: #138 --- attachments/00_base_code.cpp | 19 +++-- attachments/01_instance_creation.cpp | 19 +++-- attachments/02_validation_layers.cpp | 19 +++-- attachments/03_physical_device_selection.cpp | 19 +++-- attachments/04_logical_device.cpp | 19 +++-- attachments/05_window_surface.cpp | 19 +++-- attachments/06_swap_chain_creation.cpp | 19 +++-- attachments/07_image_views.cpp | 19 +++-- attachments/08_graphics_pipeline.cpp | 19 +++-- attachments/09_shader_modules.cpp | 19 +++-- attachments/10_fixed_functions.cpp | 19 +++-- attachments/12_graphics_pipeline_complete.cpp | 19 +++-- attachments/14_command_buffers.cpp | 19 +++-- attachments/15_hello_triangle.cpp | 19 +++-- attachments/16_frames_in_flight.cpp | 19 +++-- attachments/17_swap_chain_recreation.cpp | 19 +++-- attachments/19_vertex_buffer.cpp | 19 +++-- attachments/20_staging_buffer.cpp | 19 +++-- attachments/21_index_buffer.cpp | 19 +++-- attachments/22_descriptor_layout.cpp | 19 +++-- attachments/23_descriptor_sets.cpp | 19 +++-- attachments/24_texture_image.cpp | 19 +++-- attachments/25_sampler.cpp | 19 +++-- attachments/26_texture_mapping.cpp | 19 +++-- attachments/27_depth_buffering.cpp | 19 +++-- attachments/28_model_loading.cpp | 19 +++-- attachments/29_mipmapping.cpp | 19 +++-- attachments/30_multisampling.cpp | 19 +++-- attachments/31_compute_shader.cpp | 22 +++-- attachments/32_ecosystem_utilities.cpp | 26 ++++-- attachments/33_vulkan_profiles.cpp | 18 +++-- attachments/34_android.cpp | 22 ++++- attachments/35_gltf_ktx.cpp | 22 +++-- attachments/36_multiple_objects.cpp | 19 +++-- attachments/37_multithreading.cpp | 19 +++-- attachments/38_ray_tracing.cpp | 22 +++-- .../00_Setup/00_Base_code.adoc | 81 ++++++++++--------- .../04_Swap_chain_recreation.adoc | 2 - 38 files changed, 534 insertions(+), 251 deletions(-) diff --git a/attachments/00_base_code.cpp b/attachments/00_base_code.cpp index 6cbc61408..763d9137b 100644 --- a/attachments/00_base_code.cpp +++ b/attachments/00_base_code.cpp @@ -25,12 +25,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every member below. Later - // chapters add Vulkan objects here that still reference the window system - // connection while they are destroyed, so glfwTerminate() has to outlive - // them. It also destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every member + // below. Later chapters add Vulkan objects here that still reference the + // window system connection while they are destroyed, so glfwTerminate() has + // to outlive them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -41,8 +50,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/01_instance_creation.cpp b/attachments/01_instance_creation.cpp index 91743a3b0..b6725deb6 100644 --- a/attachments/01_instance_creation.cpp +++ b/attachments/01_instance_creation.cpp @@ -28,12 +28,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -47,8 +56,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/02_validation_layers.cpp b/attachments/02_validation_layers.cpp index 6aa47b08e..9d87106ad 100644 --- a/attachments/02_validation_layers.cpp +++ b/attachments/02_validation_layers.cpp @@ -39,12 +39,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -59,8 +68,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/03_physical_device_selection.cpp b/attachments/03_physical_device_selection.cpp index 58d15c911..3d87f8564 100644 --- a/attachments/03_physical_device_selection.cpp +++ b/attachments/03_physical_device_selection.cpp @@ -39,12 +39,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -64,8 +73,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/04_logical_device.cpp b/attachments/04_logical_device.cpp index 4c2d9b3de..1a9ed4234 100644 --- a/attachments/04_logical_device.cpp +++ b/attachments/04_logical_device.cpp @@ -40,12 +40,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -68,8 +77,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/05_window_surface.cpp b/attachments/05_window_surface.cpp index 360b82c17..a5fd47770 100644 --- a/attachments/05_window_surface.cpp +++ b/attachments/05_window_surface.cpp @@ -39,12 +39,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -65,8 +74,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/06_swap_chain_creation.cpp b/attachments/06_swap_chain_creation.cpp index 811b51286..f11933e86 100644 --- a/attachments/06_swap_chain_creation.cpp +++ b/attachments/06_swap_chain_creation.cpp @@ -41,12 +41,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -72,8 +81,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/07_image_views.cpp b/attachments/07_image_views.cpp index a78abe612..4f865a0fe 100644 --- a/attachments/07_image_views.cpp +++ b/attachments/07_image_views.cpp @@ -41,12 +41,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -72,8 +81,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/08_graphics_pipeline.cpp b/attachments/08_graphics_pipeline.cpp index 04eb1c0d8..b1fd03933 100644 --- a/attachments/08_graphics_pipeline.cpp +++ b/attachments/08_graphics_pipeline.cpp @@ -41,12 +41,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -72,8 +81,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/09_shader_modules.cpp b/attachments/09_shader_modules.cpp index 8a27fe319..4c7b71a3d 100644 --- a/attachments/09_shader_modules.cpp +++ b/attachments/09_shader_modules.cpp @@ -42,12 +42,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -73,8 +82,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/10_fixed_functions.cpp b/attachments/10_fixed_functions.cpp index 6ca5afeba..612a5fcf7 100644 --- a/attachments/10_fixed_functions.cpp +++ b/attachments/10_fixed_functions.cpp @@ -42,12 +42,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -75,8 +84,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/12_graphics_pipeline_complete.cpp b/attachments/12_graphics_pipeline_complete.cpp index 12192a4ee..762ae6e99 100644 --- a/attachments/12_graphics_pipeline_complete.cpp +++ b/attachments/12_graphics_pipeline_complete.cpp @@ -42,12 +42,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -76,8 +85,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/14_command_buffers.cpp b/attachments/14_command_buffers.cpp index e201a6f85..754c7358f 100644 --- a/attachments/14_command_buffers.cpp +++ b/attachments/14_command_buffers.cpp @@ -42,12 +42,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -79,8 +88,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/15_hello_triangle.cpp b/attachments/15_hello_triangle.cpp index 580ec2146..b9e6e1786 100644 --- a/attachments/15_hello_triangle.cpp +++ b/attachments/15_hello_triangle.cpp @@ -42,12 +42,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -84,8 +93,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/16_frames_in_flight.cpp b/attachments/16_frames_in_flight.cpp index abde61ed2..dacd583e2 100644 --- a/attachments/16_frames_in_flight.cpp +++ b/attachments/16_frames_in_flight.cpp @@ -43,12 +43,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -86,8 +95,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); diff --git a/attachments/17_swap_chain_recreation.cpp b/attachments/17_swap_chain_recreation.cpp index f5bc48bf5..91c6e171e 100644 --- a/attachments/17_swap_chain_recreation.cpp +++ b/attachments/17_swap_chain_recreation.cpp @@ -43,12 +43,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -88,8 +97,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/19_vertex_buffer.cpp b/attachments/19_vertex_buffer.cpp index 94a73edb6..8df4ff01e 100644 --- a/attachments/19_vertex_buffer.cpp +++ b/attachments/19_vertex_buffer.cpp @@ -67,12 +67,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -115,8 +124,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/20_staging_buffer.cpp b/attachments/20_staging_buffer.cpp index d1e4b5e4b..94ae1da30 100644 --- a/attachments/20_staging_buffer.cpp +++ b/attachments/20_staging_buffer.cpp @@ -67,12 +67,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -115,8 +124,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/21_index_buffer.cpp b/attachments/21_index_buffer.cpp index d0d61c17e..d625a2917 100644 --- a/attachments/21_index_buffer.cpp +++ b/attachments/21_index_buffer.cpp @@ -71,12 +71,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -121,8 +130,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/22_descriptor_layout.cpp b/attachments/22_descriptor_layout.cpp index 742e26cc3..4b14ffe4c 100644 --- a/attachments/22_descriptor_layout.cpp +++ b/attachments/22_descriptor_layout.cpp @@ -82,12 +82,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -137,8 +146,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/23_descriptor_sets.cpp b/attachments/23_descriptor_sets.cpp index 73d0df825..97f504120 100644 --- a/attachments/23_descriptor_sets.cpp +++ b/attachments/23_descriptor_sets.cpp @@ -82,12 +82,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -140,8 +149,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/24_texture_image.cpp b/attachments/24_texture_image.cpp index 846f90e5d..91cc44ff5 100644 --- a/attachments/24_texture_image.cpp +++ b/attachments/24_texture_image.cpp @@ -85,12 +85,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -146,8 +155,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/25_sampler.cpp b/attachments/25_sampler.cpp index 2e16b966d..268c6f638 100644 --- a/attachments/25_sampler.cpp +++ b/attachments/25_sampler.cpp @@ -85,12 +85,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -148,8 +157,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/26_texture_mapping.cpp b/attachments/26_texture_mapping.cpp index f7b0a46be..01c9bd499 100644 --- a/attachments/26_texture_mapping.cpp +++ b/attachments/26_texture_mapping.cpp @@ -86,12 +86,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -149,8 +158,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/27_depth_buffering.cpp b/attachments/27_depth_buffering.cpp index c4cbbc0f9..0e0c7ef24 100644 --- a/attachments/27_depth_buffering.cpp +++ b/attachments/27_depth_buffering.cpp @@ -94,12 +94,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -163,8 +172,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/28_model_loading.cpp b/attachments/28_model_loading.cpp index 635914207..f4b58eb29 100644 --- a/attachments/28_model_loading.cpp +++ b/attachments/28_model_loading.cpp @@ -100,12 +100,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -169,8 +178,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/29_mipmapping.cpp b/attachments/29_mipmapping.cpp index 849de674d..05621ee19 100644 --- a/attachments/29_mipmapping.cpp +++ b/attachments/29_mipmapping.cpp @@ -100,12 +100,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -170,8 +179,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/30_multisampling.cpp b/attachments/30_multisampling.cpp index 6698f903d..ec42910b9 100644 --- a/attachments/30_multisampling.cpp +++ b/attachments/30_multisampling.cpp @@ -100,12 +100,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -175,8 +184,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/31_compute_shader.cpp b/attachments/31_compute_shader.cpp index 90e3a17e3..37b6b3916 100644 --- a/attachments/31_compute_shader.cpp +++ b/attachments/31_compute_shader.cpp @@ -79,12 +79,21 @@ class ComputeShaderApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -143,8 +152,6 @@ class ComputeShaderApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -206,6 +213,9 @@ class ComputeShaderApplication void cleanup() const { + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/32_ecosystem_utilities.cpp b/attachments/32_ecosystem_utilities.cpp index 3a7a3c474..1c54bc6f5 100644 --- a/attachments/32_ecosystem_utilities.cpp +++ b/attachments/32_ecosystem_utilities.cpp @@ -104,20 +104,29 @@ class HelloTriangleApplication } private: - AppInfo appInfo; - - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); } } glfwGuard; + AppInfo appInfo; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -188,8 +197,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -266,6 +273,9 @@ class HelloTriangleApplication void cleanup() const { + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/attachments/33_vulkan_profiles.cpp b/attachments/33_vulkan_profiles.cpp index 0835a1d56..b3c81576c 100644 --- a/attachments/33_vulkan_profiles.cpp +++ b/attachments/33_vulkan_profiles.cpp @@ -103,12 +103,21 @@ class HelloTriangleApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -181,7 +190,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/34_android.cpp b/attachments/34_android.cpp index c32c63610..2005245de 100644 --- a/attachments/34_android.cpp +++ b/attachments/34_android.cpp @@ -268,7 +268,6 @@ class HelloTriangleApplication // Initialize window (Desktop only) void initWindow() { - glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -323,6 +322,27 @@ class HelloTriangleApplication AssetManagerType *assetManager = nullptr; #else // Desktop-specific members + // glfwGuard owns the GLFW lifetime: it initialises GLFW here and terminates it + // in the destructor. Declared before the vk::raii members below, so it is + // destroyed after them - the swapchain and surface still reference the window + // system connection when their destructors run. It also destroys any windows + // that are still open. + struct GlfwGuard + { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + + ~GlfwGuard() + { + glfwTerminate(); + } + } glfwGuard; + GLFWwindow *window = nullptr; #endif bool initialized = false; diff --git a/attachments/35_gltf_ktx.cpp b/attachments/35_gltf_ktx.cpp index d3d656af7..b1b2c5a97 100644 --- a/attachments/35_gltf_ktx.cpp +++ b/attachments/35_gltf_ktx.cpp @@ -264,12 +264,21 @@ class VulkanApplication return 0; } #else - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -339,8 +348,6 @@ class VulkanApplication #if PLATFORM_DESKTOP void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -406,6 +413,9 @@ class VulkanApplication #if PLATFORM_DESKTOP void cleanup() const { + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } #endif diff --git a/attachments/36_multiple_objects.cpp b/attachments/36_multiple_objects.cpp index 6545de521..425c01668 100644 --- a/attachments/36_multiple_objects.cpp +++ b/attachments/36_multiple_objects.cpp @@ -318,12 +318,21 @@ class VulkanApplication return 0; } #else - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -391,8 +400,6 @@ class VulkanApplication #if PLATFORM_DESKTOP void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/37_multithreading.cpp b/attachments/37_multithreading.cpp index 736e05d4a..d26252221 100644 --- a/attachments/37_multithreading.cpp +++ b/attachments/37_multithreading.cpp @@ -164,12 +164,21 @@ class MultithreadedApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -318,8 +327,6 @@ class MultithreadedApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); diff --git a/attachments/38_ray_tracing.cpp b/attachments/38_ray_tracing.cpp index d8f393990..a8debf558 100644 --- a/attachments/38_ray_tracing.cpp +++ b/attachments/38_ray_tracing.cpp @@ -129,12 +129,21 @@ class VulkanRaytracingApplication } private: - // Declared first, so it is destroyed last - after every vk::raii member below. - // The swapchain and surface still reference the window system connection when - // their destructors run, so glfwTerminate() has to outlive them. It also - // destroys any windows that are still open. + // Owns the GLFW lifetime: initialises it here and terminates it in the + // destructor. Declared first, so it is destroyed last - after every vk::raii + // member below. The swapchain and surface still reference the window system + // connection when their destructors run, so glfwTerminate() has to outlive + // them. It also destroys any windows that are still open. struct GlfwGuard { + GlfwGuard() + { + if (!glfwInit()) + { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + ~GlfwGuard() { glfwTerminate(); @@ -252,8 +261,6 @@ class VulkanRaytracingApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); @@ -313,6 +320,9 @@ class VulkanRaytracingApplication void cleanup() const { + // GLFW is torn down by glfwGuard, which is destroyed after every vk::raii + // member. Destroying the window or terminating GLFW here would free the + // window system connection while the swapchain and surface are still alive. } void recreateSwapChain() diff --git a/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc b/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc index b8df105cd..ffbc53dc7 100644 --- a/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc +++ b/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc @@ -208,10 +208,34 @@ private: } ---- -The very first call in `initWindow` should be `glfwInit()`, which initializes -the GLFW library. Because GLFW was originally designed to create an OpenGL -context, we need to tell it to not create an OpenGL context with a later -call: +Before we can open a window, the GLFW library has to be initialized with +`glfwInit()`, and it has to be shut down again with `glfwTerminate()` once we +are done with it. Instead of calling those by hand, we'll let a small guard +object own that lifetime, so GLFW is always shut down no matter how the program +exits. Add it as the *first* private member of the class: + +[,c++] +---- +private: + struct GlfwGuard { + GlfwGuard() { + if (!glfwInit()) { + throw std::runtime_error("failed to initialize GLFW!"); + } + } + + ~GlfwGuard() { glfwTerminate(); } + } glfwGuard; +---- + +Because it is a member, its constructor runs before `run` is ever called, so +GLFW is ready by the time `initWindow` executes. It also gives us somewhere to +check the result: `glfwInit` returns false if the library could not be +initialized. Why it has to be declared *first* is something we'll come back to +when we get to `cleanup`. + +Because GLFW was originally designed to create an OpenGL context, we need to +tell it to not create an OpenGL context with a window hint: [,c++] ---- @@ -268,8 +292,6 @@ You should now have a `initWindow` function that looks like this: [,c++] ---- void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -293,37 +315,22 @@ This code should be fairly self-explanatory. It loops and checks for events like pressing the X button until the user has closed the window. This is also the loop where we'll later call a function to render a single frame. -Once the window is closed we have to release it and shut GLFW down again. It is -tempting to do that in `cleanup`, but that would be a mistake. From the next -chapter on this class holds Vulkan objects, and those objects still talk to the -window system while they are being destroyed. Member variables are destroyed -*after* `run` returns, so anything `cleanup` tears down is released too early - -the swap chain would end up being destroyed after the connection it still points -at is gone, which crashes on exit. - -Instead we let a small guard own the GLFW lifetime. Members are destroyed in -reverse declaration order, so declaring it *first* means it is destroyed *last*, -after every Vulkan object we add in later chapters: - -[,c++] ----- -class HelloTriangleApplication { - // ... - -private: - // Declared first, so it is destroyed last. - struct GlfwGuard { - ~GlfwGuard() { glfwTerminate(); } - } glfwGuard; - - GLFWwindow* window = nullptr; - - // ... -}; ----- - -`glfwTerminate` also destroys any windows that are still open, so we don't need -a separate `glfwDestroyWindow` call. That leaves `cleanup` empty for now: +Once the window is closed we have to release it and shut GLFW down again. That +already happens on its own: `glfwGuard` is a member, so its destructor runs when +the application object is destroyed, and `glfwTerminate` also destroys any +windows that are still open. This is why we don't need a separate +`glfwDestroyWindow` call. + +What matters is *when* that happens, and this is where the declaration order +comes in. From the next chapter on, this class holds Vulkan objects, and those +objects still talk to the window system while they are being destroyed. Members +are destroyed in reverse declaration order, so declaring the guard *first* means +it is destroyed *last* - after every Vulkan object we add later. + +Terminating GLFW in `cleanup` instead would break exactly that. `run` calls +`cleanup` before any member is destroyed, so the connection to the window system +would be gone while the swap chain still pointed at it, and the program would +crash on exit. So `cleanup` stays empty for now: [,c++] ---- diff --git a/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc b/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc index 7da3b682f..d5bbe1b94 100644 --- a/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc +++ b/en/03_Drawing_a_triangle/04_Swap_chain_recreation.adoc @@ -221,8 +221,6 @@ Now, to actually detect resizes, we can use the `glfwSetFramebufferSizeCallback` ---- void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);