From 2d0bfc47e07bef91d98387af323d46f66bc0cfb1 Mon Sep 17 00:00:00 2001 From: JSDurand Date: Mon, 23 Jun 2025 02:32:26 +0800 Subject: Finally no bugs I finally found the correct approach to avoid all those annoying bugs, and we can "even" resize windows now. What an improvement! The remaining steps: - Load a file and draw the texts on the screen. This is relatively straightforward, as I have done this before. The point is that I am now using vulkan, and this is still a challenge to me. - Design a trait for display that can work with the terminal mode and the free graphical display at the same time. This requires a new abstraction per chance. - Integrate the output from Emacs by interpreting the commands sent from Emacs. The point of this step is to have a way to control the display through a fixed set of commands, and this set of commands needs to cooperate with Emacs, so that Emacs can control it by means of some emiited commands. - Try to rewrite the Emacs core so that it can use this new display engine. --- src/renderer/vulkan.rs | 217 ++++++++++++++++++++++++++++++------------------- 1 file changed, 134 insertions(+), 83 deletions(-) (limited to 'src/renderer') diff --git a/src/renderer/vulkan.rs b/src/renderer/vulkan.rs index 6e2c806..1ac367e 100644 --- a/src/renderer/vulkan.rs +++ b/src/renderer/vulkan.rs @@ -46,6 +46,10 @@ pub struct VulkanRenderer { frame: usize, resized: bool, resumed: bool, + pending_recreation: bool, + minimized: bool, + toupdate: bool, + window_size_at_recreate_request: PhysicalSize, } impl VulkanRenderer { @@ -60,6 +64,11 @@ impl VulkanRenderer { let resized = false; let resumed = false; + let pending_recreation = false; + let minimized = false; + let toupdate = true; + let window_size_at_recreate_request = PhysicalSize::::new(0, 0); + Self { window, entry, @@ -69,6 +78,10 @@ impl VulkanRenderer { frame, resized, resumed, + pending_recreation, + minimized, + toupdate, + window_size_at_recreate_request, } } @@ -77,6 +90,7 @@ impl VulkanRenderer { unsafe { if let Some(device) = &self.device { + dbg!(device); device.device_wait_idle()?; } @@ -86,41 +100,24 @@ impl VulkanRenderer { dbg!("after destroy swapchain"); - // --- Destroy and recreate the Vulkan surface --- - if let (Some(instance), Some(window)) = (&self.instance, &self.window) { - dbg!( - "Creating new surface (macOS workaround - skipping old surface destruction)..." - ); - // IMPORTANT: We are intentionally skipping - // `instance.destroy_surface_khr(self.data.surface, - // None);` and the `is_null()` check for the old - // surface here. - // - // On macOS, the existing surface handle often becomes - // invalid after a resize event even if its value is - // not null, causing a crash upon explicit - // destruction. - // - // A new surface will be created, and the old, - // implicitly invalidated one will be cleaned up when - // the VkInstance is destroyed at application exit. - - // dbg!("Creating new surface..."); - - let window_handle = window.window_handle().unwrap(); - let display_handle = window.display_handle().unwrap(); - - dbg!(&display_handle); - dbg!(&window_handle); - - // Use vulkanalia::window::create_surface with the current window handles - self.data.surface = vw::create_surface(instance, &display_handle, &window_handle)?; - dbg!("New surface created."); - } else { - // This case should ideally not be reached if the application is properly initialized. - panic!("Instance or Window is None during surface recreation"); - } - // -- END -- + // // --- Destroy and recreate the Vulkan surface --- + // if let (Some(instance), Some(window)) = (&self.instance, &self.window) { + // dbg!("Creating new surface"); + + // let window_handle = window.window_handle().unwrap(); + // let display_handle = window.display_handle().unwrap(); + + // dbg!(&display_handle); + // dbg!(&window_handle); + + // // Use vulkanalia::window::create_surface with the current window handles + // self.data.surface = vw::create_surface(instance, &display_handle, &window_handle)?; + // dbg!("New surface created."); + // } else { + // // This case should ideally not be reached if the application is properly initialized. + // panic!("Instance or Window is None during surface recreation"); + // } + // // -- END -- if let (Some(device), Some(instance), Some(window)) = (&self.device, &self.instance, &self.window) @@ -169,6 +166,8 @@ impl VulkanRenderer { device.destroy_framebuffer(buffer, None); } + self.data.framebuffers.clear(); + device.free_command_buffers(self.data.command_pool, &self.data.command_buffers); device.destroy_pipeline(self.data.pipeline, None); @@ -202,7 +201,7 @@ impl VulkanRenderer { vk::Fence::null(), ); - dbg!("Acquired next image"); + // dbg!("Acquired next image"); let image_index = match acquire_image_result { Ok((image_index, _)) => image_index, @@ -210,23 +209,23 @@ impl VulkanRenderer { Err(code) => return Err(code.into()), }; - dbg!("Image index obtained", image_index); + // dbg!("Image index obtained", image_index); if !self.data.images_in_flight[image_index as usize].is_null() { - dbg!("Image in flight is not null"); + // dbg!("Image in flight is not null"); device.wait_for_fences( &[self.data.images_in_flight[image_index as usize]], true, u64::MAX, )?; - dbg!("Waited for image in flight fence"); + // dbg!("Waited for image in flight fence"); } self.data.images_in_flight[image_index as usize] = self.data.in_flight_fences[self.frame]; - dbg!("Updated images_in_flight"); + // dbg!("Updated images_in_flight"); let waiting_sps = [self.data.image_available_sp[self.frame]]; let waiting_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; @@ -241,7 +240,7 @@ impl VulkanRenderer { .command_buffers(&command_buffer) .signal_semaphores(&render_sps); - dbg!("Submitting queue"); + // dbg!("Submitting queue"); device.reset_fences(&[self.data.in_flight_fences[self.frame]])?; device.queue_submit( @@ -249,7 +248,7 @@ impl VulkanRenderer { &[submit_info], self.data.in_flight_fences[self.frame], )?; - dbg!("Queue submitted"); + // dbg!("Queue submitted"); let swapchains = [self.data.swapchain]; @@ -260,9 +259,9 @@ impl VulkanRenderer { .swapchains(&swapchains) .image_indices(&image_indices); - dbg!("Presenting queue"); + // dbg!("Presenting queue"); let present_result = device.queue_present_khr(self.data.pqueue, &presentation_info); - dbg!("Queue presented"); + // dbg!("Queue presented"); if self.resized || matches!( @@ -320,13 +319,14 @@ impl VulkanRenderer { } } - unsafe fn handle_resize(&mut self, new_size: PhysicalSize) { - // recreate swapchain and framebuffers - unsafe { - self.recreate_swapchain().unwrap(); + fn handle_resize(&mut self, new_size: PhysicalSize) { + if new_size.width == 0 || new_size.height == 0 { + self.minimized = true; + } else { + self.minimized = false; + self.pending_recreation = true; + self.window_size_at_recreate_request = new_size; } - - println!("Window resized to {:?}", new_size); } } @@ -394,7 +394,7 @@ impl ApplicationHandler for VulkanRenderer { self.data = data; } - dbg!(); + dbg!("end of resumed"); } fn window_event( @@ -403,37 +403,78 @@ impl ApplicationHandler for VulkanRenderer { _window_id: WindowId, event: WindowEvent, ) { - // let window = match self.window.as_ref() { - // Some(window) => window, - // _ => return, - // }; - match event { - WindowEvent::CloseRequested => event_loop.exit(), - WindowEvent::RedrawRequested => { - // self.clear(); - // self.draw_text(&self.test_span); // store it in self maybe? - // self.present(); - - unsafe { - dbg!(); - self.render().unwrap(); - } + WindowEvent::CloseRequested => { + event_loop.exit(); + // unsafe { + // if let Some(device) = &self.device { + // device.device_wait_idle().unwrap(); + + // self.destroy(); + // } + // } + } + WindowEvent::RedrawRequested + if self.toupdate && !self.minimized && !event_loop.exiting() => + { + dbg!("redraw starts"); + self.toupdate = false; - self.window.as_ref().unwrap().request_redraw(); + unsafe { self.render() }.unwrap(); } - WindowEvent::Resized(new_size) => unsafe { + WindowEvent::Resized(new_size) => { dbg!("before handle resize"); self.handle_resize(new_size); dbg!("after handle resize"); - - self.window.as_ref().unwrap().request_redraw(); - - dbg!("after request redraw"); - }, + } _ => (), } } + + fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { + if self.toupdate { + self.window.as_ref().unwrap().request_redraw(); + } + + // if self.window.is_some() { + // let current_size = self.window.as_ref().unwrap().inner_size(); + + // // Check if the window size has stabilized AND is not zero-sized + // if self.window_size_at_recreate_request.width == current_size.width + // && self.window_size_at_recreate_request.height == current_size.height + // && current_size.width > 0 + // && current_size.height > 0 + // { + // dbg!("Attempting deferred swapchain recreation..."); + // dbg!(current_size, self.window_size_at_recreate_request); + // unsafe { + // if let Err(e) = self.recreate_swapchain() { + // panic!("Error during deferred swapchain recreation: {:?}", e); + // } else { + // #[cfg(debug_assertions)] + // dbg!("Deferred swapchain recreation successful."); + // self.pending_recreation = false; // Reset the flag after successful recreation + // self.window.as_ref().unwrap().request_redraw(); // Request a redraw to render with the new swapchain + // } + // } + // } else { + // #[cfg(debug_assertions)] + // dbg!("Still waiting for a stable, non-zero window size for recreation. Current: {:?}, Requested: {:?}", current_size, self.window_size_at_recreate_request); + // } + // } else { + // // Handle case where window might be closed before recreation + // #[cfg(debug_assertions)] + // dbg!("Window is None, cannot perform deferred recreation. Resetting recreate_pending flag."); + // self.pending_recreation = false; + // } + } + + // Always request redraw if you want continuous rendering, or only + // when needed. If you only redraw when changes occur, + // `request_redraw()` after successful recreation is key. + + // Uncomment if you want continuous redraws even when idle + // event_loop.request_redraw(); } impl RendererBackend for VulkanRenderer { @@ -587,17 +628,22 @@ impl QueueFamilyIndices { physical_device: vk::PhysicalDevice, ) -> Result { unsafe { + dbg!("start getting", &physical_device); let properties = instance.get_physical_device_queue_family_properties(physical_device); // for property in properties.iter() { // println!("property: {property:?}"); // } + dbg!("before graphics"); + let graphics = properties .iter() .position(|p| p.queue_flags.contains(vk::QueueFlags::GRAPHICS)) .map(|i| i as u32); + dbg!("after graphics"); + let mut presentation = None; for (index, _property) in properties.iter().enumerate() { @@ -720,14 +766,22 @@ unsafe fn create_swapchain( device: &Device, data: &mut AppData, ) -> Result<(), MainError> { + dbg!("start creating swapchain"); unsafe { let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?; + + dbg!("after getting indices"); + let support = SwapchainSupport::get(instance, data, data.physical_device)?; + dbg!("after getting support"); + let format = get_swapchain_surace_format(&support.formats); let pmode = get_swapchain_pmode(&support.pmodes); let extent = get_swapchain_extent(window, support.capabilities); + dbg!("after getting suface format, pmode, and extent"); + let min_image_count = support.capabilities.min_image_count; let mut max_image_count = support.capabilities.max_image_count; @@ -737,23 +791,13 @@ unsafe fn create_swapchain( let image_count = (min_image_count + 1).clamp(min_image_count, max_image_count); - // let mut queue_family_indices = Vec::new(); - let (image_sharing_mode, queue_family_indices) = if indices.graphics != indices.presentation { - // If graphics and presentation queues are different, - // use CONCURRENT mode and provide both queue family - // indices. ( vk::SharingMode::CONCURRENT, vec![indices.graphics, indices.presentation], ) } else { - // If graphics and presentation queues are the same, - // use EXCLUSIVE mode. - // - // For EXCLUSIVE mode, `queue_family_index_count` MUST - // be 0. (vk::SharingMode::EXCLUSIVE, vec![]) }; @@ -1035,6 +1079,13 @@ unsafe fn create_command_pool( data.command_pool = device.create_command_pool(&info, None)?; + #[cfg(debug_assertions)] + assert_ne!( + data.command_pool, + vk::CommandPool::null(), + "null command pool" + ); + Ok(()) } } -- cgit v1.2.3-18-g5258