summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-07-07 18:00:13 -0700
committerIrene Knapp <ireneista@irenes.space>2026-07-07 23:15:29 -0700
commitb8b25933a68616b9e541300faf0574b517e2d9ac (patch)
tree861897a35dba5182b3e619f14ba5065d8d90952f
parentce5f18f730acda5300dcd4febfccce6425b0d634 (diff)
reorganize init logic for clear lifecycles
yeahhhhhh, it wasn't viable to do this while following that part of the tutorial, but it was always gonna be needed

anyway, now all the various pieces of initialization are associated functions instead of methods, which makes it dramatically easier to see what the data dependencies are

Change-Id: Ie63272945430e27d1026ad3cb724d8ac2d2f6047
Force-Push: yes of course
-rw-r--r--src/main.rs1474
1 files changed, 722 insertions, 752 deletions
diff --git a/src/main.rs b/src/main.rs
index 7c04245..3bba33b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,7 @@
 #![deny(unsafe_code)]
 use crate::error::*;
 
-use std::cell::OnceCell;
+use std::cell::RefCell;
 use std::collections::{ BTreeMap, BTreeSet, HashSet };
 use std::ffi::{ c_void, CStr };
 use vulkanalia::{ Device, Entry, Instance, Version };
@@ -52,6 +52,13 @@ impl<T> Acceptable<T> {
       panic!("Unwrapped a rejected Acceptable.");
     }
   }
+
+  fn require(self) -> Result<T> {
+    match self {
+      Acceptable::Accepted(value) => Ok(value),
+      Acceptable::Rejected(message) => Err(Error { message }),
+    }
+  }
 }
 
 #[derive(Debug)]
@@ -73,46 +80,35 @@ struct Surreality {
   // provided by winit, and may be X11, Wayland, or some more curious thing.
   // The way we initialize Vulkan requires us to have at least one of these;
   // we could have more, but for now, we don't.
-  window: OnceCell<Window>,
-
-  //   There are a few Vulkan features (in the informal sense of "feature")
-  // that we want to be able to run both with and without. Here, we have
-  // booleans describing which we're doing. These are computed during instance
-  // and device initialization.
-  //
-  //   In the cases of validation and portability, the booleans are only used
-  // to communicate between initialization phases, and aren't actually needed
-  // during rendering. We keep them around anyway though because they're small
-  // and it's more convenient to have them all in one place.
-  enable_validation: OnceCell<bool>,
-  enable_portability: OnceCell<bool>,
-  enable_swapchain: OnceCell<bool>,
+  window: RefCell<Option<Window>>,
 
   //   The Vulkan "entry" is the part of the Vulkan library ecosystem that's
-  // responsible for finding and loading the other parts.
-  entry: OnceCell<Entry>,
+  // responsible for finding and loading the other parts. Once we have the
+  // instance, the entry is never directly used again, but we retain it
+  // because doing otherwise would segfault.
+  entry: RefCell<Option<Entry>>,
 
   //   The Vulkan "instance" is the bulk of the Vulkan library, with most of
   // the high-level responsibilities around lifecycle management.
-  instance: OnceCell<Instance>,
+  instance: RefCell<Option<Instance>>,
 
   //   The debug messager is a Vulkan object representing our callback which
   // Vulkan uses to tell us things.
   //
   //   Vulkan spells "messager" as "messenger", but this is absurd
   // over-formality and we don't indulge it.
-  debug_messager: OnceCell<vk::DebugUtilsMessengerEXT>,
+  debug_messager: RefCell<Option<vk::DebugUtilsMessengerEXT>>,
 
   //   The Vulkan "surface" is the destination that rendering happens into.
   // It is connected to the window but distinct from it.
-  surface: OnceCell<vk::SurfaceKHR>,
+  surface: RefCell<Option<vk::SurfaceKHR>>,
 
   //   The Vulkan "device" is the abstraction for a GPU. A physical one is the
-  // actual GPU, and a logical one is our connection to it. We track both.
-  // We'll be referencing the logical device a lot, so we follow Vulkan's lead
-  // and let it have a short variable name.
-  physical_device: OnceCell<vk::PhysicalDevice>,
-  device: OnceCell<Device>,
+  // actual GPU, and a logical one is our connection to it. We pick a physical
+  // device during initialization, but only the logical one is used later, so
+  // it's all we track. We'll be referencing the logical device a lot, so we
+  // follow Vulkan's lead and let it have a short variable name.
+  device: RefCell<Option<Device>>,
 
   //   Vulkan has a first-class concept of command queues. We have two of
   // them, one for graphics drawing commands and one for presentation.
@@ -125,31 +121,59 @@ struct Surreality {
   //
   //   Yes, this means the compiler has to deal with pointer aliasing
   // concerns, which have a tendency to defeat optimizations.
-  graphics_queue: OnceCell<vk::Queue>,
-  presentation_queue: OnceCell<vk::Queue>,
+  graphics_queue: RefCell<Option<vk::Queue>>,
+  presentation_queue: RefCell<Option<vk::Queue>>,
+
+  swapchain: RefCell<Option<Swapchain>>,
 
-  //   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
-  // now, we use at most one of it. We also support running without one.
-  swapchain: OnceCell<vk::SwapchainKHR>,
-  swapchain_images: OnceCell<Vec<vk::Image>>,
-  swapchain_image_views: OnceCell<Vec<vk::ImageView>>,
-  format: OnceCell<vk::Format>,
-  extent: OnceCell<vk::Extent2D>,
+  render_pass: RefCell<Option<vk::RenderPass>>,
 
-  render_pass: OnceCell<vk::RenderPass>,
+  pipeline: RefCell<Option<vk::Pipeline>>,
+  pipeline_layout: RefCell<Option<vk::PipelineLayout>>,
 
-  pipeline: OnceCell<vk::Pipeline>,
-  pipeline_layout: OnceCell<vk::PipelineLayout>,
+  framebuffers: RefCell<Option<Vec<vk::Framebuffer>>>,
 
-  framebuffers: OnceCell<Vec<vk::Framebuffer>>,
+  command_pool: RefCell<Option<vk::CommandPool>>,
+  command_buffers: RefCell<Option<Vec<vk::CommandBuffer>>>,
 
-  command_pool: OnceCell<vk::CommandPool>,
-  command_buffers: OnceCell<Vec<vk::CommandBuffer>>,
+  concurrency: RefCell<Option<Concurrency>>,
 
-  image_available_semaphores: OnceCell<Vec<vk::Semaphore>>,
-  rendering_finished_semaphores: OnceCell<Vec<vk::Semaphore>>,
+  frame_index: usize,
+}
+
+struct InstanceCreation {
+  instance: Instance,
+  debug_messager: Option<vk::DebugUtilsMessengerEXT>,
+  enable_portability: bool,
+  enable_validation: bool,
+}
+
+struct DeviceCreation {
+  physical_device: vk::PhysicalDevice,
+  device: Device,
+  indices: QueueFamilyIndices,
+  graphics_queue: vk::Queue,
+  presentation_queue: vk::Queue,
+  enable_swapchain: bool,
+}
+
+//   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
+// now, we use at most one of it. We also support running without one.
+#[derive(Debug)]
+struct Swapchain {
+  swapchain: vk::SwapchainKHR,
+  images: Vec<vk::Image>,
+  image_views: Vec<vk::ImageView>,
+  format: vk::Format,
+  extent: vk::Extent2D,
+}
+
+#[derive(Debug)]
+struct Concurrency {
+  image_available_semaphores: Vec<vk::Semaphore>,
+  rendering_finished_semaphores: Vec<vk::Semaphore>,
 
   //   Okay, the lifetime management on the fences is really subtle. There is
   // one fence for each frame, and frame_fences holds the authoritative
@@ -163,99 +187,104 @@ struct Surreality {
   // of the frame fence. This happens during rendering of the frame, so the
   // frame fence is in the "signaled" state. It will be reset right before
   // submitting the queue, then signaled again when the submission completes.
-  frame_fences: OnceCell<Vec<vk::Fence>>,
-  image_fences: Vec<vk::Fence>,
-
-  frame_index: usize,
+  frame_fences: Vec<vk::Fence>,
+  image_fences: RefCell<Vec<vk::Fence>>,
 }
 
 impl Surreality {
   fn new() -> Self {
     Surreality {
-      window: OnceCell::new(),
-      enable_validation: OnceCell::new(),
-      enable_portability: OnceCell::new(),
-      enable_swapchain: OnceCell::new(),
-      entry: OnceCell::new(),
-      instance: OnceCell::new(),
-      debug_messager: OnceCell::new(),
-      surface: OnceCell::new(),
-      physical_device: OnceCell::new(),
-      device: OnceCell::new(),
-      graphics_queue: OnceCell::new(),
-      presentation_queue: OnceCell::new(),
-      swapchain: OnceCell::new(),
-      swapchain_images: OnceCell::new(),
-      swapchain_image_views: OnceCell::new(),
-      format: OnceCell::new(),
-      extent: OnceCell::new(),
-      render_pass: OnceCell::new(),
-      pipeline: OnceCell::new(),
-      pipeline_layout: OnceCell::new(),
-      framebuffers: OnceCell::new(),
-      command_pool: OnceCell::new(),
-      command_buffers: OnceCell::new(),
-      image_available_semaphores: OnceCell::new(),
-      rendering_finished_semaphores: OnceCell::new(),
-      frame_fences: OnceCell::new(),
-      image_fences: Vec::new(),
+      window: RefCell::new(None),
+      entry: RefCell::new(None),
+      instance: RefCell::new(None),
+      debug_messager: RefCell::new(None),
+      surface: RefCell::new(None),
+      device: RefCell::new(None),
+      graphics_queue: RefCell::new(None),
+      presentation_queue: RefCell::new(None),
+      swapchain: RefCell::new(None),
+      render_pass: RefCell::new(None),
+      pipeline: RefCell::new(None),
+      pipeline_layout: RefCell::new(None),
+      framebuffers: RefCell::new(None),
+      command_pool: RefCell::new(None),
+      command_buffers: RefCell::new(None),
+      concurrency: RefCell::new(None),
       frame_index: 0,
     }
   }
 
   fn init(&mut self, event_loop: &ActiveEventLoop) -> Result<()> {
-    // TODO refactor all these to not be methods, for better isolation
+    let window = Self::init_window(event_loop)?;
+    let entry = Self::init_vulkan_entry()?;
 
-    if self.window.get().is_none() {
-      self.init_window(event_loop)?;
-    }
+    let InstanceCreation {
+      instance,
+      debug_messager,
 
-    if self.entry.get().is_none() {
-      self.init_vulkan_entry()?;
-    }
-
-    if self.instance.get().is_none() {
-      self.init_vulkan_instance()?;
-    }
-
-    if self.surface.get().is_none() {
-      self.init_vulkan_surface()?;
-    }
-
-    if self.device.get().is_none() {
-      self.init_vulkan_device()?;
-    }
-
-    if self.swapchain.get().is_none()
-       && *self.enable_swapchain.get().unwrap()
-    {
-      self.init_vulkan_swapchain()?;
-    }
-
-    if self.render_pass.get().is_none() {
-      self.init_render_pass()?;
-    }
-
-    if self.pipeline.get().is_none() {
-      self.init_pipeline()?;
-    }
-
-    if self.framebuffers.get().is_none() {
-      self.init_framebuffers()?;
-    }
-
-    if self.command_buffers.get().is_none() {
-      self.init_commands()?;
+      //   There are a few Vulkan features (in the informal sense of
+      // "feature") that we want to be able to run both with and without.
+      // Here, we have booleans describing which we're doing.
+      //
+      //   These are only used to communicate between initialization phases;
+      // we don't keep them around after that.
+      enable_portability,
+      enable_validation
+    } = Self::init_vulkan_instance(&window, &entry)?;
+
+    let surface = Self::init_surface(&window, &instance)?;
+
+    let DeviceCreation {
+      physical_device, device, indices, graphics_queue, presentation_queue,
+      enable_swapchain
+    } = Self::init_vulkan_device(&instance, &surface,
+                                 enable_validation, enable_portability)?;
+
+    if enable_swapchain {
+      let swapchain = Self::init_swapchain(
+              &window, &instance, &surface, &physical_device, &device,
+              &indices)?;
+
+      let render_pass = Surreality::init_render_pass(&device,
+                                                     &swapchain.format)?;
+
+      let (pipeline_layout, pipeline)
+              = Self::init_pipeline(&device, &swapchain.extent,
+                                    &render_pass)?;
+
+      let framebuffers = Self::init_framebuffers(
+              &device, &swapchain.extent, &swapchain.image_views,
+              &render_pass)?;
+
+      let (command_pool, command_buffers)
+              = Self::init_commands(&device, &swapchain.extent, &framebuffers,
+                                    &render_pass, &pipeline, &indices)?;
+
+      let concurrency = Self::init_concurrency(&device, &swapchain.images)?;
+
+      *self.swapchain.get_mut() = Some(swapchain);
+      *self.render_pass.get_mut() = Some(render_pass);
+      *self.pipeline.get_mut() = Some(pipeline);
+      *self.pipeline_layout.get_mut() = Some(pipeline_layout);
+      *self.framebuffers.get_mut() = Some(framebuffers);
+      *self.command_pool.borrow_mut() = Some(command_pool);
+      *self.command_buffers.borrow_mut() = Some(command_buffers);
+      *self.concurrency.get_mut() = Some(concurrency);
     }
 
-    if self.image_available_semaphores.get().is_none() {
-      self.init_concurrency()?;
-    }
+    *self.window.get_mut() = Some(window);
+    *self.entry.get_mut() = Some(entry);
+    *self.instance.get_mut() = Some(instance);
+    *self.debug_messager.get_mut() = debug_messager;
+    *self.surface.get_mut() = Some(surface);
+    *self.device.get_mut() = Some(device);
+    *self.graphics_queue.get_mut() = Some(graphics_queue);
+    *self.presentation_queue.get_mut() = Some(presentation_queue);
 
     Ok(())
   }
 
-  fn init_window(&mut self, event_loop: &ActiveEventLoop) -> Result<()> {
+  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
     // init_vulkan_instance(), below.
@@ -263,15 +292,13 @@ impl Surreality {
             .with_title("Love, Curiosity, Justice")
             .with_inner_size(LogicalSize::new(1024, 768));
 
-    let window: Window = event_loop.create_window(window_attributes)?;
-
-    self.window.set(window).unwrap();
-
-    Ok(())
+    Ok(event_loop.create_window(window_attributes)?)
   }
 
+  // TODO this is another candidate for rolling into its parent
+  // (in addition to init_surface, which is below. what is time)
   #[allow(unsafe_code)]
-  fn init_vulkan_entry(&mut self) -> Result<()> {
+  fn init_vulkan_entry() -> Result<Entry> {
     //   Okay, so, a Vulkan "entry" is a small set of functions which are used
     // to dynamically load all the rest of Vulkan. It's our responsibility to
     // know how to load the entry, then it will take care of the rest. At
@@ -284,15 +311,13 @@ impl Surreality {
     let loader = unsafe { LibloadingLoader::new(LIBRARY) }?;
     let entry = unsafe { Entry::new(loader) }?;
 
-    self.entry.set(entry).unwrap();
-
-    Ok(())
+    Ok(entry)
   }
 
   #[allow(unsafe_code)]
-  fn init_vulkan_instance(&mut self) -> Result<()> {
-    let entry = self.entry.get().unwrap();
-
+  fn init_vulkan_instance(window: &Window, entry: &Entry)
+      -> Result<InstanceCreation>
+  {
     let enable_validation = cfg!(feature = "vulkan-validation")
                             || cfg!(debug_assertions);
 
@@ -325,14 +350,16 @@ impl Surreality {
     //   This is possible because of an integration between Vulkanalia and
     // winit, which is enabled by Vulkanalia's "window" feature.
     for extension in vulkanalia::window::get_required_instance_extensions(
-                         self.window.get().unwrap())
+                         window)
     {
       extensions.push(extension.as_ptr());
     }
 
     //   Deal with Vulkan's thing about opting in to non-conforming
     // implementations.
-    if entry.version()? >= VULKAN_FIRST_PORTABILITY_VERSION {
+    let enable_portability = if entry.version()?
+                                >= VULKAN_FIRST_PORTABILITY_VERSION
+    {
       if cfg!(target_os = "macos") {
         // Vulkan on the Mac is not fully conforming.
         extensions.push(
@@ -341,31 +368,31 @@ impl Surreality {
             vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name.as_ptr());
         flags.insert(vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR);
 
-        self.enable_portability.set(true).unwrap();
+        true
       } else {
-        self.enable_portability.set(false).unwrap();
+        false
       }
     } else {
-      self.enable_portability.set(false).unwrap();
-    }
+      false
+    };
 
     // Request the LunarG validation layer, when appropriate.
     let validation_layer_name = vk::ExtensionName::from_bytes(
                                     b"VK_LAYER_KHRONOS_validation");
-    if enable_validation {
+    let enable_validation = if enable_validation {
       if available_layers.contains(&validation_layer_name) {
         layers.push(validation_layer_name.as_ptr());
 
-        self.enable_validation.set(enable_validation).unwrap();
+        true
       } else {
         eprintln!("Vulkan validation requested at build time, \
                    but no validation layer available.");
 
-        self.enable_validation.set(false).unwrap();
+        false
       }
     } else {
-      self.enable_validation.set(false).unwrap();
-    }
+      false
+    };
 
     //   Request the debug extension. This is the first of three bits of code
     // that deal with this, and has the resonsibility of making sure the
@@ -428,27 +455,28 @@ impl Surreality {
     //   Configure the debug extension. This is the last of three bits of code
     // that deal with this, and has the responsibility of asking the instance,
     // which now exists, to create the debug messager.
-    if let Some(debug_info) = debug_info
-       && self.debug_messager.get().is_none()
-    {
+    let debug_messager = if let Some(debug_info) = debug_info {
       #[allow(unsafe_code)]
-      let debug_messager = unsafe {
+      Some(unsafe {
         instance.create_debug_utils_messenger_ext(&debug_info, None)
-      }?;
-
-      self.debug_messager.set(debug_messager).unwrap();
-    }
-
-    self.instance.set(instance).unwrap();
+      }?)
+    } else {
+      None
+    };
 
-    Ok(())
+    Ok(InstanceCreation {
+      instance,
+      debug_messager,
+      enable_portability,
+      enable_validation,
+    })
   }
 
+  // TODO this is so short that it can likely be eliminated
   #[allow(unsafe_code)]
-  fn init_vulkan_surface(&mut self) -> Result<()> {
-    let window = self.window.get().unwrap();
-    let instance = self.instance.get().unwrap();
-
+  fn init_surface(window: &Window, instance: &Instance)
+      -> Result<vk::SurfaceKHR>
+  {
     //   Conveniently, Vulkanalia's "window" feature allows it to get the
     // platform-specific stuff directly out of winit for us. This wrapper does
     // not correspond 1:1 to a Vulkan function; rather, it picks the Vulkan
@@ -461,561 +489,517 @@ impl Surreality {
       vulkanalia::window::create_surface(&instance, &window, &window)
     }?;
 
-    self.surface.set(surface).unwrap();
-
-    Ok(())
+    Ok(surface)
   }
 
   #[allow(unsafe_code)]
-  fn init_vulkan_device(&mut self) -> Result<()> {
-    if self.physical_device.get().is_none() {
-      let physical_device = self.pick_vulkan_device()?;
-
-      self.physical_device.set(physical_device).unwrap();
+  fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
+                        enable_validation: bool, enable_portability: bool)
+      -> Result<DeviceCreation>
+  {
+    let physical_device = Self::pick_vulkan_device(instance, surface)?;
+
+    //   We already did the check in score_vulkan_device(), so if it fails
+    // this second time, that's our own bug and we don't need to explain it
+    // to our users.
+    // TODO looks sus though
+    let indices = Self::find_device_queue_family_indices(
+                      instance, surface, &physical_device)?.unwrap();
+
+    //   We enumerate the device extensions here so they can inform
+    // configuration. We already did this in score_vulkan_device(), but here
+    // it is again.
+    let mut available_extensions = HashSet::new();
+    for extension in unsafe {
+      instance.enumerate_device_extension_properties(physical_device, None)
+    }? {
+      available_extensions.insert(extension.extension_name);
     }
+    let available_extensions = available_extensions;
 
-    if self.device.get().is_none() {
-      //   Usually we're content to borrow these, but here, that would give us
-      // ownership problems. In reality they are smart pointer objects that do
-      // not directly contain their substantive pieces, so we can freely clone
-      // them, and in this case it's convenient to do that.
-      let instance = self.instance.get().unwrap().clone();
-      let physical_device = self.physical_device.get().unwrap().clone();
-
-      //   We already did the check in score_vulkan_device(), so if it fails
-      // this second time, that's our own bug and we don't need to explain it
-      // to our users.
-      let indices = self.find_device_queue_family_indices(&physical_device)?
-                        .unwrap();
-
-      //   We enumerate the device extensions here so they can inform
-      // configuration. We already did this in score_vulkan_device(), but here
-      // it is again.
-      let mut available_extensions = HashSet::new();
-      for extension in unsafe {
-        instance.enumerate_device_extension_properties(physical_device, None)
-      }? {
-        available_extensions.insert(extension.extension_name);
-      }
-      let available_extensions = available_extensions;
-
-      //   Old versions of Vulkan want layers to be enabled at the device
-      // level as well. Newer ones will ignore this and just use the instance
-      // layers.
-      let features = vk::PhysicalDeviceFeatures::builder();
-      let mut extensions = Vec::new();
-      let mut layers = Vec::new();
-
-      let validation_layer_name = vk::ExtensionName::from_bytes(
-                                      b"VK_LAYER_KHRONOS_validation");
-      if *self.enable_validation.get().unwrap() {
-        //   It's not concerning if this isn't supported, because device
-        // layers are ignored on recent versions, they're purely historical.
-        if available_extensions.contains(&validation_layer_name) {
-          layers.push(validation_layer_name.as_ptr());
-        }
-      }
+    //   Old versions of Vulkan want layers to be enabled at the device
+    // level as well. Newer ones will ignore this and just use the instance
+    // layers.
+    let features = vk::PhysicalDeviceFeatures::builder();
+    let mut extensions = Vec::new();
+    let mut layers = Vec::new();
 
-      let portability_extension_name = vk::ExtensionName::from_bytes(
-                                           b"VK_KHR_portability_subset");
-      if *self.enable_portability.get().unwrap() {
-        //   This is untested, since the only scenario where it would come up
-        // is on a Mac, which we don't actually support. Sorry, and good luck.
-        if available_extensions.contains(&portability_extension_name) {
-          extensions.push(portability_extension_name.as_ptr());
-        }
+    let validation_layer_name = vk::ExtensionName::from_bytes(
+                                    b"VK_LAYER_KHRONOS_validation");
+    if enable_validation {
+      //   It's not concerning if this isn't supported, because device
+      // layers are ignored on recent versions, they're purely historical.
+      if available_extensions.contains(&validation_layer_name) {
+        layers.push(validation_layer_name.as_ptr());
       }
+    }
 
-      let swapchain_extension_name = vk::KHR_SWAPCHAIN_EXTENSION.name;
-      if available_extensions.contains(&swapchain_extension_name) {
-        //   It's important that we not call the swapchain extension
-        // functions until we've verified the extension is supported. To
-        // emphasize that, we do it on a separate line.
-        //
-        //   We've done this check once already, in scoring, and now here
-        // we are discarding its results a second time. We'll do it for the
-        // third and last time in swapchain creation.
-        if let Acceptable::Accepted(_)
-               = self.find_device_swapchain_features(&physical_device)?
-        {
-          extensions.push(swapchain_extension_name.as_ptr());
-
-          self.enable_swapchain.set(true).unwrap();
-        } else {
-          self.enable_swapchain.set(false).unwrap();
-        }
-      } else {
-        self.enable_swapchain.set(false).unwrap();
+    let portability_extension_name = vk::ExtensionName::from_bytes(
+                                         b"VK_KHR_portability_subset");
+    if enable_portability {
+      //   This is untested, since the only scenario where it would come up
+      // is on a Mac, which we don't actually support. Sorry, and good luck.
+      if available_extensions.contains(&portability_extension_name) {
+        extensions.push(portability_extension_name.as_ptr());
       }
+    }
+
+    let swapchain_extension_name = vk::KHR_SWAPCHAIN_EXTENSION.name;
+    let enable_swapchain = if available_extensions.contains(
+                                  &swapchain_extension_name)
+    {
+      //   It's important that we not call the swapchain extension
+      // functions until we've verified the extension is supported. To
+      // emphasize that, we do it on a separate line.
+      //
+      //   We've done this check once already, in scoring, and now here
+      // we are discarding its results a second time. We'll do it for the
+      // third and last time in swapchain creation.
+      if let Acceptable::Accepted(_) = Self::find_device_swapchain_features(
+                 &instance, &surface, &physical_device)?
+      {
+        extensions.push(swapchain_extension_name.as_ptr());
 
-      //   We have one or more queue family indices; we don't know a priori
-      // how many, because it's possible some of them are the same. We only
-      // want to create one queue per distinct family, so we find the unique
-      // indices...
-      let mut unique_queue_family_indices = BTreeSet::new();
-      unique_queue_family_indices.insert(indices.graphics);
-      unique_queue_family_indices.insert(indices.presentation);
-
-      // ... then add a queue create info struct for each.
-      let mut queues = Vec::new();
-      for index in unique_queue_family_indices {
-        //   Passing the priorities vector also implicitly sets the count of
-        // how many queues we are creating within the family. This nicety is
-        // one of the fun things Vulkanalia's builders do for us.
-        queues.push(vk::DeviceQueueCreateInfo::builder()
-                        .queue_family_index(index)
-                        .queue_priorities(&[1.0]));
+        true
+      } else {
+        false
       }
+    } else {
+      false
+    };
 
-      let device_info = vk::DeviceCreateInfo::builder()
-              .queue_create_infos(&queues)
-              .enabled_layer_names(&layers)
-              .enabled_extension_names(&extensions)
-              .enabled_features(&features);
+    //   We have one or more queue family indices; we don't know a priori
+    // how many, because it's possible some of them are the same. We only
+    // want to create one queue per distinct family, so we find the unique
+    // indices...
+    let mut unique_queue_family_indices = BTreeSet::new();
+    unique_queue_family_indices.insert(indices.graphics);
+    unique_queue_family_indices.insert(indices.presentation);
+
+    // ... then add a queue create info struct for each.
+    let mut queues = Vec::new();
+    for index in unique_queue_family_indices {
+      //   Passing the priorities vector also implicitly sets the count of
+      // how many queues we are creating within the family. This nicety is
+      // one of the fun things Vulkanalia's builders do for us.
+      queues.push(vk::DeviceQueueCreateInfo::builder()
+                      .queue_family_index(index)
+                      .queue_priorities(&[1.0]));
+    }
 
-      let device = unsafe {
-        instance.create_device(*self.physical_device.get().unwrap(),
-                               &device_info, None)
-      }?;
+    let device_info = vk::DeviceCreateInfo::builder()
+            .queue_create_infos(&queues)
+            .enabled_layer_names(&layers)
+            .enabled_extension_names(&extensions)
+            .enabled_features(&features);
 
-      //   So, this is a little confusing. Queues are found in queue families.
-      // The family has an index within the device, and the queue has an index
-      // within the family. We computed the family index above, and when we
-      // created the device we told it to create just a single queue in that
-      // family. Now we pass both indices to find the actual queue object.
-      let graphics_queue = unsafe {
-        device.get_device_queue(indices.graphics, 0)
-      };
+    let device = unsafe {
+      instance.create_device(physical_device, &device_info, None)
+    }?;
 
-      let presentation_queue = unsafe {
-        device.get_device_queue(indices.presentation, 0)
-      };
+    //   So, this is a little confusing. Queues are found in queue families.
+    // The family has an index within the device, and the queue has an index
+    // within the family. We computed the family index above, and when we
+    // created the device we told it to create just a single queue in that
+    // family. Now we pass both indices to find the actual queue object.
+    let graphics_queue = unsafe {
+      device.get_device_queue(indices.graphics, 0)
+    };
 
-      self.device.set(device).unwrap();
-      self.graphics_queue.set(graphics_queue).unwrap();
-      self.presentation_queue.set(presentation_queue).unwrap();
-    }
+    let presentation_queue = unsafe {
+      device.get_device_queue(indices.presentation, 0)
+    };
 
-    Ok(())
+    Ok(DeviceCreation {
+      physical_device,
+      device,
+      indices,
+      graphics_queue,
+      presentation_queue,
+      enable_swapchain
+    })
   }
 
   #[allow(unsafe_code)]
-  fn init_vulkan_swapchain(&mut self) -> Result<()> {
-    // Here, too, we need to clone the smart pointer to simplify ownership.
-    let physical_device = self.physical_device.get().unwrap().clone();
+  fn init_swapchain(window: &Window, instance: &Instance,
+                    surface: &vk::SurfaceKHR,
+                    physical_device: &vk::PhysicalDevice, device: &Device,
+                    indices: &QueueFamilyIndices)
+      -> Result<Swapchain>
+  {
+    let features = Self::find_device_swapchain_features(
+            instance, surface, physical_device)?.require()?;
 
-    if let Acceptable::Accepted(features)
-           = self.find_device_swapchain_features(&physical_device)?
-    {
-      let physical_device = self.physical_device.get().unwrap().clone();
-      let device = self.device.get().unwrap().clone();
-      let surface = self.surface.get().unwrap().clone();
+    let format = Self::pick_surface_format(&features.formats)?;
 
-      let format = self.pick_surface_format(&features.formats)?;
+    let presentation_mode
+            = Self::pick_presentation_mode(&features.presentation_modes)?;
+    let extent = Self::pick_image_extent(window, features.capabilities)?;
 
-      let presentation_mode
-              = self.pick_presentation_mode(&features.presentation_modes)?;
-      let extent = self.pick_image_extent(features.capabilities)?;
+    let mut image_count = features.capabilities.min_image_count + 1;
+    if features.capabilities.max_image_count != 0 {
+      image_count
+          = image_count.clamp(0, features.capabilities.max_image_count);
+    }
 
-      let mut image_count = features.capabilities.min_image_count + 1;
-      if features.capabilities.max_image_count != 0 {
-        image_count
-            = image_count.clamp(0, features.capabilities.max_image_count);
-      }
+    let mut unique_queue_family_indices = BTreeSet::new();
+    unique_queue_family_indices.insert(indices.graphics);
+    unique_queue_family_indices.insert(indices.presentation);
 
-      //   We call this yet again. It needs to return the same thing here
-      // that it did before; fortunately it's simple enough that that's a safe
-      // assumption.
-      let indices = self.find_device_queue_family_indices(&physical_device)?
-                        .unwrap();
-
-      let mut unique_queue_family_indices = BTreeSet::new();
-      unique_queue_family_indices.insert(indices.graphics);
-      unique_queue_family_indices.insert(indices.presentation);
-
-      //   If there's only one queue, we use exclusive sharing mode, which
-      // will allow things to work without locks. Otherwise we use concurrent
-      // mode.
-      let (ordered_indices, sharing_mode)
-              = if unique_queue_family_indices.len() < 2
-      {
-        (vec![indices.graphics], vk::SharingMode::EXCLUSIVE)
-      } else {
-        (vec![indices.graphics, indices.presentation],
-         vk::SharingMode::CONCURRENT)
-      };
+    //   If there's only one queue, we use exclusive sharing mode, which
+    // will allow things to work without locks. Otherwise we use concurrent
+    // mode.
+    let (ordered_indices, sharing_mode)
+            = if unique_queue_family_indices.len() < 2
+    {
+      (vec![indices.graphics], vk::SharingMode::EXCLUSIVE)
+    } else {
+      (vec![indices.graphics, indices.presentation],
+       vk::SharingMode::CONCURRENT)
+    };
 
-      let swapchain_info = vk::SwapchainCreateInfoKHR::builder()
-              .surface(surface)
-              .min_image_count(image_count)
-              .image_format(format.format)
-              .image_color_space(format.color_space)
-              .image_extent(extent)
-              .image_array_layers(1)
-              .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
-              .image_sharing_mode(sharing_mode)
-              .queue_family_indices(&ordered_indices)
-              .pre_transform(features.capabilities.current_transform)
-              .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
-              .present_mode(presentation_mode)
-              .clipped(true)
-              .old_swapchain(vk::SwapchainKHR::null());
-
-      let swapchain = unsafe {
-        device.create_swapchain_khr(&swapchain_info, None)
-      }?;
+    let swapchain_info = vk::SwapchainCreateInfoKHR::builder()
+            .surface(*surface)
+            .min_image_count(image_count)
+            .image_format(format.format)
+            .image_color_space(format.color_space)
+            .image_extent(extent)
+            .image_array_layers(1)
+            .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
+            .image_sharing_mode(sharing_mode)
+            .queue_family_indices(&ordered_indices)
+            .pre_transform(features.capabilities.current_transform)
+            .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
+            .present_mode(presentation_mode)
+            .clipped(true)
+            .old_swapchain(vk::SwapchainKHR::null());
+
+    let swapchain = unsafe {
+      device.create_swapchain_khr(&swapchain_info, None)
+    }?;
 
-      let images = unsafe {
-        device.get_swapchain_images_khr(swapchain)
-      }?;
+    let images = unsafe {
+      device.get_swapchain_images_khr(swapchain)
+    }?;
 
-      let mut image_views = Vec::new();
-      for image in &images {
-        let components = vk::ComponentMapping::builder()
-                             .r(vk::ComponentSwizzle::IDENTITY)
-                             .g(vk::ComponentSwizzle::IDENTITY)
-                             .b(vk::ComponentSwizzle::IDENTITY)
-                             .a(vk::ComponentSwizzle::IDENTITY);
-
-        let subresource_range = vk::ImageSubresourceRange::builder()
-                                    .aspect_mask(vk::ImageAspectFlags::COLOR)
-                                    .base_mip_level(0)
-                                    .level_count(1)
-                                    .base_array_layer(0)
-                                    .layer_count(1);
-
-        let view_info = vk::ImageViewCreateInfo::builder()
-                            .image(*image)
-                            .view_type(vk::ImageViewType::_2D)
-                            .format(format.format)
-                            .components(components)
-                            .subresource_range(subresource_range);
-
-        let view = unsafe {
-          device.create_image_view(&view_info, None)
-        }?;
-
-        image_views.push(view);
-      }
+    let mut image_views = Vec::new();
+    for image in &images {
+      let components = vk::ComponentMapping::builder()
+                           .r(vk::ComponentSwizzle::IDENTITY)
+                           .g(vk::ComponentSwizzle::IDENTITY)
+                           .b(vk::ComponentSwizzle::IDENTITY)
+                           .a(vk::ComponentSwizzle::IDENTITY);
+
+      let subresource_range = vk::ImageSubresourceRange::builder()
+                                  .aspect_mask(vk::ImageAspectFlags::COLOR)
+                                  .base_mip_level(0)
+                                  .level_count(1)
+                                  .base_array_layer(0)
+                                  .layer_count(1);
+
+      let view_info = vk::ImageViewCreateInfo::builder()
+                          .image(*image)
+                          .view_type(vk::ImageViewType::_2D)
+                          .format(format.format)
+                          .components(components)
+                          .subresource_range(subresource_range);
+
+      let view = unsafe {
+        device.create_image_view(&view_info, None)
+      }?;
 
-      self.swapchain.set(swapchain).unwrap();
-      self.swapchain_images.set(images).unwrap();
-      self.swapchain_image_views.set(image_views).unwrap();
-      self.format.set(format.format).unwrap();
-      self.extent.set(extent).unwrap();
+      image_views.push(view);
     }
 
-    Ok(())
+    Ok(Swapchain {
+      swapchain, images, image_views,
+      format: format.format,
+      extent
+    })
   }
 
   #[allow(unsafe_code)]
-  fn init_render_pass(&mut self) -> Result<()> {
-    if self.render_pass.get().is_none() {
-      let device = self.device.get().unwrap();
-      let format = self.format.get().unwrap();
-
-      let color_attachment
-              = vk::AttachmentDescription::builder()
-                    .format(*format)
-                    .samples(vk::SampleCountFlags::_1)
-                    .load_op(vk::AttachmentLoadOp::CLEAR)
-                    .store_op(vk::AttachmentStoreOp::STORE)
-                    .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
-                    .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
-                    .initial_layout(vk::ImageLayout::UNDEFINED)
-                    .final_layout(vk::ImageLayout::PRESENT_SRC_KHR);
-
-      let color_attachment_reference
-              = vk::AttachmentReference::builder()
-                    .attachment(0)
-                    .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
-
-      let subpass_attachments = [color_attachment_reference];
-      let subpass = vk::SubpassDescription::builder()
-                        .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
-                        .color_attachments(&subpass_attachments);
-
-      let dependency
-            = vk::SubpassDependency::builder()
-                  .src_subpass(vk::SUBPASS_EXTERNAL)
-                  .src_stage_mask(
-                       vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
-                  .src_access_mask(vk::AccessFlags::empty())
-                  .dst_subpass(0)
-                  .dst_stage_mask(
-                       vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
-                  .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE);
-
-      let render_attachments = [color_attachment];
-      let subpasses = [subpass];
-      let dependencies = [dependency];
-      let render_pass_info = vk::RenderPassCreateInfo::builder()
-                                 .attachments(&render_attachments)
-                                 .subpasses(&subpasses)
-                                 .dependencies(&dependencies);
-
-      let render_pass = unsafe {
-        device.create_render_pass(&render_pass_info, None)
-      }?;
-
-      self.render_pass.set(render_pass).unwrap();
-    }
+  fn init_render_pass(device: &Device, format: &vk::Format)
+      -> Result<vk::RenderPass>
+  {
+    let color_attachment
+            = vk::AttachmentDescription::builder()
+                  .format(*format)
+                  .samples(vk::SampleCountFlags::_1)
+                  .load_op(vk::AttachmentLoadOp::CLEAR)
+                  .store_op(vk::AttachmentStoreOp::STORE)
+                  .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
+                  .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
+                  .initial_layout(vk::ImageLayout::UNDEFINED)
+                  .final_layout(vk::ImageLayout::PRESENT_SRC_KHR);
+
+    let color_attachment_reference
+            = vk::AttachmentReference::builder()
+                  .attachment(0)
+                  .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
+
+    let subpass_attachments = [color_attachment_reference];
+    let subpass = vk::SubpassDescription::builder()
+                      .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
+                      .color_attachments(&subpass_attachments);
+
+    let dependency
+          = vk::SubpassDependency::builder()
+                .src_subpass(vk::SUBPASS_EXTERNAL)
+                .src_stage_mask(
+                     vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
+                .src_access_mask(vk::AccessFlags::empty())
+                .dst_subpass(0)
+                .dst_stage_mask(
+                     vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
+                .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE);
+
+    let render_attachments = [color_attachment];
+    let subpasses = [subpass];
+    let dependencies = [dependency];
+    let render_pass_info = vk::RenderPassCreateInfo::builder()
+                               .attachments(&render_attachments)
+                               .subpasses(&subpasses)
+                               .dependencies(&dependencies);
+
+    let render_pass = unsafe {
+      device.create_render_pass(&render_pass_info, None)
+    }?;
 
-    Ok(())
+    Ok(render_pass)
   }
 
   #[allow(unsafe_code)]
-  fn init_pipeline(&mut self) -> Result<()> {
-    if self.pipeline.get().is_none() {
-      let device = self.device.get().unwrap().clone();
-      let extent = self.extent.get().unwrap().clone();
-      let render_pass = self.render_pass.get().unwrap().clone();
-
-      let vertex_binary = include_bytes!(
-              concat!(env!("OUT_DIR"), "/shader.vert.spv"));
-      let fragment_binary = include_bytes!(
-              concat!(env!("OUT_DIR"), "/shader.frag.spv"));
-
-      let vertex_module = self.load_spirv_shader_module(vertex_binary)?;
-      let fragment_module = self.load_spirv_shader_module(fragment_binary)?;
-
-      let vertex_stage_info = vk::PipelineShaderStageCreateInfo::builder()
-                                  .stage(vk::ShaderStageFlags::VERTEX)
-                                  .module(vertex_module)
+  fn init_pipeline(device: &Device, extent: &vk::Extent2D,
+                   render_pass: &vk::RenderPass)
+      -> Result<(vk::PipelineLayout, vk::Pipeline)>
+  {
+    let vertex_binary = include_bytes!(
+            concat!(env!("OUT_DIR"), "/shader.vert.spv"));
+    let fragment_binary = include_bytes!(
+            concat!(env!("OUT_DIR"), "/shader.frag.spv"));
+
+    let vertex_module
+            = Self::load_spirv_shader_module(device, vertex_binary)?;
+    let fragment_module
+            = Self::load_spirv_shader_module(device, fragment_binary)?;
+
+    let vertex_stage_info = vk::PipelineShaderStageCreateInfo::builder()
+                                .stage(vk::ShaderStageFlags::VERTEX)
+                                .module(vertex_module)
+                                .name(b"main\0");
+
+    let fragment_stage_info = vk::PipelineShaderStageCreateInfo::builder()
+                                  .stage(vk::ShaderStageFlags::FRAGMENT)
+                                  .module(fragment_module)
                                   .name(b"main\0");
 
-      let fragment_stage_info = vk::PipelineShaderStageCreateInfo::builder()
-                                    .stage(vk::ShaderStageFlags::FRAGMENT)
-                                    .module(fragment_module)
-                                    .name(b"main\0");
-
-      let vertex_input_state_info
-              = vk::PipelineVertexInputStateCreateInfo::builder();
-
-      let input_assembly_state_info
-              = vk::PipelineInputAssemblyStateCreateInfo::builder()
-                    .topology(vk::PrimitiveTopology::TRIANGLE_LIST)
-                    .primitive_restart_enable(false);
-
-      let viewport = vk::Viewport::builder()
-                         .x(0.0)
-                         .y(0.0)
-                         .width(extent.width as f32)
-                         .height(extent.height as f32)
-                         .min_depth(0.0)
-                         .max_depth(1.0);
-      let viewports = [viewport];
-
-      let scissor = vk::Rect2D::builder()
-                         .offset(vk::Offset2D { x: 0, y: 0 })
-                         .extent(extent);
-      let scissor_list = [scissor];
-
-      let viewport_state_info = vk::PipelineViewportStateCreateInfo::builder()
-                         .viewports(&viewports)
-                         .scissors(&scissor_list);
-
-      let rasterizer_state_info
-              = vk::PipelineRasterizationStateCreateInfo::builder()
-                    .depth_clamp_enable(false)
-                    .rasterizer_discard_enable(false)
-                    .polygon_mode(vk::PolygonMode::FILL)
-                    .line_width(1.0)
-                    .cull_mode(vk::CullModeFlags::BACK)
-                    .front_face(vk::FrontFace::CLOCKWISE)
-                    .depth_bias_enable(false);
-
-      let multisample_state_info
-              = vk::PipelineMultisampleStateCreateInfo::builder()
-                    .sample_shading_enable(false)
-                    .rasterization_samples(vk::SampleCountFlags::_1);
-
-      let blend_attachment_info
-              = vk::PipelineColorBlendAttachmentState::builder()
-                    .color_write_mask(vk::ColorComponentFlags::all())
-                    .blend_enable(false)
-                    .src_color_blend_factor(vk::BlendFactor::ONE)
-                    .dst_color_blend_factor(vk::BlendFactor::ZERO)
-                    .color_blend_op(vk::BlendOp::ADD)
-                    .src_alpha_blend_factor(vk::BlendFactor::ONE)
-                    .dst_alpha_blend_factor(vk::BlendFactor::ZERO)
-                    .alpha_blend_op(vk::BlendOp::ADD);
-      let blend_attachments = [blend_attachment_info];
-
-      let blend_info = vk::PipelineColorBlendStateCreateInfo::builder()
-                           .logic_op_enable(false)
-                           .logic_op(vk::LogicOp::COPY)
-                           .attachments(&blend_attachments)
-                           .blend_constants([0.0, 0.0, 0.0, 0.0]);
-
-      let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder();
-
-      let pipeline_layout = unsafe {
-        device.create_pipeline_layout(&pipeline_layout_info, None)
-      }?;
-
-      let stages = [vertex_stage_info, fragment_stage_info];
-      let pipeline_info
-              = vk::GraphicsPipelineCreateInfo::builder()
-                    .stages(&stages)
-                    .vertex_input_state(&vertex_input_state_info)
-                    .input_assembly_state(&input_assembly_state_info)
-                    .viewport_state(&viewport_state_info)
-                    .rasterization_state(&rasterizer_state_info)
-                    .multisample_state(&multisample_state_info)
-                    .color_blend_state(&blend_info)
-                    .layout(pipeline_layout)
-                    .render_pass(render_pass)
-                    .subpass(0);
-
-      let pipeline = unsafe {
-        device.create_graphics_pipelines(vk::PipelineCache::null(),
-                                         &[pipeline_info], None)
-      }?.0[0];
-
-      self.pipeline_layout.set(pipeline_layout).unwrap();
-      self.pipeline.set(pipeline).unwrap();
+    let vertex_input_state_info
+            = vk::PipelineVertexInputStateCreateInfo::builder();
+
+    let input_assembly_state_info
+            = vk::PipelineInputAssemblyStateCreateInfo::builder()
+                  .topology(vk::PrimitiveTopology::TRIANGLE_LIST)
+                  .primitive_restart_enable(false);
+
+    let viewport = vk::Viewport::builder()
+                       .x(0.0)
+                       .y(0.0)
+                       .width(extent.width as f32)
+                       .height(extent.height as f32)
+                       .min_depth(0.0)
+                       .max_depth(1.0);
+    let viewports = [viewport];
+
+    let scissor = vk::Rect2D::builder()
+                       .offset(vk::Offset2D { x: 0, y: 0 })
+                       .extent(*extent);
+    let scissor_list = [scissor];
+
+    let viewport_state_info = vk::PipelineViewportStateCreateInfo::builder()
+                       .viewports(&viewports)
+                       .scissors(&scissor_list);
+
+    let rasterizer_state_info
+            = vk::PipelineRasterizationStateCreateInfo::builder()
+                  .depth_clamp_enable(false)
+                  .rasterizer_discard_enable(false)
+                  .polygon_mode(vk::PolygonMode::FILL)
+                  .line_width(1.0)
+                  .cull_mode(vk::CullModeFlags::BACK)
+                  .front_face(vk::FrontFace::CLOCKWISE)
+                  .depth_bias_enable(false);
+
+    let multisample_state_info
+            = vk::PipelineMultisampleStateCreateInfo::builder()
+                  .sample_shading_enable(false)
+                  .rasterization_samples(vk::SampleCountFlags::_1);
+
+    let blend_attachment_info
+            = vk::PipelineColorBlendAttachmentState::builder()
+                  .color_write_mask(vk::ColorComponentFlags::all())
+                  .blend_enable(false)
+                  .src_color_blend_factor(vk::BlendFactor::ONE)
+                  .dst_color_blend_factor(vk::BlendFactor::ZERO)
+                  .color_blend_op(vk::BlendOp::ADD)
+                  .src_alpha_blend_factor(vk::BlendFactor::ONE)
+                  .dst_alpha_blend_factor(vk::BlendFactor::ZERO)
+                  .alpha_blend_op(vk::BlendOp::ADD);
+    let blend_attachments = [blend_attachment_info];
+
+    let blend_info = vk::PipelineColorBlendStateCreateInfo::builder()
+                         .logic_op_enable(false)
+                         .logic_op(vk::LogicOp::COPY)
+                         .attachments(&blend_attachments)
+                         .blend_constants([0.0, 0.0, 0.0, 0.0]);
+
+    let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder();
+
+    let pipeline_layout = unsafe {
+      device.create_pipeline_layout(&pipeline_layout_info, None)
+    }?;
 
-      unsafe {
-        device.destroy_shader_module(vertex_module, None);
-        device.destroy_shader_module(fragment_module, None);
-      };
-    }
+    let stages = [vertex_stage_info, fragment_stage_info];
+    let pipeline_info
+            = vk::GraphicsPipelineCreateInfo::builder()
+                  .stages(&stages)
+                  .vertex_input_state(&vertex_input_state_info)
+                  .input_assembly_state(&input_assembly_state_info)
+                  .viewport_state(&viewport_state_info)
+                  .rasterization_state(&rasterizer_state_info)
+                  .multisample_state(&multisample_state_info)
+                  .color_blend_state(&blend_info)
+                  .layout(pipeline_layout)
+                  .render_pass(*render_pass)
+                  .subpass(0);
+
+    let pipeline = unsafe {
+      device.create_graphics_pipelines(vk::PipelineCache::null(),
+                                       &[pipeline_info], None)
+    }?.0[0];
+
+    unsafe {
+      device.destroy_shader_module(vertex_module, None);
+      device.destroy_shader_module(fragment_module, None);
+    };
 
-    Ok(())
+    Ok((pipeline_layout, pipeline))
   }
 
   #[allow(unsafe_code)]
-  fn init_framebuffers(&mut self) -> Result<()> {
-    if self.framebuffers.get().is_none() {
-      let device = self.device.get().unwrap().clone();
-      let extent = self.extent.get().unwrap().clone();
-      let image_views = self.swapchain_image_views.get().unwrap();
-      let render_pass = self.render_pass.get().unwrap();
-
-      let mut framebuffers = Vec::new();
-
-      for image_view in image_views {
-        let attachments = [*image_view];
-
-        let framebuffer_info = vk::FramebufferCreateInfo::builder()
-                                   .render_pass(*render_pass)
-                                   .attachments(&attachments)
-                                   .width(extent.width)
-                                   .height(extent.height)
-                                   .layers(1);
-
-        let framebuffer = unsafe {
-          device.create_framebuffer(&framebuffer_info, None)
-        }?;
-
-        framebuffers.push(framebuffer);
-      }
+  fn init_framebuffers(device: &Device, extent: &vk::Extent2D,
+                       swapchain_image_views: &Vec<vk::ImageView>,
+                       render_pass: &vk::RenderPass)
+      -> Result<Vec<vk::Framebuffer>>
+  {
+    let mut framebuffers = Vec::new();
 
-      self.framebuffers.set(framebuffers).unwrap();
-    }
+    for image_view in swapchain_image_views {
+      let attachments = [*image_view];
 
-    Ok(())
-  }
+      let framebuffer_info = vk::FramebufferCreateInfo::builder()
+                                 .render_pass(*render_pass)
+                                 .attachments(&attachments)
+                                 .width(extent.width)
+                                 .height(extent.height)
+                                 .layers(1);
 
-  #[allow(unsafe_code)]
-  fn init_commands(&mut self) -> Result<()> {
-    if self.command_pool.get().is_none() {
-      let physical_device = self.physical_device.get().unwrap().clone();
-      let device = self.device.get().unwrap().clone();
+      let framebuffer = unsafe {
+        device.create_framebuffer(&framebuffer_info, None)
+      }?;
 
-      // We call this one last time. It's kind of a problem.
-      let indices = self.find_device_queue_family_indices(&physical_device)?
-                        .unwrap();
+      framebuffers.push(framebuffer);
+    }
 
-      let command_pool_info = vk::CommandPoolCreateInfo::builder()
-                                  .flags(vk::CommandPoolCreateFlags::empty())
-                                  .queue_family_index(indices.graphics);
+    Ok(framebuffers)
+  }
 
-      let command_pool = 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,
+                   indices: &QueueFamilyIndices)
+      -> Result<(vk::CommandPool, Vec<vk::CommandBuffer>)>
+  {
+    // We call this one last time. It's kind of a problem.
 
-      self.command_pool.set(command_pool).unwrap();
-    }
+    let command_pool_info = vk::CommandPoolCreateInfo::builder()
+                                .flags(vk::CommandPoolCreateFlags::empty())
+                                .queue_family_index(indices.graphics);
 
-    if self.command_buffers.get().is_none() {
-      let device = self.device.get().unwrap();
-      let extent = self.extent.get().unwrap();
-      let framebuffers = self.framebuffers.get().unwrap();
-      let render_pass = self.render_pass.get().unwrap();
-      let pipeline = self.pipeline.get().unwrap();
-      let command_pool = self.command_pool.get().unwrap();
-
-      let command_buffer_allocation_info
-              = vk::CommandBufferAllocateInfo::builder()
-                    .command_pool(*command_pool)
-                    .level(vk::CommandBufferLevel::PRIMARY)
-                    .command_buffer_count(framebuffers.len() as u32);
-      let command_buffers = unsafe {
-        device.allocate_command_buffers(&command_buffer_allocation_info)
-      }?;
+    let command_pool = unsafe {
+      device.create_command_pool(&command_pool_info, None)
+    }?;
 
-      for (index, framebuffer) in framebuffers.iter().enumerate() {
-        let command_buffer = command_buffers[index];
+    let command_buffer_allocation_info
+            = vk::CommandBufferAllocateInfo::builder()
+                  .command_pool(command_pool)
+                  .level(vk::CommandBufferLevel::PRIMARY)
+                  .command_buffer_count(framebuffers.len() as u32);
+    let command_buffers = unsafe {
+      device.allocate_command_buffers(&command_buffer_allocation_info)
+    }?;
 
-        let inheritance_info = vk::CommandBufferInheritanceInfo::builder();
+    for (index, framebuffer) in framebuffers.iter().enumerate() {
+      let command_buffer = command_buffers[index];
 
-        let command_buffer_begin_info
-                = vk::CommandBufferBeginInfo::builder()
-                      .flags(vk::CommandBufferUsageFlags::empty())
-                      .inheritance_info(&inheritance_info);
+      let inheritance_info = vk::CommandBufferInheritanceInfo::builder();
 
-        unsafe {
-          device.begin_command_buffer(command_buffer,
-                                      &command_buffer_begin_info)
-        }?;
+      let command_buffer_begin_info
+              = vk::CommandBufferBeginInfo::builder()
+                    .flags(vk::CommandBufferUsageFlags::empty())
+                    .inheritance_info(&inheritance_info);
 
-        let render_area = vk::Rect2D::builder()
-                              .offset(vk::Offset2D::default())
-                              .extent(*extent);
+      unsafe {
+        device.begin_command_buffer(command_buffer,
+                                    &command_buffer_begin_info)
+      }?;
 
-        let clear_value = vk::ClearValue {
-          color: vk::ClearColorValue {
-            float32: [0.0, 0.0, 0.0, 1.0]
-          }
-        };
-        let clear_values = [clear_value];
+      let render_area = vk::Rect2D::builder()
+                            .offset(vk::Offset2D::default())
+                            .extent(*extent);
 
-        let begin_pass_info = vk::RenderPassBeginInfo::builder()
-                                  .render_pass(*render_pass)
-                                  .framebuffer(*framebuffer)
-                                  .render_area(render_area)
-                                  .clear_values(&clear_values);
+      let clear_value = vk::ClearValue {
+        color: vk::ClearColorValue {
+          float32: [0.0, 0.0, 0.0, 1.0]
+        }
+      };
+      let clear_values = [clear_value];
 
-        unsafe {
-          device.cmd_begin_render_pass(command_buffer, &begin_pass_info,
-                                       vk::SubpassContents::INLINE)
-        };
+      let begin_pass_info = vk::RenderPassBeginInfo::builder()
+                                .render_pass(*render_pass)
+                                .framebuffer(*framebuffer)
+                                .render_area(render_area)
+                                .clear_values(&clear_values);
 
-        unsafe {
-          device.cmd_bind_pipeline(command_buffer,
-                                   vk::PipelineBindPoint::GRAPHICS,
-                                   *pipeline)
-        };
+      unsafe {
+        device.cmd_begin_render_pass(command_buffer, &begin_pass_info,
+                                     vk::SubpassContents::INLINE)
+      };
 
-        unsafe { device.cmd_draw(command_buffer, 3, 1, 0, 0) };
+      unsafe {
+        device.cmd_bind_pipeline(command_buffer,
+                                 vk::PipelineBindPoint::GRAPHICS,
+                                 *pipeline)
+      };
 
-        unsafe { device.cmd_end_render_pass(command_buffer) };
+      unsafe { device.cmd_draw(command_buffer, 3, 1, 0, 0) };
 
-        unsafe { device.end_command_buffer(command_buffer) }?;
-      }
+      unsafe { device.cmd_end_render_pass(command_buffer) };
 
-      self.command_buffers.set(command_buffers).unwrap();
+      unsafe { device.end_command_buffer(command_buffer) }?;
     }
 
-    Ok(())
+    Ok((command_pool, command_buffers))
   }
 
   #[allow(unsafe_code)]
-  fn init_concurrency(&mut self) -> Result<()> {
-    let device = self.device.get().unwrap();
-    let swapchain_images = self.swapchain_images.get().unwrap();
+  fn init_concurrency(device: &Device,
+                      swapchain_images: &Vec<vk::Image>)
+      -> Result<Concurrency>
+  {
     let semaphore_info = vk::SemaphoreCreateInfo::builder();
     let fence_info = vk::FenceCreateInfo::builder()
                          .flags(vk::FenceCreateFlags::SIGNALED);
@@ -1023,7 +1007,6 @@ impl Surreality {
     let mut image_available_semaphores = Vec::new();
     let mut rendering_finished_semaphores = Vec::new();
     let mut frame_fences = Vec::new();
-
     for _ in 0 .. N_SIMULTANEOUS_FRAMES {
       image_available_semaphores.push(unsafe {
         device.create_semaphore(&semaphore_info, None)
@@ -1038,17 +1021,17 @@ impl Surreality {
       }?);
     }
 
-    self.image_available_semaphores
-        .set(image_available_semaphores).unwrap();
-    self.rendering_finished_semaphores
-        .set(rendering_finished_semaphores).unwrap();
-    self.frame_fences.set(frame_fences).unwrap();
-
+    let mut image_fences = Vec::new();
     for _ in 0 .. swapchain_images.len() {
-      self.image_fences.push(vk::Fence::null());
+      image_fences.push(vk::Fence::null());
     }
 
-    Ok(())
+    Ok(Concurrency {
+      image_available_semaphores,
+      rendering_finished_semaphores,
+      frame_fences,
+      image_fences: RefCell::new(image_fences),
+    })
   }
 
   //   To Vulkan, a "physical" device is the actual GPU, and a "logical"
@@ -1056,15 +1039,15 @@ impl Surreality {
   // Before we can create a logical device, we must choose which physical
   // device to connect it to.
   #[allow(unsafe_code)]
-  fn pick_vulkan_device(&mut self) -> Result<vk::PhysicalDevice> {
+  fn pick_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR)
+      -> Result<vk::PhysicalDevice>
+  {
     let mut best_device = None;
     let mut best_score = None;
     let mut rejected = BTreeMap::new();
 
-    for device in unsafe {
-      self.instance.get().unwrap().enumerate_physical_devices()
-    }? {
-      match self.score_vulkan_device(&device)? {
+    for device in unsafe { instance.enumerate_physical_devices() }? {
+      match Self::score_vulkan_device(instance, surface, &device)? {
         Acceptable::Accepted(new_score) => {
           if let Some(old_score) = best_score {
             if new_score > old_score {
@@ -1078,8 +1061,7 @@ impl Surreality {
         }
         Acceptable::Rejected(reason) => {
           let properties = unsafe {
-            self.instance.get().unwrap()
-                .get_physical_device_properties(device)
+            instance.get_physical_device_properties(device)
           };
 
           let name = properties.device_name.to_string_lossy().into_owned();
@@ -1114,7 +1096,8 @@ impl Surreality {
   // the outer Result is whether we successfully evaluated the device, and the
   // inner Acceptable is whether we approve of it.
   #[allow(unsafe_code)]
-  fn score_vulkan_device(&mut self, device: &vk::PhysicalDevice)
+  fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
+                         physical_device: &vk::PhysicalDevice)
       -> Result<Acceptable<u64>>
   {
     //   Not all devices support graphics, and not all devices support
@@ -1122,17 +1105,16 @@ impl Surreality {
     // by looking up the indices of the queue families we would use, though
     // we ignore the actual values and recompute them later.
     if let Acceptable::Rejected(rationale)
-           = self.find_device_queue_family_indices(device)?
+           = Self::find_device_queue_family_indices(
+                 instance, surface, physical_device)?
     {
       return Ok(Acceptable::Rejected(rationale));
     }
 
-    let instance = self.instance.get().unwrap();
-
     //   At this point we know the device meets our high-level requirements,
     // so it's just a question of scoring.
     let properties = unsafe {
-      instance.get_physical_device_properties(*device)
+      instance.get_physical_device_properties(*physical_device)
     };
 
     let mut score = 0;
@@ -1158,7 +1140,7 @@ impl Surreality {
     // supports, so we enumerate those.
     let mut available_extensions = HashSet::new();
     for extension in unsafe {
-      instance.enumerate_device_extension_properties(*device, None)
+      instance.enumerate_device_extension_properties(*physical_device, None)
     }? {
       available_extensions.insert(extension.extension_name);
     }
@@ -1171,8 +1153,8 @@ impl Surreality {
       //
       //   With that said, however, it only counts if we're able to actually
       // use it on the surface we have. Let's find out...
-      if let Acceptable::Accepted(_)
-             = self.find_device_swapchain_features(device)?
+      if let Acceptable::Accepted(_) = Self::find_device_swapchain_features(
+                 instance, surface, physical_device)?
       {
         //   We don't count it for enough points to override a device type
         // bracket, but it's good for a lot within the bracket.
@@ -1187,14 +1169,12 @@ impl Surreality {
     Ok(Acceptable::Accepted(score))
   }
 
-  // TODO: save the result of this somewhere and only call it once
   #[allow(unsafe_code)]
-  fn find_device_queue_family_indices(&mut self, device: &vk::PhysicalDevice)
+  fn find_device_queue_family_indices(instance: &Instance,
+                                      surface: &vk::SurfaceKHR,
+                                      device: &vk::PhysicalDevice)
       -> Result<Acceptable<QueueFamilyIndices>>
   {
-    let instance = self.instance.get().unwrap();
-    let surface = self.surface.get().unwrap();
-
     //   We need a queue family that supports graphics drawing commands, and a
     // queue family that supports presentation commands. These may or may not
     // be the same family.
@@ -1235,23 +1215,22 @@ impl Surreality {
   //   We expect our caller to have already verified that the device supports
   // the swapchain extension.
   #[allow(unsafe_code)]
-  fn find_device_swapchain_features(&mut self, device: &vk::PhysicalDevice)
+  fn find_device_swapchain_features(instance: &Instance,
+                                    surface: &vk::SurfaceKHR,
+                                    physical_device: &vk::PhysicalDevice)
       -> Result<Acceptable<SwapchainFeatures>>
   {
-    let instance = self.instance.get().unwrap();
-    let surface = self.surface.get().unwrap();
-
     let capabilities = unsafe {
       instance.get_physical_device_surface_capabilities_khr(
-          *device, *surface)
+          *physical_device, *surface)
     }?;
     let formats = unsafe {
       instance.get_physical_device_surface_formats_khr(
-          *device, *surface)
+          *physical_device, *surface)
     }?;
     let presentation_modes = unsafe {
       instance.get_physical_device_surface_present_modes_khr(
-          *device, *surface)
+          *physical_device, *surface)
     }?;
 
     if formats.is_empty() {
@@ -1265,8 +1244,7 @@ impl Surreality {
     }
   }
 
-  fn pick_surface_format(&mut self,
-                         available_formats: &Vec<vk::SurfaceFormatKHR>)
+  fn pick_surface_format(available_formats: &Vec<vk::SurfaceFormatKHR>)
       -> Result<vk::SurfaceFormatKHR>
   {
     for format in available_formats {
@@ -1280,15 +1258,15 @@ impl Surreality {
     return Ok(available_formats[0].clone());
   }
 
-  fn pick_presentation_mode(&mut self,
-                            _available_modes: &Vec<vk::PresentModeKHR>)
+  fn pick_presentation_mode(_available_modes: &Vec<vk::PresentModeKHR>)
       -> Result<vk::PresentModeKHR>
   {
     // It's guaranteed to have this one.
     return Ok(vk::PresentModeKHR::FIFO);
   }
 
-  fn pick_image_extent(&mut self, capabilities: vk::SurfaceCapabilitiesKHR)
+  fn pick_image_extent(window: &Window,
+                       capabilities: vk::SurfaceCapabilitiesKHR)
       -> Result<vk::Extent2D>
   {
     if capabilities.current_extent.width != u32::MAX
@@ -1296,7 +1274,6 @@ impl Surreality {
     {
       Ok(capabilities.current_extent)
     } else {
-      let window = self.window.get().unwrap();
       let window_size = window.inner_size();
 
       let width = window_size.width
@@ -1311,11 +1288,9 @@ impl Surreality {
   }
 
   #[allow(unsafe_code)]
-  fn load_spirv_shader_module(&mut self, binary: &[u8])
+  fn load_spirv_shader_module(device: &Device, binary: &[u8])
       -> Result<vk::ShaderModule>
   {
-    let device = self.device.get().unwrap();
-
     let bytecode = Bytecode::new(binary)?;
 
     let module_info = vk::ShaderModuleCreateInfo::builder()
@@ -1331,42 +1306,51 @@ impl Surreality {
 
   #[allow(unsafe_code)]
   fn render(&mut self, window_id: WindowId) -> Result<()> {
-    if let Some(window) = self.window.get()
+    if let Some(window) = self.window.borrow().as_ref()
        && window_id == window.id()
     {
-      let device = self.device.get().unwrap();
-      let graphics_queue = self.graphics_queue.get().unwrap();
-      let presentation_queue = self.presentation_queue.get().unwrap();
-      let swapchain = self.swapchain.get().unwrap();
-      let command_buffers = self.command_buffers.get().unwrap();
+      let device = self.device.borrow();
+      let device = device.as_ref().unwrap();
+      let graphics_queue = self.graphics_queue.borrow();
+      let graphics_queue = graphics_queue.as_ref().unwrap();
+      let presentation_queue = self.presentation_queue.borrow();
+      let presentation_queue = presentation_queue.as_ref().unwrap();
+      let swapchain = self.swapchain.borrow();
+      let swapchain = swapchain.as_ref().unwrap().swapchain;
+      let command_buffers = self.command_buffers.borrow();
       let frame_index = self.frame_index;
       let image_available_semaphore
-              = self.image_available_semaphores.get().unwrap()[frame_index];
+              = self.concurrency.borrow().as_ref().unwrap()
+                    .image_available_semaphores[frame_index];
       let rendering_finished_semaphore
-              = self.rendering_finished_semaphores.get()
-                    .unwrap()[frame_index];
-      let frame_fence = self.frame_fences.get().unwrap()[frame_index];
+              = self.concurrency.borrow().as_ref().unwrap()
+                    .rendering_finished_semaphores[frame_index];
+      let frame_fence = self.concurrency.borrow().as_ref().unwrap()
+                            .frame_fences[frame_index];
 
       unsafe { device.wait_for_fences(&[frame_fence], true, u64::MAX) }?;
 
       let image_index = unsafe {
-        device.acquire_next_image_khr(*swapchain, u64::MAX,
+        device.acquire_next_image_khr(swapchain, u64::MAX,
                                       image_available_semaphore,
                                       vk::Fence::null())
       }?.0 as usize;
 
-      let image_fence = self.image_fences[image_index];
+      let image_fence = self.concurrency.borrow().as_ref().unwrap()
+                            .image_fences.borrow()[image_index];
       if !image_fence.is_null() {
         unsafe { device.wait_for_fences(&[image_fence], true, u64::MAX) }?;
       }
 
-      self.image_fences[image_index] = frame_fence;
+      if let Some(concurrency) = self.concurrency.borrow().as_ref() {
+        concurrency.image_fences.borrow_mut()[image_index] = frame_fence;
+      }
 
       let first_semaphores = [image_available_semaphore];
       let second_semaphores = [rendering_finished_semaphore];
 
       let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
-      let command_buffers = [command_buffers[image_index]];
+      let command_buffers = [command_buffers.as_ref().unwrap()[image_index]];
       let submit_info = vk::SubmitInfo::builder()
                             .wait_semaphores(&first_semaphores)
                             .wait_dst_stage_mask(&wait_stages)
@@ -1379,7 +1363,7 @@ impl Surreality {
                             frame_fence)
       }?;
 
-      let swapchains = [*swapchain];
+      let swapchains = [swapchain];
       let image_indices = [image_index as u32];
       let present_info = vk::PresentInfoKHR::builder()
                              .wait_semaphores(&second_semaphores)
@@ -1400,89 +1384,75 @@ impl Surreality {
 impl Drop for Surreality {
   #[allow(unsafe_code)]
   fn drop(&mut self) {
-    if let Some(device) = self.device.get() {
+    if let Some(device) = self.device.borrow().as_ref() {
       unsafe { device.device_wait_idle() }.unwrap();
     }
 
-    if let Some(image_available_semaphores)
-               = self.image_available_semaphores.get()
-       && let Some(device) = self.device.get()
+    if let Some(concurrency) = self.concurrency.replace(None)
+       && let Some(device) = self.device.borrow().as_ref()
     {
-      for semaphore in image_available_semaphores {
-        unsafe { device.destroy_semaphore(*semaphore, None) };
+      for semaphore in concurrency.image_available_semaphores {
+        unsafe { device.destroy_semaphore(semaphore, None) };
       }
-    }
 
-    if let Some(rendering_finished_semaphores)
-               = self.rendering_finished_semaphores.get()
-       && let Some(device) = self.device.get()
-    {
-      for semaphore in rendering_finished_semaphores {
-        unsafe { device.destroy_semaphore(*semaphore, None) };
+      for semaphore in concurrency.rendering_finished_semaphores {
+        unsafe { device.destroy_semaphore(semaphore, None) };
       }
-    }
 
-    if let Some(frame_fences) = self.frame_fences.get()
-       && let Some(device) = self.device.get()
-    {
-      for fence in frame_fences {
-        unsafe { device.destroy_fence(*fence, None) };
+      for fence in concurrency.frame_fences {
+        unsafe { device.destroy_fence(fence, None) };
       }
     }
 
-    if let Some(command_pool) = self.command_pool.get()
-       && let Some(device) = self.device.get()
+    if let Some(command_pool) = self.command_pool.replace(None)
+       && let Some(device) = self.device.borrow().as_ref()
     {
-      unsafe { device.destroy_command_pool(*command_pool, None) };
+      unsafe { device.destroy_command_pool(command_pool, None) };
     }
 
-    if let Some(framebuffers) = self.framebuffers.get()
-       && let Some(device) = self.device.get()
+    if let Some(framebuffers) = self.framebuffers.replace(None)
+       && let Some(device) = self.device.borrow().as_ref()
     {
       for framebuffer in framebuffers {
-        unsafe { device.destroy_framebuffer(*framebuffer, None) };
+        unsafe { device.destroy_framebuffer(framebuffer, None) };
       }
     }
 
-    if let Some(pipeline) = self.pipeline.get()
-       && let Some(device) = self.device.get()
+    if let Some(pipeline) = self.pipeline.replace(None)
+       && let Some(device) = self.device.borrow().as_ref()
     {
-      unsafe { device.destroy_pipeline(*pipeline, None) };
+      unsafe { device.destroy_pipeline(pipeline, None) };
     }
 
-    if let Some(render_pass) = self.render_pass.get()
-       && let Some(device) = self.device.get()
+    if let Some(render_pass) = self.render_pass.replace(None)
+       && let Some(device) = self.device.borrow().as_ref()
     {
-      unsafe { device.destroy_render_pass(*render_pass, None) };
+      unsafe { device.destroy_render_pass(render_pass, None) };
     }
 
-    if let Some(pipeline_layout) = self.pipeline_layout.get()
-       && let Some(device) = self.device.get()
+    if let Some(pipeline_layout) = self.pipeline_layout.replace(None)
+       && let Some(device) = self.device.borrow().as_ref()
     {
-      unsafe { device.destroy_pipeline_layout(*pipeline_layout, None) };
+      unsafe { device.destroy_pipeline_layout(pipeline_layout, None) };
     }
 
-    if let Some(image_views) = self.swapchain_image_views.get()
-       && let Some(device) = self.device.get()
+    if let Some(swapchain) = self.swapchain.replace(None)
+       && let Some(device) = self.device.borrow().as_ref()
     {
-      for view in image_views {
-        unsafe { device.destroy_image_view(*view, None) };
+      for view in swapchain.image_views {
+        unsafe { device.destroy_image_view(view, None) };
       }
-    }
 
-    if let Some(swapchain) = self.swapchain.get()
-       && let Some(device) = self.device.get()
-    {
-      unsafe { device.destroy_swapchain_khr(*swapchain, None) };
+      unsafe { device.destroy_swapchain_khr(swapchain.swapchain, None) };
     }
 
-    if let Some(surface) = self.surface.get()
-       && let Some(instance) = self.instance.get()
+    if let Some(surface) = self.surface.replace(None)
+       && let Some(instance) = self.instance.borrow().as_ref()
     {
-      unsafe { instance.destroy_surface_khr(*surface, None) };
+      unsafe { instance.destroy_surface_khr(surface, None) };
     }
 
-    if let Some(device) = self.device.get() {
+    if let Some(device) = self.device.replace(None) {
       unsafe { device.destroy_device(None) };
     }
 
@@ -1491,15 +1461,15 @@ impl Drop for Surreality {
     // messages while destroying the instance itself only applies to the
     // instance and the messager, so if we were to destroy anything we
     // shouldn't after this point, we'd miss out on diagnostics.
-    if let Some(debug_messager) = self.debug_messager.get()
-       && let Some(instance) = self.instance.get()
+    if let Some(debug_messager) = self.debug_messager.replace(None)
+       && let Some(instance) = self.instance.borrow().as_ref()
     {
       unsafe {
-        instance.destroy_debug_utils_messenger_ext(*debug_messager, None);
+        instance.destroy_debug_utils_messenger_ext(debug_messager, None);
       }
     }
 
-    if let Some(instance) = self.instance.get() {
+    if let Some(instance) = self.instance.replace(None) {
       unsafe { instance.destroy_instance(None) };
     }
   }