summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/graphics_scene.rs39
-rw-r--r--src/graphics_window_dressing.rs309
-rw-r--r--src/main.rs36
3 files changed, 225 insertions, 159 deletions
diff --git a/src/graphics_scene.rs b/src/graphics_scene.rs
index 771255a..f3694f7 100644
--- a/src/graphics_scene.rs
+++ b/src/graphics_scene.rs
@@ -1,5 +1,6 @@
 #![deny(unsafe_code)]
 use crate::error::*;
+use crate::graphics_window_dressing::RenderState;
 use crate::linear_algebra::{ Vec3, Vec4, Transformation };
 use crate::shader_data::VertexPushBlock;
 
@@ -11,19 +12,15 @@ use vulkanalia::vk::{ self, HasBuilder, DeviceV1_0 };
 
 
 #[allow(unsafe_code)]
-pub fn generate_scene_commands(command_buffer: &vk::CommandBuffer,
-                               time: f32, device: &Device,
-                               extent: &vk::Extent2D,
-                               framebuffer: &vk::Framebuffer,
-                               render_pass: &vk::RenderPass,
-                               pipeline_layout: &vk::PipelineLayout,
-                               pipeline: &vk::Pipeline,
-                               vertex_buffer: &vk::Buffer,
-                               index_buffer: &vk::Buffer,
-                               index_count: usize,
-                               descriptor_set: &vk::DescriptorSet)
-    -> Result<()>
+pub fn generate_scene_commands<'a>(image_index: usize, time: f32,
+                                   render_state: &'a RenderState,
+                                   device: &Device, extent: &vk::Extent2D)
+    -> Result<&'a vk::CommandBuffer>
 {
+  let command_buffer = &render_state.command_buffers[image_index];
+  let framebuffer = &render_state.framebuffers[image_index];
+  let descriptor_set = &render_state.descriptor_sets[image_index];
+
   let inheritance_info = vk::CommandBufferInheritanceInfo::builder();
 
   let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder()
@@ -52,7 +49,7 @@ pub fn generate_scene_commands(command_buffer: &vk::CommandBuffer,
   let clear_values = [color_clear_value, depth_clear_value];
 
   let begin_pass_info = vk::RenderPassBeginInfo::builder()
-          .render_pass(*render_pass)
+          .render_pass(render_state.render_pass)
           .framebuffer(*framebuffer)
           .render_area(render_area)
           .clear_values(&clear_values);
@@ -65,23 +62,23 @@ pub fn generate_scene_commands(command_buffer: &vk::CommandBuffer,
   unsafe {
     device.cmd_bind_pipeline(*command_buffer,
                              vk::PipelineBindPoint::GRAPHICS,
-                             *pipeline)
+                             render_state.pipeline)
   };
 
   unsafe {
     device.cmd_bind_vertex_buffers(*command_buffer, 0,
-                                   &[*vertex_buffer], &[0])
+                                   &[render_state.vertex_buffer], &[0])
   };
 
   unsafe {
-    device.cmd_bind_index_buffer(*command_buffer, *index_buffer, 0,
-                                 vk::IndexType::UINT32)
+    device.cmd_bind_index_buffer(*command_buffer, render_state.index_buffer,
+                                 0, vk::IndexType::UINT32)
   };
 
   unsafe {
     device.cmd_bind_descriptor_sets(*command_buffer,
                                     vk::PipelineBindPoint::GRAPHICS,
-                                    *pipeline_layout,
+                                    render_state.pipeline_layout,
                                     0,
                                     &[*descriptor_set],
                                     &[])
@@ -101,14 +98,14 @@ pub fn generate_scene_commands(command_buffer: &vk::CommandBuffer,
   };
 
   unsafe {
-    device.cmd_push_constants(*command_buffer, *pipeline_layout,
+    device.cmd_push_constants(*command_buffer, render_state.pipeline_layout,
                               vk::ShaderStageFlags::VERTEX,
                               0,
                               push_block_bytes)
   };
 
   unsafe {
-    device.cmd_draw_indexed(*command_buffer, index_count as u32,
+    device.cmd_draw_indexed(*command_buffer, render_state.index_count as u32,
                             1, 0, 0, 0)
   };
 
@@ -116,6 +113,6 @@ pub fn generate_scene_commands(command_buffer: &vk::CommandBuffer,
 
   unsafe { device.end_command_buffer(*command_buffer) }?;
 
-  Ok(())
+  Ok(command_buffer)
 }
 
diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs
index 51483cf..1b5b602 100644
--- a/src/graphics_window_dressing.rs
+++ b/src/graphics_window_dressing.rs
@@ -27,8 +27,9 @@ pub const N_SIMULTANEOUS_FRAMES: usize = 5;
 
 //   The WindowDressing collects the Vulkan graphics objects which need to be
 // regenerated or modified when the window changes in certain ways, such as
-// resizing. The ones which don't need that are collected above, in
-// PermanentGraphicsState.
+// resizing, but are not needed during rendering. The ones which don't need to
+// be regenerated are collected in PermanentGraphicsState. The ones which are
+// needed during rendering are collected in RenderState, below.
 #[derive(Debug)]
 pub struct WindowDressing {
   pub swapchain: Swapchain,
@@ -42,23 +43,9 @@ pub struct WindowDressing {
   depth_image_view: vk::ImageView,
   depth_format: vk::Format,
 
-  pub render_pass: vk::RenderPass,
-
-  pub pipeline: vk::Pipeline,
-  pub pipeline_layout: vk::PipelineLayout,
-
-  pub framebuffers: Vec<vk::Framebuffer>,
-
   primary_command_pool: vk::CommandPool,
   transient_command_pool: vk::CommandPool,
 
-  pub vertex_buffer: vk::Buffer,
-  vertex_buffer_memory: vk::DeviceMemory,
-
-  pub index_buffer: vk::Buffer,
-  index_buffer_memory: vk::DeviceMemory,
-  pub index_count: usize,
-
   texture_image: vk::Image,
   texture_image_memory: vk::DeviceMemory,
   texture_image_view: vk::ImageView,
@@ -69,13 +56,32 @@ pub struct WindowDressing {
   pub uniform_buffer_memory: Vec<vk::DeviceMemory>,
 
   descriptor_pool: vk::DescriptorPool,
-  pub descriptor_sets: Vec<vk::DescriptorSet>,
-
-  pub command_buffers: Vec<vk::CommandBuffer>,
 
   pub concurrency: Concurrency,
 }
 
+//   The RenderState collects the Vulkan graphics objects which need to be
+// regenerated or modified when the window changes, as with WindowDressing,
+// and which are also used as part of rendering.
+#[derive(Debug)]
+pub struct RenderState {
+  pub render_pass: vk::RenderPass,
+
+  pub pipeline: vk::Pipeline,
+  pub pipeline_layout: vk::PipelineLayout,
+
+  pub vertex_buffer: vk::Buffer,
+  vertex_buffer_memory: vk::DeviceMemory,
+
+  pub index_buffer: vk::Buffer,
+  index_buffer_memory: vk::DeviceMemory,
+  pub index_count: usize,
+
+  pub framebuffers: Vec<vk::Framebuffer>,
+  pub command_buffers: Vec<vk::CommandBuffer>,
+  pub descriptor_sets: Vec<vk::DescriptorSet>,
+}
+
 //   A swapchain is the generalized facility that is used to implement
 // double buffering, triple buffering, rendering passes that feed into each
 // other, and other things of that nature. It's a first-class thing but for
@@ -125,7 +131,6 @@ impl WindowDressing {
     let physical_device = &for_reinit.physical_device;
     let sample_count = for_reinit.sample_count;
     let indices = &for_reinit.indices;
-    let descriptor_set_layout = &for_reinit.descriptor_set_layout;
 
     let swapchain = init_swapchain(
             window, instance, surface, &physical_device, device, &indices)?;
@@ -138,30 +143,9 @@ impl WindowDressing {
             = init_depth(instance, &physical_device, device,
                          &swapchain.extent, sample_count)?;
 
-    let render_pass = init_render_pass(device, sample_count,
-                                       &swapchain.format, &depth_format)?;
-
-    let (pipeline_layout, pipeline)
-            = init_pipeline(device, descriptor_set_layout, &swapchain.extent,
-                            sample_count, &render_pass)?;
-
-    let framebuffers = init_framebuffers(
-            device, &swapchain.extent, &swapchain.image_views,
-            &color_image_view, &depth_image_view, &render_pass)?;
-
     let (primary_command_pool, transient_command_pool)
             = init_command_pools(device, indices)?;
 
-    let (vertices, indices) = load_model()?;
-    let index_count = indices.len();
-
-    let (vertex_buffer, vertex_buffer_memory)
-            = init_vertex_buffer(vertices, instance, physical_device, device,
-                                 graphics_queue, &transient_command_pool)?;
-    let (index_buffer, index_buffer_memory)
-            = init_index_buffer(indices, instance, physical_device, device,
-                                graphics_queue, &transient_command_pool)?;
-
     let (texture_image, texture_image_memory, texture_image_view, mip_count)
             = init_texture(instance, physical_device, device,
                            graphics_queue, &transient_command_pool)?;
@@ -174,16 +158,6 @@ impl WindowDressing {
 
     let descriptor_pool
             = init_descriptor_pool(device, swapchain.images.len())?;
-    let descriptor_sets
-            = init_descriptor_sets(device, descriptor_set_layout,
-                                   &uniform_buffers, &descriptor_pool,
-                                   swapchain.images.len(),
-                                   &texture_image_view, &sampler)?;
-
-    let command_buffers = init_command_buffers(
-            device, &swapchain.extent, &framebuffers, &render_pass,
-            &pipeline_layout, &pipeline, &vertex_buffer, &index_buffer,
-            index_count, &descriptor_sets, &primary_command_pool)?;
 
     let concurrency = init_concurrency(device, &swapchain.images)?;
 
@@ -196,15 +170,6 @@ impl WindowDressing {
       depth_image_memory,
       depth_image_view,
       depth_format,
-      render_pass,
-      pipeline,
-      pipeline_layout,
-      framebuffers,
-      vertex_buffer,
-      vertex_buffer_memory,
-      index_buffer,
-      index_buffer_memory,
-      index_count,
       texture_image,
       texture_image_memory,
       texture_image_view,
@@ -213,10 +178,8 @@ impl WindowDressing {
       uniform_buffers,
       uniform_buffer_memory,
       descriptor_pool,
-      descriptor_sets,
       primary_command_pool,
       transient_command_pool,
-      command_buffers,
       concurrency,
     })
   }
@@ -251,17 +214,6 @@ impl WindowDressing {
             = init_depth(instance, &physical_device, device,
                          &swapchain.extent, sample_count)?;
 
-    let render_pass = init_render_pass(device, sample_count,
-                                       &swapchain.format, &depth_format)?;
-
-    let (pipeline_layout, pipeline)
-            = init_pipeline(device, descriptor_set_layout, &swapchain.extent,
-                            sample_count, &render_pass)?;
-
-    let framebuffers = init_framebuffers(
-            device, &swapchain.extent, &swapchain.image_views,
-            &color_image_view, &depth_image_view, &render_pass)?;
-
     let (uniform_buffers, uniform_buffer_memory)
             = init_uniform_buffers(instance, physical_device, device,
                                    swapchain.images.len())?;
@@ -270,19 +222,6 @@ impl WindowDressing {
     let descriptor_pool
             = init_descriptor_pool(device, swapchain.images.len())?;
 
-    let descriptor_sets
-            = init_descriptor_sets(device, descriptor_set_layout,
-                                   &uniform_buffers, &descriptor_pool,
-                                   swapchain.images.len(),
-                                   &self.texture_image_view, &self.sampler)?;
-
-    // Notice that we reused the command pool.
-    let command_buffers = init_command_buffers(
-            device, &swapchain.extent, &framebuffers, &render_pass,
-            &pipeline_layout, &pipeline, &self.vertex_buffer,
-            &self.index_buffer, self.index_count, &descriptor_sets,
-            &self.primary_command_pool)?;
-
     self.concurrency.image_fences.resize(swapchain.images.len(),
                                          vk::Fence::null());
 
@@ -294,15 +233,9 @@ impl WindowDressing {
     self.depth_image_memory = depth_image_memory;
     self.depth_image_view = depth_image_view;
     self.depth_format = depth_format;
-    self.render_pass = render_pass;
-    self.pipeline = pipeline;
-    self.pipeline_layout = pipeline_layout;
-    self.framebuffers = framebuffers;
     self.uniform_buffers = uniform_buffers;
     self.uniform_buffer_memory = uniform_buffer_memory;
     self.descriptor_pool = descriptor_pool;
-    self.descriptor_sets = descriptor_sets;
-    self.command_buffers = command_buffers;
 
     Ok(())
   }
@@ -314,12 +247,6 @@ impl WindowDressing {
   pub fn destroy(mut self, device: &Device) {
     self.destroy_replaceable(device);
 
-    unsafe { device.destroy_buffer(self.vertex_buffer, None) };
-    unsafe { device.free_memory(self.vertex_buffer_memory, None) };
-
-    unsafe { device.destroy_buffer(self.index_buffer, None) };
-    unsafe { device.free_memory(self.index_buffer_memory, None) };
-
     unsafe { device.destroy_image(self.texture_image, None) };
     unsafe { device.free_memory(self.texture_image_memory, None) };
     unsafe { device.destroy_image_view(self.texture_image_view, None) };
@@ -346,20 +273,6 @@ impl WindowDressing {
 
   #[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 buffers in the pool, but do not destroy the
-    // pool itself. Notice also that we only do this for the primary command
-    // pool, because that's the only one where we've kept track of the
-    // buffers. We promise ourselves to free buffers in the transient pool
-    // immediately after using them.
-    unsafe {
-      device.free_command_buffers(self.primary_command_pool,
-                                  &self.command_buffers)
-    };
-
     //   While the descriptor pool is also a pool, it has a preallocated size
     // which will be different next time. So, we destroy it all the way.
     unsafe { device.destroy_descriptor_pool(self.descriptor_pool, None) };
@@ -374,9 +287,6 @@ impl WindowDressing {
       unsafe { device.free_memory(*memory, None) };
     }
 
-    unsafe { device.destroy_pipeline(self.pipeline, None) };
-    unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) };
-    unsafe { device.destroy_render_pass(self.render_pass, None) };
     unsafe { device.destroy_image(self.color_image, None) };
     unsafe { device.free_memory(self.color_image_memory, None) };
     unsafe { device.destroy_image_view(self.color_image_view, None) };
@@ -393,6 +303,167 @@ impl WindowDressing {
 }
 
 
+impl RenderState {
+  pub fn new(permanent: &PermanentGraphicsState,
+             for_reinit: &GraphicsStateForReinit,
+             window_dressing: &WindowDressing)
+      -> Result<Self>
+  {
+    let device = &permanent.device;
+    let instance = &permanent.instance;
+    let graphics_queue = &permanent.graphics_queue;
+    let physical_device = &for_reinit.physical_device;
+    let sample_count = for_reinit.sample_count;
+    let descriptor_set_layout = &for_reinit.descriptor_set_layout;
+    let primary_command_pool = &window_dressing.primary_command_pool;
+    let transient_command_pool = &window_dressing.transient_command_pool;
+    let swapchain = &window_dressing.swapchain;
+    let depth_format = &window_dressing.depth_format;
+    let color_image_view = &window_dressing.color_image_view;
+    let depth_image_view = &window_dressing.depth_image_view;
+    let texture_image_view = &window_dressing.texture_image_view;
+    let uniform_buffers = &window_dressing.uniform_buffers;
+    let descriptor_pool = &window_dressing.descriptor_pool;
+    let sampler = &window_dressing.sampler;
+
+    let render_pass = init_render_pass(device, sample_count,
+                                       &swapchain.format, &depth_format)?;
+
+    let (pipeline_layout, pipeline)
+            = init_pipeline(device, descriptor_set_layout, &swapchain.extent,
+                            sample_count, &render_pass)?;
+
+    let framebuffers = init_framebuffers(
+            device, &swapchain.extent, &swapchain.image_views,
+            &color_image_view, &depth_image_view, &render_pass)?;
+
+    let (vertices, indices) = load_model()?;
+    let index_count = indices.len();
+
+    let command_buffers = init_command_buffers(device, &framebuffers,
+                                               primary_command_pool)?;
+
+    let (vertex_buffer, vertex_buffer_memory)
+            = init_vertex_buffer(vertices, instance, physical_device, device,
+                                 graphics_queue, &transient_command_pool)?;
+    let (index_buffer, index_buffer_memory)
+            = init_index_buffer(indices, instance, physical_device, device,
+                                graphics_queue, &transient_command_pool)?;
+
+    let descriptor_sets
+            = init_descriptor_sets(device, descriptor_set_layout,
+                                   &uniform_buffers, &descriptor_pool,
+                                   swapchain.images.len(),
+                                   &texture_image_view, &sampler)?;
+
+    Ok(RenderState {
+      render_pass,
+      pipeline,
+      pipeline_layout,
+      vertex_buffer,
+      vertex_buffer_memory,
+      index_buffer,
+      index_buffer_memory,
+      index_count,
+      framebuffers,
+      command_buffers,
+      descriptor_sets,
+    })
+  }
+
+  //   This relies on its caller to have already waited for the device to be
+  // idle.
+  pub fn reinit(&mut self, permanent: &PermanentGraphicsState,
+                for_reinit: &GraphicsStateForReinit,
+                window_dressing: &WindowDressing)
+      -> Result<()>
+  {
+    let device = &permanent.device;
+    let sample_count = for_reinit.sample_count;
+    let descriptor_set_layout = &for_reinit.descriptor_set_layout;
+    let primary_command_pool = &window_dressing.primary_command_pool;
+    let swapchain = &window_dressing.swapchain;
+    let depth_format = &window_dressing.depth_format;
+    let color_image_view = &window_dressing.color_image_view;
+    let depth_image_view = &window_dressing.depth_image_view;
+    let texture_image_view = &window_dressing.texture_image_view;
+    let uniform_buffers = &window_dressing.uniform_buffers;
+    let descriptor_pool = &window_dressing.descriptor_pool;
+    let sampler = &window_dressing.sampler;
+
+    self.destroy_replaceable(device, primary_command_pool);
+
+    let render_pass = init_render_pass(device, sample_count,
+                                       &swapchain.format, &depth_format)?;
+
+    let (pipeline_layout, pipeline)
+            = init_pipeline(device, descriptor_set_layout, &swapchain.extent,
+                            sample_count, &render_pass)?;
+
+    let framebuffers = init_framebuffers(
+            device, &swapchain.extent, &swapchain.image_views,
+            &color_image_view, &depth_image_view, &render_pass)?;
+
+    // Notice that we reused the command pool.
+    let command_buffers = init_command_buffers(device, &framebuffers,
+                                               primary_command_pool)?;
+
+    let descriptor_sets
+            = init_descriptor_sets(device, descriptor_set_layout,
+                                   &uniform_buffers, &descriptor_pool,
+                                   swapchain.images.len(),
+                                   texture_image_view, sampler)?;
+
+    self.render_pass = render_pass;
+    self.pipeline = pipeline;
+    self.pipeline_layout = pipeline_layout;
+    self.framebuffers = framebuffers;
+    self.command_buffers = command_buffers;
+    self.descriptor_sets = descriptor_sets;
+
+    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,
+                 window_dressing: &WindowDressing)
+  {
+    self.destroy_replaceable(device, &window_dressing.primary_command_pool);
+
+    unsafe { device.destroy_buffer(self.vertex_buffer, None) };
+    unsafe { device.free_memory(self.vertex_buffer_memory, None) };
+
+    unsafe { device.destroy_buffer(self.index_buffer, None) };
+    unsafe { device.free_memory(self.index_buffer_memory, None) };
+  }
+
+  #[allow(unsafe_code)]
+  fn destroy_replaceable(&mut self, device: &Device,
+                         primary_command_pool: &vk::CommandPool)
+  {
+    for framebuffer in &self.framebuffers {
+      unsafe { device.destroy_framebuffer(*framebuffer, None) };
+    }
+
+    //   Notice that we free the buffers in the pool, but do not destroy the
+    // pool itself. Notice also that we only do this for the primary command
+    // pool, because that's the only one where we've kept track of the
+    // buffers. We promise ourselves to free buffers in the transient pool
+    // immediately after using them.
+    unsafe {
+      device.free_command_buffers(*primary_command_pool,
+                                  &self.command_buffers)
+    };
+
+    unsafe { device.destroy_pipeline(self.pipeline, None) };
+    unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) };
+    unsafe { device.destroy_render_pass(self.render_pass, None) };
+  }
+}
+
+
 #[allow(unsafe_code)]
 fn init_swapchain(window: &Window, instance: &Instance,
                   surface: &vk::SurfaceKHR,
@@ -1041,15 +1112,7 @@ fn init_command_pools(device: &Device, indices: &QueueFamilyIndices)
 
 #[allow(unsafe_code)]
 fn init_command_buffers(device: &Device,
-                        extent: &vk::Extent2D,
                         framebuffers: &Vec<vk::Framebuffer>,
-                        render_pass: &vk::RenderPass,
-                        pipeline_layout: &vk::PipelineLayout,
-                        pipeline: &vk::Pipeline,
-                        vertex_buffer: &vk::Buffer,
-                        index_buffer: &vk::Buffer,
-                        index_count: usize,
-                        descriptor_sets: &Vec<vk::DescriptorSet>,
                         command_pool: &vk::CommandPool)
     -> Result<Vec<vk::CommandBuffer>>
 {
diff --git a/src/main.rs b/src/main.rs
index 8a42147..10b203a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,7 @@ use crate::graphics_permanent::{
   PermanentGraphicsState, GraphicsStateForReinit
 };
 use crate::graphics_window_dressing::{
-  WindowDressing, N_SIMULTANEOUS_FRAMES
+  WindowDressing, RenderState, N_SIMULTANEOUS_FRAMES
 };
 use crate::linear_algebra::{ Vec3, Mat4, Transformation };
 use crate::shader_data::UniformBlock;
@@ -34,6 +34,7 @@ struct Surreality {
   permanent: RefCell<Option<PermanentGraphicsState>>,
   for_reinit: RefCell<Option<GraphicsStateForReinit>>,
   window_dressing: RefCell<Option<WindowDressing>>,
+  render_state: RefCell<Option<RenderState>>,
   is_minimized: bool,
   is_reinit_queued: bool,
   frame_index: usize,
@@ -50,6 +51,7 @@ impl Surreality {
       permanent: RefCell::new(None),
       for_reinit: RefCell::new(None),
       window_dressing: RefCell::new(None),
+      render_state: RefCell::new(None),
       is_minimized: false,
       is_reinit_queued: false,
       frame_index: 0,
@@ -66,9 +68,13 @@ impl Surreality {
         = PermanentGraphicsState::new(event_loop)?;
 
     if enable_swapchain.0 {
-      *self.window_dressing.get_mut()
-          = Some(WindowDressing::new(&permanent, &for_reinit,
-                                     enable_anisotropy)?);
+      let window_dressing = WindowDressing::new(&permanent, &for_reinit,
+                                                enable_anisotropy)?;
+      let render_state = RenderState::new(&permanent, &for_reinit,
+                                          &window_dressing)?;
+
+      *self.window_dressing.get_mut() = Some(window_dressing);
+      *self.render_state.get_mut() = Some(render_state);
     }
 
     *self.permanent.get_mut() = Some(permanent);
@@ -82,8 +88,10 @@ impl Surreality {
        && let Some(for_reinit) = self.for_reinit.borrow().as_ref()
        && let Some(window_dressing)
               = self.window_dressing.borrow_mut().as_mut()
+       && let Some(render_state) = self.render_state.borrow_mut().as_mut()
     {
       window_dressing.reinit(permanent, for_reinit)?;
+      render_state.reinit(permanent, for_reinit, &window_dressing)?;
     }
 
     Ok(())
@@ -109,6 +117,7 @@ impl Surreality {
     if let Some(permanent) = self.permanent.borrow().as_ref()
        && let Some(window_dressing)
               = self.window_dressing.borrow_mut().as_mut()
+       && let Some(render_state) = self.render_state.borrow_mut().as_mut()
        && window_id == permanent.window.id()
     {
       let device = &permanent.device;
@@ -141,16 +150,9 @@ impl Surreality {
 
       let time = self.simulation_start.elapsed().as_secs_f32();
 
-      let command_buffer = window_dressing.command_buffers[image_index];
-      let framebuffer = window_dressing.framebuffers[image_index];
-      let descriptor_set = window_dressing.descriptor_sets[image_index];
-
-      crate::graphics_scene::generate_scene_commands(
-          &command_buffer, time, device, &window_dressing.swapchain.extent,
-          &framebuffer, &window_dressing.render_pass,
-          &window_dressing.pipeline_layout, &window_dressing.pipeline,
-          &window_dressing.vertex_buffer, &window_dressing.index_buffer,
-          window_dressing.index_count, &descriptor_set)?;
+      let command_buffer = crate::graphics_scene::generate_scene_commands(
+              image_index, time, &render_state, device,
+              &window_dressing.swapchain.extent)?;
 
       render_uniforms(time, device,
                       &window_dressing.uniform_buffer_memory[image_index],
@@ -160,7 +162,7 @@ impl Surreality {
       let second_semaphores = [*rendering_finished_semaphore];
 
       let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
-      let command_buffers = [command_buffer];
+      let command_buffers = [*command_buffer];
       let submit_info = vk::SubmitInfo::builder()
                             .wait_semaphores(&first_semaphores)
                             .wait_dst_stage_mask(&wait_stages)
@@ -213,6 +215,10 @@ impl Drop for Surreality {
       unsafe { permanent.device.device_wait_idle() }.unwrap();
 
       if let Some(window_dressing) = self.window_dressing.replace(None) {
+        if let Some(render_state) = self.render_state.replace(None) {
+          render_state.destroy(&permanent.device, &window_dressing);
+        }
+
         window_dressing.destroy(&permanent.device);
       }