summaryrefslogtreecommitdiff
path: root/src/renderer
diff options
context:
space:
mode:
authorJSDurand <mmemmew@gmail.com>2025-06-23 02:32:26 +0800
committerJSDurand <mmemmew@gmail.com>2025-06-23 02:32:26 +0800
commit2d0bfc47e07bef91d98387af323d46f66bc0cfb1 (patch)
tree25938074ac0493722c47af76bf3a305f7224b205 /src/renderer
parenta33d65e60592e962fe8e5099c4058be8395a2323 (diff)
Finally no bugsHEADmaster
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.
Diffstat (limited to 'src/renderer')
-rw-r--r--src/renderer/vulkan.rs217
1 files changed, 134 insertions, 83 deletions
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<u32>,
}
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::<u32>::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<u32>) {
- // recreate swapchain and framebuffers
- unsafe {
- self.recreate_swapchain().unwrap();
+ fn handle_resize(&mut self, new_size: PhysicalSize<u32>) {
+ 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<Self, MainError> {
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(())
}
}