diff --git a/attachments/00_base_code.cpp b/attachments/00_base_code.cpp index 3b685670..763d9137 100644 --- a/attachments/00_base_code.cpp +++ b/attachments/00_base_code.cpp @@ -25,12 +25,31 @@ class HelloTriangleApplication } private: + // 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(); + } + } glfwGuard; + GLFWwindow *window = nullptr; void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -51,9 +70,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 4686f1b6..b6725deb 100644 --- a/attachments/01_instance_creation.cpp +++ b/attachments/01_instance_creation.cpp @@ -28,6 +28,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -35,8 +56,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -58,9 +77,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 2d5ea7f8..9d87106a 100644 --- a/attachments/02_validation_layers.cpp +++ b/attachments/02_validation_layers.cpp @@ -39,6 +39,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -47,8 +68,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -71,9 +90,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 2fa96044..3d87f856 100644 --- a/attachments/03_physical_device_selection.cpp +++ b/attachments/03_physical_device_selection.cpp @@ -39,6 +39,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -52,8 +73,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -77,9 +96,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 a8c285a3..1a9ed423 100644 --- a/attachments/04_logical_device.cpp +++ b/attachments/04_logical_device.cpp @@ -40,6 +40,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -56,8 +77,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -82,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/05_window_surface.cpp b/attachments/05_window_surface.cpp index bb7b4bee..a5fd4777 100644 --- a/attachments/05_window_surface.cpp +++ b/attachments/05_window_surface.cpp @@ -39,6 +39,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -53,8 +74,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -80,9 +99,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 d6bb7724..f11933e8 100644 --- a/attachments/06_swap_chain_creation.cpp +++ b/attachments/06_swap_chain_creation.cpp @@ -41,6 +41,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -60,8 +81,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -88,9 +107,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 a6cb5c23..4f865a0f 100644 --- a/attachments/07_image_views.cpp +++ b/attachments/07_image_views.cpp @@ -41,6 +41,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -60,8 +81,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -89,9 +108,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 8705c201..b1fd0393 100644 --- a/attachments/08_graphics_pipeline.cpp +++ b/attachments/08_graphics_pipeline.cpp @@ -41,6 +41,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -60,8 +81,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -90,9 +109,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 5e9ed2b4..4c7b71a3 100644 --- a/attachments/09_shader_modules.cpp +++ b/attachments/09_shader_modules.cpp @@ -42,6 +42,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -61,8 +82,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -91,9 +110,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 2998d3ae..612a5fcf 100644 --- a/attachments/10_fixed_functions.cpp +++ b/attachments/10_fixed_functions.cpp @@ -42,6 +42,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -63,8 +84,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -93,9 +112,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 c8d7a47d..762ae6e9 100644 --- a/attachments/12_graphics_pipeline_complete.cpp +++ b/attachments/12_graphics_pipeline_complete.cpp @@ -42,6 +42,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -64,8 +85,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -94,9 +113,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 badf1d53..754c7358 100644 --- a/attachments/14_command_buffers.cpp +++ b/attachments/14_command_buffers.cpp @@ -42,6 +42,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -67,8 +88,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -99,9 +118,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 91f0e5c8..b9e6e178 100644 --- a/attachments/15_hello_triangle.cpp +++ b/attachments/15_hello_triangle.cpp @@ -42,6 +42,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -72,8 +93,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -107,9 +126,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 189c1133..dacd583e 100644 --- a/attachments/16_frames_in_flight.cpp +++ b/attachments/16_frames_in_flight.cpp @@ -43,6 +43,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -74,8 +95,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -110,9 +129,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 fcd6df65..91c6e171 100644 --- a/attachments/17_swap_chain_recreation.cpp +++ b/attachments/17_swap_chain_recreation.cpp @@ -43,6 +43,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -76,8 +97,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -126,9 +145,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 77721ad6..8df4ff01 100644 --- a/attachments/19_vertex_buffer.cpp +++ b/attachments/19_vertex_buffer.cpp @@ -67,6 +67,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -103,8 +124,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -154,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/20_staging_buffer.cpp b/attachments/20_staging_buffer.cpp index 64980f7f..94ae1da3 100644 --- a/attachments/20_staging_buffer.cpp +++ b/attachments/20_staging_buffer.cpp @@ -67,6 +67,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -103,8 +124,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -154,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/21_index_buffer.cpp b/attachments/21_index_buffer.cpp index 957b735a..d625a291 100644 --- a/attachments/21_index_buffer.cpp +++ b/attachments/21_index_buffer.cpp @@ -71,6 +71,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -109,8 +130,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -161,9 +180,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 83355311..4b14ffe4 100644 --- a/attachments/22_descriptor_layout.cpp +++ b/attachments/22_descriptor_layout.cpp @@ -82,6 +82,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -125,8 +146,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -179,9 +198,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 881abc5c..97f50412 100644 --- a/attachments/23_descriptor_sets.cpp +++ b/attachments/23_descriptor_sets.cpp @@ -82,6 +82,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -128,8 +149,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -184,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/24_texture_image.cpp b/attachments/24_texture_image.cpp index b8d61315..91cc44ff 100644 --- a/attachments/24_texture_image.cpp +++ b/attachments/24_texture_image.cpp @@ -85,6 +85,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -134,8 +155,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -191,9 +210,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 0608f172..268c6f63 100644 --- a/attachments/25_sampler.cpp +++ b/attachments/25_sampler.cpp @@ -85,6 +85,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -136,8 +157,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -195,9 +214,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 26811191..01c9bd49 100644 --- a/attachments/26_texture_mapping.cpp +++ b/attachments/26_texture_mapping.cpp @@ -86,6 +86,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -137,8 +158,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -196,9 +215,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 dfb24250..0e0c7ef2 100644 --- a/attachments/27_depth_buffering.cpp +++ b/attachments/27_depth_buffering.cpp @@ -94,6 +94,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -151,8 +172,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -211,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/28_model_loading.cpp b/attachments/28_model_loading.cpp index fe4ada52..f4b58eb2 100644 --- a/attachments/28_model_loading.cpp +++ b/attachments/28_model_loading.cpp @@ -100,6 +100,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -157,8 +178,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -218,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/29_mipmapping.cpp b/attachments/29_mipmapping.cpp index d80a73cb..05621ee1 100644 --- a/attachments/29_mipmapping.cpp +++ b/attachments/29_mipmapping.cpp @@ -100,6 +100,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -158,8 +179,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -219,9 +238,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 9a312fa2..ec42910b 100644 --- a/attachments/30_multisampling.cpp +++ b/attachments/30_multisampling.cpp @@ -100,6 +100,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -163,8 +184,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -225,9 +244,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 67ffe943..37b6b391 100644 --- a/attachments/31_compute_shader.cpp +++ b/attachments/31_compute_shader.cpp @@ -79,6 +79,27 @@ class ComputeShaderApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -131,8 +152,6 @@ class ComputeShaderApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -194,9 +213,9 @@ class ComputeShaderApplication void cleanup() const { - 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/32_ecosystem_utilities.cpp b/attachments/32_ecosystem_utilities.cpp index a0a4c2d2..1c54bc6f 100644 --- a/attachments/32_ecosystem_utilities.cpp +++ b/attachments/32_ecosystem_utilities.cpp @@ -104,6 +104,27 @@ class HelloTriangleApplication } private: + // 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; @@ -176,8 +197,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -254,8 +273,9 @@ class HelloTriangleApplication void cleanup() const { - 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/33_vulkan_profiles.cpp b/attachments/33_vulkan_profiles.cpp index 4b1268e5..b3c81576 100644 --- a/attachments/33_vulkan_profiles.cpp +++ b/attachments/33_vulkan_profiles.cpp @@ -103,6 +103,27 @@ class HelloTriangleApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -169,7 +190,6 @@ class HelloTriangleApplication void initWindow() { - glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -255,8 +275,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/34_android.cpp b/attachments/34_android.cpp index c32c6361..2005245d 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 777010f4..b1b2c5a9 100644 --- a/attachments/35_gltf_ktx.cpp +++ b/attachments/35_gltf_ktx.cpp @@ -264,6 +264,27 @@ class VulkanApplication return 0; } #else + // 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; + GLFWwindow *window = nullptr; #endif @@ -327,8 +348,6 @@ class VulkanApplication #if PLATFORM_DESKTOP void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -394,8 +413,9 @@ class VulkanApplication #if PLATFORM_DESKTOP void cleanup() const { - 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. } #endif diff --git a/attachments/36_multiple_objects.cpp b/attachments/36_multiple_objects.cpp index 9afc9003..425c0166 100644 --- a/attachments/36_multiple_objects.cpp +++ b/attachments/36_multiple_objects.cpp @@ -318,6 +318,27 @@ class VulkanApplication return 0; } #else + // 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; + GLFWwindow *window = nullptr; #endif @@ -379,8 +400,6 @@ class VulkanApplication #if PLATFORM_DESKTOP void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -465,10 +484,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 aa443369..d2625222 100644 --- a/attachments/37_multithreading.cpp +++ b/attachments/37_multithreading.cpp @@ -164,6 +164,27 @@ class MultithreadedApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; @@ -306,8 +327,6 @@ class MultithreadedApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); @@ -555,9 +574,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 e0843931..a8debf55 100644 --- a/attachments/38_ray_tracing.cpp +++ b/attachments/38_ray_tracing.cpp @@ -129,6 +129,27 @@ class VulkanRaytracingApplication } private: + // 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; + GLFWwindow *window = nullptr; vk::raii::Context context; @@ -240,8 +261,6 @@ class VulkanRaytracingApplication void initWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); @@ -301,9 +320,9 @@ class VulkanRaytracingApplication void cleanup() const { - 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/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc b/en/03_Drawing_a_triangle/00_Setup/00_Base_code.adoc index eeaf0702..ffbc53dc 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,20 +315,32 @@ 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. 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++] ---- void cleanup() { - glfwDestroyWindow(window); - - glfwTerminate(); } ---- -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 3a942cdc..d5bbe1b9 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! @@ -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);