Handle resizing window

This recreates the swap-chain if the window is resized, as mentioned in
"Swap chain recreation".

(Note that the "Drawing: Frames in flight" step in the tutorial was
skipped.)
This commit is contained in:
Stephen Seo 2024-03-14 13:15:12 +09:00
parent e82aa002c4
commit f52c9029bc

View file

@ -1,7 +1,7 @@
mod ffi; mod ffi;
use std::collections::HashSet; use std::collections::HashSet;
use std::ffi::{CStr, CString}; use std::ffi::{c_void, CStr, CString};
const WINDOW_WIDTH: i32 = 800; const WINDOW_WIDTH: i32 = 800;
const WINDOW_HEIGHT: i32 = 600; const WINDOW_HEIGHT: i32 = 600;
@ -77,6 +77,13 @@ extern "C" fn validation_debug_callback(
ffi::VK_FALSE ffi::VK_FALSE
} }
extern "C" fn framebuffer_resize_callback(window: *mut ffi::GLFWwindow, _width: i32, _height: i32) {
unsafe {
let app: *mut VulkanApp = ffi::glfwGetWindowUserPointer(window) as *mut VulkanApp;
(*app).set_resize_flag();
}
}
fn create_debug_utils_messenger_ext( fn create_debug_utils_messenger_ext(
instance: ffi::VkInstance, instance: ffi::VkInstance,
create_info: *const ffi::VkDebugUtilsMessengerCreateInfoEXT, create_info: *const ffi::VkDebugUtilsMessengerCreateInfoEXT,
@ -180,6 +187,7 @@ struct VulkanApp {
image_available_semaphore: ffi::VkSemaphore, image_available_semaphore: ffi::VkSemaphore,
render_finished_semaphore: ffi::VkSemaphore, render_finished_semaphore: ffi::VkSemaphore,
in_flight_fence: ffi::VkFence, in_flight_fence: ffi::VkFence,
framebuffer_resized: bool,
} }
impl VulkanApp { impl VulkanApp {
@ -207,6 +215,7 @@ impl VulkanApp {
image_available_semaphore: std::ptr::null_mut(), image_available_semaphore: std::ptr::null_mut(),
render_finished_semaphore: std::ptr::null_mut(), render_finished_semaphore: std::ptr::null_mut(),
in_flight_fence: std::ptr::null_mut(), in_flight_fence: std::ptr::null_mut(),
framebuffer_resized: false,
} }
} }
@ -215,7 +224,7 @@ impl VulkanApp {
unsafe { unsafe {
ffi::glfwInit(); ffi::glfwInit();
ffi::glfwWindowHint(ffi::GLFW_CLIENT_API as i32, ffi::GLFW_NO_API as i32); ffi::glfwWindowHint(ffi::GLFW_CLIENT_API as i32, ffi::GLFW_NO_API as i32);
ffi::glfwWindowHint(ffi::GLFW_RESIZABLE as i32, ffi::GLFW_FALSE as i32); ffi::glfwWindowHint(ffi::GLFW_RESIZABLE as i32, ffi::GLFW_TRUE as i32);
self.window = ffi::glfwCreateWindow( self.window = ffi::glfwCreateWindow(
WINDOW_WIDTH, WINDOW_WIDTH,
WINDOW_HEIGHT, WINDOW_HEIGHT,
@ -226,6 +235,9 @@ impl VulkanApp {
if self.window.is_null() { if self.window.is_null() {
panic!("ERROR: Failed to create glfw window!"); panic!("ERROR: Failed to create glfw window!");
} }
ffi::glfwSetWindowUserPointer(self.window, self as *mut Self as *mut c_void);
ffi::glfwSetFramebufferSizeCallback(self.window, Some(framebuffer_resize_callback));
} }
} }
@ -1395,13 +1407,12 @@ impl VulkanApp {
ffi::VK_TRUE, ffi::VK_TRUE,
u64::MAX, u64::MAX,
); );
ffi::vkResetFences(self.device, 1, std::ptr::addr_of!(self.in_flight_fence));
} }
let mut image_index: u32 = 0; let mut image_index: u32 = 0;
unsafe { unsafe {
ffi::vkAcquireNextImageKHR( let result = ffi::vkAcquireNextImageKHR(
self.device, self.device,
self.swap_chain, self.swap_chain,
u64::MAX, u64::MAX,
@ -1409,6 +1420,18 @@ impl VulkanApp {
std::ptr::null_mut(), std::ptr::null_mut(),
std::ptr::addr_of_mut!(image_index), std::ptr::addr_of_mut!(image_index),
); );
if result == ffi::VkResult_VK_ERROR_OUT_OF_DATE_KHR {
// Recreate swapchain.
self.recreate_swap_chain()?;
return Ok(());
} else if result != ffi::VkResult_VK_SUCCESS
&& result != ffi::VkResult_VK_SUBOPTIMAL_KHR
{
return Err(String::from("Failed to acquire swap chain image!"));
}
ffi::vkResetFences(self.device, 1, std::ptr::addr_of!(self.in_flight_fence));
ffi::vkResetCommandBuffer(self.command_buffer, 0); ffi::vkResetCommandBuffer(self.command_buffer, 0);
self.record_command_buffer(self.command_buffer, image_index as usize)?; self.record_command_buffer(self.command_buffer, image_index as usize)?;
} }
@ -1454,7 +1477,17 @@ impl VulkanApp {
present_info.pResults = std::ptr::null_mut(); present_info.pResults = std::ptr::null_mut();
unsafe { unsafe {
ffi::vkQueuePresentKHR(self.present_queue, std::ptr::addr_of!(present_info)); let result =
ffi::vkQueuePresentKHR(self.present_queue, std::ptr::addr_of!(present_info));
if result == ffi::VkResult_VK_ERROR_OUT_OF_DATE_KHR
|| result == ffi::VkResult_VK_SUBOPTIMAL_KHR
|| self.framebuffer_resized
{
self.framebuffer_resized = false;
self.recreate_swap_chain()?;
} else if result != ffi::VkResult_VK_SUCCESS {
return Err(String::from("Failed to present swap chain image!"));
}
} }
Ok(()) Ok(())
@ -1494,10 +1527,55 @@ impl VulkanApp {
Ok(()) Ok(())
} }
fn recreate_swap_chain(&mut self) -> Result<(), String> {
unsafe {
ffi::vkDeviceWaitIdle(self.device);
}
self.cleanup_swap_chain()?;
self.create_swap_chain()?;
self.create_image_views()?;
self.create_framebuffers()?;
Ok(())
}
fn cleanup_swap_chain(&mut self) -> Result<(), String> {
for framebuffer in &self.swap_chain_framebuffers {
unsafe {
ffi::vkDestroyFramebuffer(self.device, *framebuffer, std::ptr::null());
}
}
self.swap_chain_framebuffers.clear();
for view in &self.swap_chain_image_views {
unsafe {
ffi::vkDestroyImageView(self.device, *view, std::ptr::null());
}
}
self.swap_chain_image_views.clear();
if !self.swap_chain.is_null() {
unsafe {
ffi::vkDestroySwapchainKHR(self.device, self.swap_chain, std::ptr::null());
}
}
self.swap_chain = std::ptr::null_mut();
Ok(())
}
pub fn set_resize_flag(&mut self) {
self.framebuffer_resized = true;
}
} }
impl Drop for VulkanApp { impl Drop for VulkanApp {
fn drop(&mut self) { fn drop(&mut self) {
self.cleanup_swap_chain().unwrap();
if !self.in_flight_fence.is_null() { if !self.in_flight_fence.is_null() {
unsafe { unsafe {
ffi::vkDestroyFence(self.device, self.in_flight_fence, std::ptr::null()); ffi::vkDestroyFence(self.device, self.in_flight_fence, std::ptr::null());
@ -1530,12 +1608,6 @@ impl Drop for VulkanApp {
} }
} }
for framebuffer in &self.swap_chain_framebuffers {
unsafe {
ffi::vkDestroyFramebuffer(self.device, *framebuffer, std::ptr::null());
}
}
if !self.graphics_pipeline.is_null() { if !self.graphics_pipeline.is_null() {
unsafe { unsafe {
ffi::vkDestroyPipeline(self.device, self.graphics_pipeline, std::ptr::null()); ffi::vkDestroyPipeline(self.device, self.graphics_pipeline, std::ptr::null());
@ -1554,18 +1626,6 @@ impl Drop for VulkanApp {
} }
} }
for view in &self.swap_chain_image_views {
unsafe {
ffi::vkDestroyImageView(self.device, *view, std::ptr::null());
}
}
if !self.swap_chain.is_null() {
unsafe {
ffi::vkDestroySwapchainKHR(self.device, self.swap_chain, std::ptr::null());
}
}
if !self.device.is_null() { if !self.device.is_null() {
unsafe { unsafe {
ffi::vkDestroyDevice(self.device, std::ptr::null()); ffi::vkDestroyDevice(self.device, std::ptr::null());