summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-07-09 17:41:09 -0700
committerIrene Knapp <ireneista@irenes.space>2026-07-09 17:41:09 -0700
commite547dc5ab4ec1667bcde6368f756f63123eae587 (patch)
tree925ce9406b80e8e1567b631f4ef228520b9d34a4
parent05982c6344ec308bcd60fae9457979a20ff18bc8 (diff)
okay, reinitializing for window changes works now
yay

Force-Push: yes
Change-Id: Id8dce4cd4bc696be322fe5b612837e3651437f7e
-rw-r--r--src/graphics_permanent.rs17
-rw-r--r--src/graphics_window_dressing.rs117
-rw-r--r--src/main.rs74
3 files changed, 168 insertions, 40 deletions
diff --git a/src/graphics_permanent.rs b/src/graphics_permanent.rs
index d41bbfa..c2225a3 100644
--- a/src/graphics_permanent.rs
+++ b/src/graphics_permanent.rs
@@ -94,8 +94,7 @@ pub struct EnableSwapchain(pub bool);
 impl PermanentGraphicsState {
   #[allow(unsafe_code)]
   pub fn new(event_loop: &ActiveEventLoop)
-      -> Result<(Self, vk::PhysicalDevice, QueueFamilyIndices,
-                 EnableSwapchain)>
+      -> Result<(Self, GraphicsStateForReinit, EnableSwapchain)>
   {
     let window = init_window(event_loop)?;
 
@@ -131,7 +130,9 @@ impl PermanentGraphicsState {
     Ok((PermanentGraphicsState {
       window, entry, instance, debug_messager, surface, device,
       graphics_queue, presentation_queue,
-    }, physical_device, indices, enable_swapchain))
+    }, GraphicsStateForReinit {
+      physical_device, indices
+    }, enable_swapchain))
   }
 
   #[allow(unsafe_code)]
@@ -206,6 +207,16 @@ impl PermanentGraphicsState {
 }
 
 
+//   The GraphicsStateForReinit connects Vulkan objects which are only needed
+// during the creation of the window-dressing objects. They are used during
+// initial startup, and again any time the window-dressing needs to be
+// reinitialized. Most notably, they are not needed when rendering.
+pub struct GraphicsStateForReinit {
+  pub physical_device: vk::PhysicalDevice,
+  pub indices: QueueFamilyIndices,
+}
+
+
 fn init_window(event_loop: &ActiveEventLoop) -> Result<Window> {
   //   Notice that we do this before having a Vulkan instance. The window is
   // actually a parameter needed to create the instance; see
diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs
index e35332f..63fc693 100644
--- a/src/graphics_window_dressing.rs
+++ b/src/graphics_window_dressing.rs
@@ -1,6 +1,8 @@
 #![deny(unsafe_code)]
 use crate::error::*;
-use crate::graphics_permanent::{ PermanentGraphicsState, QueueFamilyIndices };
+use crate::graphics_permanent::{
+  PermanentGraphicsState, GraphicsStateForReinit, QueueFamilyIndices
+};
 
 use std::collections::BTreeSet;
 use vulkanalia::{ Device, Instance };
@@ -73,14 +75,15 @@ pub struct Concurrency {
 
 impl WindowDressing {
   pub fn new(permanent: &PermanentGraphicsState,
-             physical_device: vk::PhysicalDevice,
-             indices: QueueFamilyIndices)
+             for_reinit: &GraphicsStateForReinit)
       -> Result<Self>
   {
     let window = &permanent.window;
     let instance = &permanent.instance;
     let surface = &permanent.surface;
     let device = &permanent.device;
+    let physical_device = &for_reinit.physical_device;
+    let indices = &for_reinit.indices;
 
     let swapchain = init_swapchain(
             window, instance, surface, &physical_device, device, &indices)?;
@@ -93,9 +96,10 @@ impl WindowDressing {
     let framebuffers = init_framebuffers(
             device, &swapchain.extent, &swapchain.image_views, &render_pass)?;
 
-    let (command_pool, command_buffers)
-            = init_commands(device, &swapchain.extent, &framebuffers,
-                            &render_pass, &pipeline, &indices)?;
+    let command_pool = init_command_pool(device, indices)?;
+    let command_buffers = init_commands(device, &swapchain.extent,
+                                        &framebuffers, &render_pass,
+                                        &pipeline, &command_pool)?;
 
     let concurrency = init_concurrency(device, &swapchain.images)?;
 
@@ -111,9 +115,58 @@ impl WindowDressing {
     })
   }
 
+
   #[allow(unsafe_code)]
-  pub fn destroy(self, permanent: &PermanentGraphicsState) {
+  pub fn reinit(&mut self, permanent: &PermanentGraphicsState,
+                for_reinit: &GraphicsStateForReinit)
+      -> Result<()>
+  {
+    let window = &permanent.window;
+    let instance = &permanent.instance;
+    let surface = &permanent.surface;
     let device = &permanent.device;
+    let physical_device = &for_reinit.physical_device;
+    let indices = &for_reinit.indices;
+
+    unsafe { device.device_wait_idle() }.unwrap();
+
+    self.destroy_replaceable(device);
+
+    let swapchain = init_swapchain(
+            window, instance, surface, &physical_device, device, &indices)?;
+
+    let render_pass = init_render_pass(device, &swapchain.format)?;
+
+    let (pipeline_layout, pipeline)
+            = init_pipeline(device, &swapchain.extent, &render_pass)?;
+
+    let framebuffers = init_framebuffers(
+            device, &swapchain.extent, &swapchain.image_views, &render_pass)?;
+
+    // Notice that we reused the command pool.
+    let command_buffers = init_commands(
+            device, &swapchain.extent, &framebuffers, &render_pass,
+            &pipeline, &self.command_pool)?;
+
+    self.concurrency.image_fences.resize(swapchain.images.len(),
+                                         vk::Fence::null());
+
+    self.swapchain = swapchain;
+    self.render_pass = render_pass;
+    self.pipeline = pipeline;
+    self.pipeline_layout = pipeline_layout;
+    self.framebuffers = framebuffers;
+    self.command_buffers = command_buffers;
+
+    Ok(())
+  }
+
+
+  //   This relies on its caller to have already waited for the device to be
+  // idle.
+  #[allow(unsafe_code)]
+  pub fn destroy(mut self, device: &Device) {
+    self.destroy_replaceable(device);
 
     for semaphore in self.concurrency.image_available_semaphores {
       unsafe { device.destroy_semaphore(semaphore, None) };
@@ -127,18 +180,28 @@ impl WindowDressing {
       unsafe { device.destroy_fence(fence, None) };
     }
 
+    // Notice that destroy_replaceable freed the pool, but did not destroy it.
     unsafe { device.destroy_command_pool(self.command_pool, None) };
+  }
 
-    for framebuffer in self.framebuffers {
-      unsafe { device.destroy_framebuffer(framebuffer, None) };
+
+  #[allow(unsafe_code)]
+  fn destroy_replaceable(&mut self, device: &Device) {
+    for framebuffer in &self.framebuffers {
+      unsafe { device.destroy_framebuffer(*framebuffer, None) };
     }
 
+    // Notice that we free the pool, but do not destroy it.
+    unsafe {
+      device.free_command_buffers(self.command_pool, &self.command_buffers)
+    };
+
     unsafe { device.destroy_pipeline(self.pipeline, None) };
-    unsafe { device.destroy_render_pass(self.render_pass, None) };
     unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) };
+    unsafe { device.destroy_render_pass(self.render_pass, None) };
 
-    for view in self.swapchain.image_views {
-      unsafe { device.destroy_image_view(view, None) };
+    for view in &self.swapchain.image_views {
+      unsafe { device.destroy_image_view(*view, None) };
     }
 
     unsafe { device.destroy_swapchain_khr(self.swapchain.swapchain, None) };
@@ -446,27 +509,31 @@ fn init_framebuffers(device: &Device, extent: &vk::Extent2D,
 
 
 #[allow(unsafe_code)]
-fn init_commands(device: &Device,
-                 extent: &vk::Extent2D,
-                 framebuffers: &Vec<vk::Framebuffer>,
-                 render_pass: &vk::RenderPass,
-                 pipeline: &vk::Pipeline,
-                 indices: &QueueFamilyIndices)
-    -> Result<(vk::CommandPool, Vec<vk::CommandBuffer>)>
+fn init_command_pool(device: &Device, indices: &QueueFamilyIndices)
+    -> Result<vk::CommandPool>
 {
-  // We call this one last time. It's kind of a problem.
-
   let command_pool_info = vk::CommandPoolCreateInfo::builder()
                               .flags(vk::CommandPoolCreateFlags::empty())
                               .queue_family_index(indices.graphics);
 
-  let command_pool = unsafe {
+  Ok(unsafe {
     device.create_command_pool(&command_pool_info, None)
-  }?;
+  }?)
+}
+
 
+#[allow(unsafe_code)]
+fn init_commands(device: &Device,
+                 extent: &vk::Extent2D,
+                 framebuffers: &Vec<vk::Framebuffer>,
+                 render_pass: &vk::RenderPass,
+                 pipeline: &vk::Pipeline,
+                 command_pool: &vk::CommandPool)
+    -> Result<Vec<vk::CommandBuffer>>
+{
   let command_buffer_allocation_info
           = vk::CommandBufferAllocateInfo::builder()
-                .command_pool(command_pool)
+                .command_pool(*command_pool)
                 .level(vk::CommandBufferLevel::PRIMARY)
                 .command_buffer_count(framebuffers.len() as u32);
   let command_buffers = unsafe {
@@ -523,7 +590,7 @@ fn init_commands(device: &Device,
     unsafe { device.end_command_buffer(command_buffer) }?;
   }
 
-  Ok((command_pool, command_buffers))
+  Ok(command_buffers)
 }
 
 
diff --git a/src/main.rs b/src/main.rs
index 5dea456..9beb151 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,8 @@
 #![deny(unsafe_code)]
 use crate::error::*;
-use crate::graphics_permanent::PermanentGraphicsState;
+use crate::graphics_permanent::{
+  PermanentGraphicsState, GraphicsStateForReinit
+};
 use crate::graphics_window_dressing::{
   WindowDressing, N_SIMULTANEOUS_FRAMES
 };
@@ -20,7 +22,9 @@ mod graphics_window_dressing;
 
 struct Surreality {
   permanent: RefCell<Option<PermanentGraphicsState>>,
+  for_reinit: RefCell<Option<GraphicsStateForReinit>>,
   window_dressing: RefCell<Option<WindowDressing>>,
+  is_reinit_queued: bool,
   frame_index: usize,
 }
 
@@ -28,28 +32,56 @@ impl Surreality {
   fn new() -> Self {
     Surreality {
       permanent: RefCell::new(None),
+      for_reinit: RefCell::new(None),
       window_dressing: RefCell::new(None),
+      is_reinit_queued: false,
       frame_index: 0,
     }
   }
 
   #[allow(unsafe_code)]
   fn init(&mut self, event_loop: &ActiveEventLoop) -> Result<()> {
-    let (permanent, physical_device, indices, enable_swapchain)
-            = PermanentGraphicsState::new(event_loop)?;
+    let (permanent, for_reinit, enable_swapchain)
+        = PermanentGraphicsState::new(event_loop)?;
 
     if enable_swapchain.0 {
       *self.window_dressing.get_mut()
-          = Some(WindowDressing::new(&permanent, physical_device, indices)?);
+          = Some(WindowDressing::new(&permanent, &for_reinit)?);
     }
 
     *self.permanent.get_mut() = Some(permanent);
+    *self.for_reinit.get_mut() = Some(for_reinit);
+
+    Ok(())
+  }
+
+  fn reinit(&mut self) -> Result<()> {
+    if let Some(permanent) = self.permanent.borrow().as_ref()
+       && let Some(for_reinit) = self.for_reinit.borrow().as_ref()
+       && let Some(window_dressing)
+              = self.window_dressing.borrow_mut().as_mut()
+    {
+      window_dressing.reinit(permanent, for_reinit)?;
+    }
+
+    Ok(())
+  }
+
+
+  fn do_frame(&mut self, window_id: WindowId) -> Result<()> {
+    if self.render(window_id)? || self.is_reinit_queued {
+      self.is_reinit_queued = false;
+
+      self.reinit()?;
+    }
 
     Ok(())
   }
 
+  //   This returns true if, and only if, the window dressing should be
+  // reinitialized.
   #[allow(unsafe_code)]
-  fn render(&mut self, window_id: WindowId) -> Result<()> {
+  fn render(&mut self, window_id: WindowId) -> Result<bool> {
     if let Some(permanent) = self.permanent.borrow().as_ref()
        && let Some(window_dressing)
               = self.window_dressing.borrow_mut().as_mut()
@@ -66,11 +98,15 @@ impl Surreality {
 
       unsafe { device.wait_for_fences(&[*frame_fence], true, u64::MAX) }?;
 
-      let image_index = unsafe {
+      let image_index = match unsafe {
         device.acquire_next_image_khr(window_dressing.swapchain.swapchain,
                                       u64::MAX, *image_available_semaphore,
                                       vk::Fence::null())
-      }?.0 as usize;
+      } {
+        Ok((image_index, _)) => image_index as usize,
+        Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return Ok(true),
+        Err(e) => return Err(Error::from(e)),
+      };
 
       let image_fence = &concurrency.image_fences[image_index];
       if !image_fence.is_null() {
@@ -103,14 +139,19 @@ impl Surreality {
                              .swapchains(&swapchains)
                              .image_indices(&image_indices);
 
-      unsafe {
+      match unsafe {
         device.queue_present_khr(permanent.presentation_queue, &present_info)
-      }?;
+      } {
+        Ok(vk::SuccessCode::SUBOPTIMAL_KHR) => return Ok(true),
+        Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return Ok(true),
+        Err(e) => return Err(Error::from(e)),
+        Ok(_) => (),
+      }
 
       self.frame_index = (frame_index + 1) % N_SIMULTANEOUS_FRAMES;
     }
 
-    Ok(())
+    Ok(false)
   }
 }
 
@@ -121,7 +162,7 @@ impl Drop for Surreality {
       unsafe { permanent.device.device_wait_idle() }.unwrap();
 
       if let Some(window_dressing) = self.window_dressing.replace(None) {
-        window_dressing.destroy(&permanent);
+        window_dressing.destroy(&permanent.device);
       }
 
       permanent.destroy();
@@ -145,14 +186,23 @@ impl ApplicationHandler for Surreality {
     match event {
       WindowEvent::RedrawRequested => {
         if !event_loop.exiting() {
-          if let Err(e) = self.render(window_id) {
+          if let Err(e) = self.do_frame(window_id) {
             eprintln!("Error: {}", e);
           }
         }
       }
+
       WindowEvent::CloseRequested => {
         event_loop.exit();
       }
+
+      WindowEvent::Resized(_) => {
+        self.is_reinit_queued = true;
+        if let Err(e) = self.do_frame(window_id) {
+          eprintln!("Error: {}", e);
+        }
+      }
+
       _ => { }
     }
   }