diff --git a/src/app.rs b/src/app.rs index 51ee29c..c120222 100644 --- a/src/app.rs +++ b/src/app.rs @@ -32,11 +32,13 @@ pub const MAX_FRAMES_IN_FLIGHT: usize = 2; /// Our Vulkan app. #[derive(Clone, Debug)] pub struct App { - entry: Entry, + /// This value needs to stick around, but we don't use it directly + _entry: Entry, instance: Instance, data: AppData, pub device: Device, frame: usize, + pub resized: bool, } impl App { @@ -65,84 +67,22 @@ impl App { create_sync_objects(&device, &mut data)?; Ok(Self { - entry, + _entry: entry, instance, data, device, frame: 0, + resized: false, }) } - /// Renders a frame for our Vulkan app. - /// - /// # Safety - /// Here be Dragons - pub unsafe fn render(&mut self, _window: &Window) -> Result<()> { - self.device.wait_for_fences( - &[self.data.in_flight_fences[self.frame]], - true, - u64::max_value(), - )?; - - let image_index = self - .device - .acquire_next_image_khr( - self.data.swapchain, - u64::max_value(), - self.data.image_available_semaphores[self.frame], - vk::Fence::null(), - )? - .0 as usize; - - if !self.data.images_in_flight[image_index as usize].is_null() { - self.device.wait_for_fences( - &[self.data.images_in_flight[image_index as usize]], - true, - u64::max_value(), - )?; - } - - self.data.images_in_flight[image_index as usize] = self.data.in_flight_fences[self.frame]; - - let wait_semaphores = &[self.data.image_available_semaphores[self.frame]]; - let wait_stages = &[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; - let command_buffers = &[self.data.command_buffers[image_index as usize]]; - let signal_semaphores = &[self.data.render_finished_semaphores[self.frame]]; - let submit_info = vk::SubmitInfo::builder() - .wait_semaphores(wait_semaphores) - .wait_dst_stage_mask(wait_stages) - .command_buffers(command_buffers) - .signal_semaphores(signal_semaphores); - - self.device - .reset_fences(&[self.data.in_flight_fences[self.frame]])?; - - self.device.queue_submit( - self.data.graphics_queue, - &[submit_info], - self.data.in_flight_fences[self.frame], - )?; - - let swapchains = &[self.data.swapchain]; - let image_indices = &[image_index as u32]; - let present_info = vk::PresentInfoKHR::builder() - .wait_semaphores(signal_semaphores) - .swapchains(swapchains) - .image_indices(image_indices); - - self.device - .queue_present_khr(self.data.present_queue, &present_info)?; - - self.frame = (self.frame + 1) % MAX_FRAMES_IN_FLIGHT; - - Ok(()) - } - /// Destroys our Vulkan app, in reverse order of creation /// /// # Safety /// Here be Dragons pub unsafe fn destroy(&mut self) { + self.destroy_swapchain(); + self.data .in_flight_fences .iter() @@ -157,19 +97,6 @@ impl App { .for_each(|s| self.device.destroy_semaphore(*s, None)); self.device .destroy_command_pool(self.data.command_pool, None); - self.data - .framebuffers - .iter() - .for_each(|f| self.device.destroy_framebuffer(*f, None)); - self.device.destroy_pipeline(self.data.pipeline, None); - self.device - .destroy_pipeline_layout(self.data.pipeline_layout, None); - self.device.destroy_render_pass(self.data.render_pass, None); - self.data - .swapchain_image_views - .iter() - .for_each(|v| self.device.destroy_image_view(*v, None)); - self.device.destroy_swapchain_khr(self.data.swapchain, None); self.device.destroy_device(None); self.instance.destroy_surface_khr(self.data.surface, None); @@ -180,6 +107,120 @@ impl App { self.instance.destroy_instance(None); } + + /// Cleanup the swapchain and related objects + /// + /// # Safety + /// Here be Dragons + unsafe fn destroy_swapchain(&mut self) { + self.data + .framebuffers + .iter() + .for_each(|f| self.device.destroy_framebuffer(*f, None)); + self.device + .free_command_buffers(self.data.command_pool, &self.data.command_buffers); + self.device.destroy_pipeline(self.data.pipeline, None); + self.device + .destroy_pipeline_layout(self.data.pipeline_layout, None); + self.device.destroy_render_pass(self.data.render_pass, None); + self.data + .swapchain_image_views + .iter() + .for_each(|v| self.device.destroy_image_view(*v, None)); + self.device.destroy_swapchain_khr(self.data.swapchain, None); + } + + /// Renders a frame for our Vulkan app. + /// + /// # Safety + /// Here be Dragons + pub unsafe fn render(&mut self, window: &Window) -> Result<()> { + let in_flight_fence = self.data.in_flight_fences[self.frame]; + + self.device + .wait_for_fences(&[in_flight_fence], true, u64::max_value())?; + let result = self.device.acquire_next_image_khr( + self.data.swapchain, + u64::max_value(), + self.data.image_available_semaphores[self.frame], + vk::Fence::null(), + ); + let image_index = match result { + Ok((image_index, _)) => image_index as usize, + Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return self.recreate_swapchain(window), + Err(e) => return Err(anyhow!(e)), + }; + + let image_in_flight = self.data.images_in_flight[image_index]; + if !image_in_flight.is_null() { + self.device + .wait_for_fences(&[image_in_flight], true, u64::max_value())?; + } + + self.data.images_in_flight[image_index] = in_flight_fence; + + let wait_semaphores = &[self.data.image_available_semaphores[self.frame]]; + let wait_stages = &[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; + let command_buffers = &[self.data.command_buffers[image_index]]; + let signal_semaphores = &[self.data.render_finished_semaphores[self.frame]]; + let submit_info = vk::SubmitInfo::builder() + .wait_semaphores(wait_semaphores) + .wait_dst_stage_mask(wait_stages) + .command_buffers(command_buffers) + .signal_semaphores(signal_semaphores); + + self.device.reset_fences(&[in_flight_fence])?; + + self.device + .queue_submit(self.data.graphics_queue, &[submit_info], in_flight_fence)?; + + let swapchains = &[self.data.swapchain]; + let image_indices = &[image_index as u32]; + let present_info = vk::PresentInfoKHR::builder() + .wait_semaphores(signal_semaphores) + .swapchains(swapchains) + .image_indices(image_indices); + + let result = self + .device + .queue_present_khr(self.data.present_queue, &present_info); + + let changed = result == Ok(vk::SuccessCode::SUBOPTIMAL_KHR) + || result == Err(vk::ErrorCode::OUT_OF_DATE_KHR); + if self.resized || changed { + self.recreate_swapchain(window)?; + } else if let Err(e) = result { + return Err(anyhow!(e)); + } + + self.frame = (self.frame + 1) % MAX_FRAMES_IN_FLIGHT; + + Ok(()) + } + + /// Recreates the swapchain + /// + /// # Safety + /// Here be Dragons + fn recreate_swapchain(&mut self, window: &Window) -> Result<()> { + unsafe { + self.device.device_wait_idle()?; + self.destroy_swapchain(); + + create_swapchain(window, &self.instance, &self.device, &mut self.data)?; + create_swapchain_image_views(&self.device, &mut self.data)?; + create_render_pass(&self.instance, &self.device, &mut self.data)?; + create_pipeline(&self.device, &mut self.data)?; + create_framebuffers(&self.device, &mut self.data)?; + create_command_buffers(&self.device, &mut self.data)?; + + self.data + .images_in_flight + .resize(self.data.swapchain_images.len(), vk::Fence::null()); + } + + Ok(()) + } } /// The Vulkan handles and associated properties used by our Vulkan app. diff --git a/src/app/functions.rs b/src/app/functions.rs index b34377b..9474c15 100644 --- a/src/app/functions.rs +++ b/src/app/functions.rs @@ -377,7 +377,7 @@ pub(super) unsafe fn create_swapchain_image_views( //================================================ pub(super) unsafe fn create_render_pass( - instance: &Instance, + _instance: &Instance, device: &Device, data: &mut AppData, ) -> Result<()> { diff --git a/src/lib.rs b/src/lib.rs index 44e3bfb..b728bbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,11 +21,26 @@ pub fn run() -> Result<()> { // App let mut app = unsafe { App::create(&window)? }; let mut destroying = false; + let mut minimized = false; event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; match event { // Render a frame if our Vulkan app is not being destroyed - Event::MainEventsCleared if !destroying => unsafe { app.render(&window) }.unwrap(), + Event::MainEventsCleared if !destroying && !minimized => { + unsafe { app.render(&window) }.unwrap() + } + // Let the app know it has been resized + Event::WindowEvent { + event: WindowEvent::Resized(size), + .. + } => { + if size.width == 0 || size.height == 0 { + minimized = true; + } else { + minimized = false; + app.resized = true; + } + } // Destroy our Vulkan app. Event::WindowEvent { event: WindowEvent::CloseRequested,