summary refs log tree commit diff
path: root/src/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics')
-rw-r--r--src/graphics/mod.rs2
-rw-r--r--src/graphics/render_state.rs574
-rw-r--r--src/graphics/scene.rs2
-rw-r--r--src/graphics/util.rs173
-rw-r--r--src/graphics/window_dressing.rs761
5 files changed, 770 insertions, 742 deletions
diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs
index f164588..7eca16a 100644
--- a/src/graphics/mod.rs
+++ b/src/graphics/mod.rs
@@ -1,5 +1,7 @@
 #![deny(unsafe_code)]
 
 pub mod permanent;
+pub mod render_state;
 pub mod scene;
+pub mod util;
 pub mod window_dressing;
diff --git a/src/graphics/render_state.rs b/src/graphics/render_state.rs
new file mode 100644
index 0000000..d8a607c
--- /dev/null
+++ b/src/graphics/render_state.rs
@@ -0,0 +1,574 @@
+#![deny(unsafe_code)]
+use crate::error::*;
+use crate::graphics::permanent::{
+  PermanentGraphicsState, GraphicsStateForReinit
+};
+use crate::graphics::util::{
+  allocate_buffer, copy_buffer, stage_in_buffer
+};
+use crate::model_loader::load_model;
+use crate::graphics::window_dressing::WindowDressing;
+use crate::shader_data::{ Vertex, UniformBlock, VertexPushBlock };
+
+use std::mem::size_of;
+
+use vulkanalia::{ Device, Instance };
+use vulkanalia::vk::{ self, Handle, HasBuilder, DeviceV1_0 };
+
+
+//   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>,
+}
+
+
+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_render_pass(device: &Device, sample_count: vk::SampleCountFlags,
+                    color_format: &vk::Format, depth_format: &vk::Format)
+    -> Result<vk::RenderPass>
+{
+  let color_attachment = vk::AttachmentDescription::builder()
+          .format(*color_format)
+          .samples(sample_count)
+          .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::COLOR_ATTACHMENT_OPTIMAL);
+
+  let color_attachment_reference = vk::AttachmentReference::builder()
+          .attachment(0)
+          .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
+
+  let depth_attachment = vk::AttachmentDescription::builder()
+          .format(*depth_format)
+          .samples(sample_count)
+          .load_op(vk::AttachmentLoadOp::CLEAR)
+          .store_op(vk::AttachmentStoreOp::DONT_CARE)
+          .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
+          .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
+          .initial_layout(vk::ImageLayout::UNDEFINED)
+          .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+
+  let depth_attachment_reference = vk::AttachmentReference::builder()
+          .attachment(1)
+          .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+
+  let color_resolve_attachment = vk::AttachmentDescription::builder()
+          .format(*color_format)
+          .samples(vk::SampleCountFlags::_1)
+          .load_op(vk::AttachmentLoadOp::DONT_CARE)
+          .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_resolve_attachment_reference = vk::AttachmentReference::builder()
+          .attachment(2)
+          .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
+
+  let color_attachments = [color_attachment_reference];
+  let resolve_attachments = [color_resolve_attachment_reference];
+  let subpass = vk::SubpassDescription::builder()
+          .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
+          .color_attachments(&color_attachments)
+          .depth_stencil_attachment(&depth_attachment_reference)
+          .resolve_attachments(&resolve_attachments);
+
+  let dependency = vk::SubpassDependency::builder()
+          .src_subpass(vk::SUBPASS_EXTERNAL)
+          .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
+                          | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS)
+          .src_access_mask(vk::AccessFlags::empty())
+          .dst_subpass(0)
+          .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
+                          | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS)
+          .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE
+                           | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE);
+
+  let render_attachments = [color_attachment,
+                            depth_attachment,
+                            color_resolve_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(render_pass)
+}
+
+
+#[allow(unsafe_code)]
+fn init_pipeline(device: &Device,
+                 descriptor_set_layout: &vk::DescriptorSetLayout,
+                 extent: &vk::Extent2D, sample_count: vk::SampleCountFlags,
+                 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 = PermanentGraphicsState::load_spirv_shader_module(
+          device, vertex_binary)?;
+  let fragment_module = PermanentGraphicsState::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 binding_descriptions = [Vertex::<f32>::binding_description()];
+  let attribute_descriptions = Vertex::<f32>::attribute_descriptions();
+  let vertex_input_state_info
+          = vk::PipelineVertexInputStateCreateInfo::builder()
+                .vertex_binding_descriptions(&binding_descriptions)
+                .vertex_attribute_descriptions(&attribute_descriptions);
+
+  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(sample_count);
+
+  let depth_state_info = vk::PipelineDepthStencilStateCreateInfo::builder()
+          .depth_test_enable(true)
+          .depth_write_enable(true)
+          .depth_compare_op(vk::CompareOp::LESS)
+          .depth_bounds_test_enable(false)
+          .min_depth_bounds(0.0)
+          .max_depth_bounds(1.0)
+          .stencil_test_enable(false);
+
+  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 vertex_push_constant_range = vk::PushConstantRange::builder()
+          .stage_flags(vk::ShaderStageFlags::VERTEX)
+          .offset(0)
+          .size(size_of::<VertexPushBlock<f32>>() as u32);
+
+  let layouts = [*descriptor_set_layout];
+  let push_constant_ranges = [vertex_push_constant_range];
+  let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder()
+                                 .set_layouts(&layouts)
+                                 .push_constant_ranges(&push_constant_ranges);
+
+  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)
+          .depth_stencil_state(&depth_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((pipeline_layout, pipeline))
+}
+
+
+#[allow(unsafe_code)]
+fn init_framebuffers(device: &Device, extent: &vk::Extent2D,
+                     swapchain_image_views: &Vec<vk::ImageView>,
+                     color_image_view: &vk::ImageView,
+                     depth_image_view: &vk::ImageView,
+                     render_pass: &vk::RenderPass)
+    -> Result<Vec<vk::Framebuffer>>
+{
+  let mut framebuffers = Vec::new();
+
+  for color_resolve_image_view in swapchain_image_views {
+    let attachments = [*color_image_view,
+                       *depth_image_view,
+                       *color_resolve_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);
+  }
+
+  Ok(framebuffers)
+}
+
+
+#[allow(unsafe_code)]
+fn init_command_buffers(device: &Device,
+                        framebuffers: &Vec<vk::Framebuffer>,
+                        command_pool: &vk::CommandPool)
+    -> Result<Vec<vk::CommandBuffer>>
+{
+  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)
+  }?;
+
+  Ok(command_buffers)
+}
+
+
+fn init_vertex_buffer(vertices: Vec<Vertex<f32>>, instance: &Instance,
+                      physical_device: &vk::PhysicalDevice, device: &Device,
+                      queue: &vk::Queue, command_pool: &vk::CommandPool)
+    -> Result<(vk::Buffer, vk::DeviceMemory)>
+{
+  init_buffer(instance, physical_device, device, queue, command_pool,
+              vk::BufferUsageFlags::VERTEX_BUFFER, &vertices)
+}
+
+
+fn init_index_buffer(indices: Vec<u32>, instance: &Instance,
+                     physical_device: &vk::PhysicalDevice, device: &Device,
+                     queue: &vk::Queue, command_pool: &vk::CommandPool)
+    -> Result<(vk::Buffer, vk::DeviceMemory)>
+{
+  init_buffer(instance, physical_device, device, queue, command_pool,
+              vk::BufferUsageFlags::INDEX_BUFFER, &indices)
+}
+
+
+#[allow(unsafe_code)]
+fn init_buffer<T>(instance: &Instance,
+                  physical_device: &vk::PhysicalDevice, device: &Device,
+                  queue: &vk::Queue, command_pool: &vk::CommandPool,
+                  usage: vk::BufferUsageFlags, contents: &[T])
+    -> Result<(vk::Buffer, vk::DeviceMemory)>
+{
+  let (staging_buffer, staging_memory, size)
+          = stage_in_buffer(instance, physical_device, device, contents)?;
+
+  let final_usage = vk::BufferUsageFlags::TRANSFER_DST | usage;
+  let final_memory_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL;
+  let (final_buffer, device_memory)
+          = allocate_buffer(instance, physical_device, device,
+                            size as vk::DeviceSize, final_usage,
+                            final_memory_flags)?;
+
+  copy_buffer(device, queue, command_pool, &staging_buffer, &final_buffer,
+              size as vk::DeviceSize)?;
+
+  unsafe { device.destroy_buffer(staging_buffer, None) };
+  unsafe { device.free_memory(staging_memory, None) };
+
+  Ok((final_buffer, device_memory))
+}
+
+
+#[allow(unsafe_code)]
+fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout,
+                        buffers: &Vec<vk::Buffer>, pool: &vk::DescriptorPool,
+                        count: usize, texture_image_view: &vk::ImageView,
+                        sampler: &vk::Sampler)
+    -> Result<Vec<vk::DescriptorSet>>
+{
+  let layouts = vec![*layout; count];
+  let set_info = vk::DescriptorSetAllocateInfo::builder()
+          .descriptor_pool(*pool)
+          .set_layouts(&layouts);
+  let sets = unsafe { device.allocate_descriptor_sets(&set_info) }?;
+
+  for index in 0 .. count {
+    let buffer_info = vk::DescriptorBufferInfo::builder()
+            .buffer(buffers[index])
+            .offset(0)
+            .range(size_of::<UniformBlock<f32>>() as vk::DeviceSize);
+
+    let buffer_info_list = [buffer_info];
+    let uniform_block_write_info = vk::WriteDescriptorSet::builder()
+            .dst_set(sets[index])
+            .dst_binding(0)
+            .dst_array_element(0)
+            .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
+            .buffer_info(&buffer_info_list);
+
+    let image_info = vk::DescriptorImageInfo::builder()
+            .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
+            .image_view(*texture_image_view)
+            .sampler(*sampler);
+    let image_info_list = [image_info];
+    let sampler_write_info = vk::WriteDescriptorSet::builder()
+            .dst_set(sets[index])
+            .dst_binding(1)
+            .dst_array_element(0)
+            .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
+            .image_info(&image_info_list);
+
+    let write_info_list = [uniform_block_write_info, sampler_write_info];
+    let copy_info_list: [vk::CopyDescriptorSet; 0] = [];
+
+    unsafe {
+      device.update_descriptor_sets(&write_info_list, &copy_info_list)
+    };
+  }
+
+  Ok(sets)
+}
diff --git a/src/graphics/scene.rs b/src/graphics/scene.rs
index e59ae89..b5cf7bd 100644
--- a/src/graphics/scene.rs
+++ b/src/graphics/scene.rs
@@ -1,6 +1,6 @@
 #![deny(unsafe_code)]
 use crate::error::*;
-use crate::graphics::window_dressing::RenderState;
+use crate::graphics::render_state::RenderState;
 use crate::linear_algebra::{ Vec3, Vec4, Transformation };
 use crate::shader_data::VertexPushBlock;
 
diff --git a/src/graphics/util.rs b/src/graphics/util.rs
new file mode 100644
index 0000000..2debc4e
--- /dev/null
+++ b/src/graphics/util.rs
@@ -0,0 +1,173 @@
+#![allow(unsafe_code)]
+use crate::error::*;
+
+use std::mem::size_of;
+use std::ptr::copy_nonoverlapping;
+
+use vulkanalia::{ Device, Instance };
+use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0 };
+
+
+pub fn stage_in_buffer<T>(instance: &Instance,
+                          physical_device: &vk::PhysicalDevice,
+                          device: &Device, contents: &[T])
+    -> Result<(vk::Buffer, vk::DeviceMemory, usize)>
+{
+  let size = size_of::<T>() * contents.len();
+
+  let staging_usage = vk::BufferUsageFlags::TRANSFER_SRC;
+  let staging_memory_flags = vk::MemoryPropertyFlags::HOST_COHERENT
+                             | vk::MemoryPropertyFlags::HOST_VISIBLE;
+  let (staging_buffer, staging_memory)
+          = allocate_buffer(instance, physical_device, device,
+                            size as vk::DeviceSize, staging_usage,
+                            staging_memory_flags)?;
+
+  let host_memory = unsafe {
+    device.map_memory(staging_memory, 0, size as vk::DeviceSize,
+                      vk::MemoryMapFlags::empty())
+  }?;
+
+  unsafe {
+    copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len())
+  };
+
+  unsafe { device.unmap_memory(staging_memory) };
+
+  Ok((staging_buffer, staging_memory, size))
+}
+
+
+#[allow(unsafe_code)]
+pub fn allocate_buffer(instance: &Instance,
+                       physical_device: &vk::PhysicalDevice, device: &Device,
+                       size: vk::DeviceSize, usage: vk::BufferUsageFlags,
+                       memory_flags: vk::MemoryPropertyFlags)
+    -> Result<(vk::Buffer, vk::DeviceMemory)>
+{
+  let buffer_info = vk::BufferCreateInfo::builder()
+                        .size(size)
+                        .usage(usage)
+                        .sharing_mode(vk::SharingMode::EXCLUSIVE);
+
+  let buffer = unsafe { device.create_buffer(&buffer_info, None) }?;
+
+  //   The requirements are mostly what you'd think: size and alignment. The
+  // bits field is something special; see pick_memory_type() for the
+  // explanation. Despite the simplicity of this data, Vulkan wants to be the
+  // one to tell us about it, and we let it.
+  let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
+
+  let type_index = pick_memory_type(instance, physical_device,
+                                    &memory_flags, &requirements)?;
+
+  let memory_info = vk::MemoryAllocateInfo::builder()
+          .allocation_size(requirements.size)
+          .memory_type_index(type_index);
+
+  let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?;
+
+  unsafe { device.bind_buffer_memory(buffer, device_memory, 0) }?;
+
+  Ok((buffer, device_memory))
+}
+
+
+#[allow(unsafe_code)]
+pub fn copy_buffer(device: &Device, queue: &vk::Queue,
+                   command_pool: &vk::CommandPool, source: &vk::Buffer,
+                   destination: &vk::Buffer, size: vk::DeviceSize)
+    -> Result<()>
+{
+  let command_buffer = begin_transient_commands(device, command_pool)?;
+
+  let copy_info = vk::BufferCopy::builder().size(size);
+  unsafe {
+    device.cmd_copy_buffer(command_buffer, *source, *destination,
+                           &[copy_info])
+  };
+
+  end_transient_commands(command_buffer, device, queue, command_pool)?;
+
+  Ok(())
+}
+
+
+#[allow(unsafe_code)]
+pub fn pick_memory_type(instance: &Instance,
+                        physical_device: &vk::PhysicalDevice,
+                        properties: &vk::MemoryPropertyFlags,
+                        requirements: &vk::MemoryRequirements)
+    -> Result<u32>
+{
+  let memory_map = unsafe {
+    instance.get_physical_device_memory_properties(*physical_device)
+  };
+
+  //   So. The memory_type_bits field is a map of which indices are suitable,
+  // based on the buffer our caller passed to
+  // get_buffer_memory_requirements(). Yes, that means there's a hard cap on
+  // how many memory types there can be, based on the size of the bitfield.
+  for index in 0 .. memory_map.memory_type_count {
+    if requirements.memory_type_bits & (1 << index) == 0 {
+      continue;
+    }
+
+    let memory_type = memory_map.memory_types[index as usize];
+
+    if memory_type.property_flags.contains(*properties) {
+      return Ok(index);
+    }
+  }
+
+  Err(Error {
+    message: "The system has no suitable memory for a buffer.".to_string()
+  })
+}
+
+
+#[allow(unsafe_code)]
+pub fn begin_transient_commands(device: &Device,
+                                command_pool: &vk::CommandPool)
+    -> Result<vk::CommandBuffer>
+{
+  let command_buffer_allocation_info
+          = vk::CommandBufferAllocateInfo::builder()
+                .command_pool(*command_pool)
+                .level(vk::CommandBufferLevel::PRIMARY)
+                .command_buffer_count(1);
+  let command_buffer = unsafe {
+    device.allocate_command_buffers(&command_buffer_allocation_info)
+  }?[0];
+
+  let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder()
+          .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
+
+  unsafe {
+    device.begin_command_buffer(command_buffer, &command_buffer_begin_info)
+  }?;
+
+  Ok(command_buffer)
+}
+
+
+#[allow(unsafe_code)]
+pub fn end_transient_commands(command_buffer: vk::CommandBuffer,
+                              device: &Device, queue: &vk::Queue,
+                              command_pool: &vk::CommandPool)
+    -> Result<()>
+{
+  unsafe { device.end_command_buffer(command_buffer) }?;
+
+  let command_buffers = [command_buffer];
+  let submit_info = vk::SubmitInfo::builder()
+          .command_buffers(&command_buffers);
+  unsafe { device.queue_submit(*queue, &[submit_info], vk::Fence::null()) }?;
+
+  unsafe { device.queue_wait_idle(*queue) }?;
+
+  unsafe { device.free_command_buffers(*command_pool, &command_buffers) };
+
+  Ok(())
+}
+
diff --git a/src/graphics/window_dressing.rs b/src/graphics/window_dressing.rs
index dc7f47f..065bd53 100644
--- a/src/graphics/window_dressing.rs
+++ b/src/graphics/window_dressing.rs
@@ -4,13 +4,16 @@ use crate::graphics::permanent::{
   PermanentGraphicsState, GraphicsStateForReinit, QueueFamilyIndices,
   EnableAnisotropy
 };
-use crate::model_loader::load_model;
-use crate::shader_data::{ Vertex, UniformBlock, VertexPushBlock };
+use crate::graphics::util::{
+  allocate_buffer, stage_in_buffer,
+  pick_memory_type,
+  begin_transient_commands, end_transient_commands
+};
+use crate::shader_data::UniformBlock;
 
 use std::collections::BTreeSet;
 use std::io::Cursor;
 use std::mem::size_of;
-use std::ptr::copy_nonoverlapping;
 
 use png::Decoder;
 use vulkanalia::{ Device, Instance };
@@ -36,51 +39,29 @@ pub struct WindowDressing {
 
   color_image: vk::Image,
   color_image_memory: vk::DeviceMemory,
-  color_image_view: vk::ImageView,
+  pub color_image_view: vk::ImageView,
 
   depth_image: vk::Image,
   depth_image_memory: vk::DeviceMemory,
-  depth_image_view: vk::ImageView,
-  depth_format: vk::Format,
+  pub depth_image_view: vk::ImageView,
+  pub depth_format: vk::Format,
 
-  primary_command_pool: vk::CommandPool,
-  transient_command_pool: vk::CommandPool,
+  pub primary_command_pool: vk::CommandPool,
+  pub transient_command_pool: vk::CommandPool,
 
   texture_image: vk::Image,
   texture_image_memory: vk::DeviceMemory,
-  texture_image_view: vk::ImageView,
-  mip_count: u32,
-  sampler: vk::Sampler,
+  pub texture_image_view: vk::ImageView,
+  pub sampler: vk::Sampler,
 
-  uniform_buffers: Vec<vk::Buffer>,
+  pub uniform_buffers: Vec<vk::Buffer>,
   pub uniform_buffer_memory: Vec<vk::DeviceMemory>,
 
-  descriptor_pool: vk::DescriptorPool,
+  pub descriptor_pool: vk::DescriptorPool,
 
   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
@@ -89,9 +70,9 @@ pub struct RenderState {
 #[derive(Debug)]
 pub struct Swapchain {
   pub swapchain: vk::SwapchainKHR,
-  images: Vec<vk::Image>,
-  image_views: Vec<vk::ImageView>,
-  format: vk::Format,
+  pub images: Vec<vk::Image>,
+  pub image_views: Vec<vk::ImageView>,
+  pub format: vk::Format,
   pub extent: vk::Extent2D,
 }
 
@@ -173,7 +154,6 @@ impl WindowDressing {
       texture_image,
       texture_image_memory,
       texture_image_view,
-      mip_count,
       sampler,
       uniform_buffers,
       uniform_buffer_memory,
@@ -197,7 +177,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;
 
     unsafe { device.device_wait_idle() }.unwrap();
 
@@ -303,167 +282,6 @@ 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,
@@ -588,283 +406,6 @@ fn init_depth(instance: &Instance, physical_device: &vk::PhysicalDevice,
 
 
 #[allow(unsafe_code)]
-fn init_render_pass(device: &Device, sample_count: vk::SampleCountFlags,
-                    color_format: &vk::Format, depth_format: &vk::Format)
-    -> Result<vk::RenderPass>
-{
-  let color_attachment = vk::AttachmentDescription::builder()
-          .format(*color_format)
-          .samples(sample_count)
-          .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::COLOR_ATTACHMENT_OPTIMAL);
-
-  let color_attachment_reference = vk::AttachmentReference::builder()
-          .attachment(0)
-          .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
-
-  let depth_attachment = vk::AttachmentDescription::builder()
-          .format(*depth_format)
-          .samples(sample_count)
-          .load_op(vk::AttachmentLoadOp::CLEAR)
-          .store_op(vk::AttachmentStoreOp::DONT_CARE)
-          .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
-          .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
-          .initial_layout(vk::ImageLayout::UNDEFINED)
-          .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
-
-  let depth_attachment_reference = vk::AttachmentReference::builder()
-          .attachment(1)
-          .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
-
-  let color_resolve_attachment = vk::AttachmentDescription::builder()
-          .format(*color_format)
-          .samples(vk::SampleCountFlags::_1)
-          .load_op(vk::AttachmentLoadOp::DONT_CARE)
-          .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_resolve_attachment_reference = vk::AttachmentReference::builder()
-          .attachment(2)
-          .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
-
-  let color_attachments = [color_attachment_reference];
-  let resolve_attachments = [color_resolve_attachment_reference];
-  let subpass = vk::SubpassDescription::builder()
-          .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
-          .color_attachments(&color_attachments)
-          .depth_stencil_attachment(&depth_attachment_reference)
-          .resolve_attachments(&resolve_attachments);
-
-  let dependency = vk::SubpassDependency::builder()
-          .src_subpass(vk::SUBPASS_EXTERNAL)
-          .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
-                          | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS)
-          .src_access_mask(vk::AccessFlags::empty())
-          .dst_subpass(0)
-          .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
-                          | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS)
-          .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE
-                           | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE);
-
-  let render_attachments = [color_attachment,
-                            depth_attachment,
-                            color_resolve_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(render_pass)
-}
-
-
-#[allow(unsafe_code)]
-fn init_pipeline(device: &Device,
-                 descriptor_set_layout: &vk::DescriptorSetLayout,
-                 extent: &vk::Extent2D, sample_count: vk::SampleCountFlags,
-                 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 = PermanentGraphicsState::load_spirv_shader_module(
-          device, vertex_binary)?;
-  let fragment_module = PermanentGraphicsState::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 binding_descriptions = [Vertex::<f32>::binding_description()];
-  let attribute_descriptions = Vertex::<f32>::attribute_descriptions();
-  let vertex_input_state_info
-          = vk::PipelineVertexInputStateCreateInfo::builder()
-                .vertex_binding_descriptions(&binding_descriptions)
-                .vertex_attribute_descriptions(&attribute_descriptions);
-
-  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(sample_count);
-
-  let depth_state_info = vk::PipelineDepthStencilStateCreateInfo::builder()
-          .depth_test_enable(true)
-          .depth_write_enable(true)
-          .depth_compare_op(vk::CompareOp::LESS)
-          .depth_bounds_test_enable(false)
-          .min_depth_bounds(0.0)
-          .max_depth_bounds(1.0)
-          .stencil_test_enable(false);
-
-  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 vertex_push_constant_range = vk::PushConstantRange::builder()
-          .stage_flags(vk::ShaderStageFlags::VERTEX)
-          .offset(0)
-          .size(size_of::<VertexPushBlock<f32>>() as u32);
-
-  let layouts = [*descriptor_set_layout];
-  let push_constant_ranges = [vertex_push_constant_range];
-  let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder()
-                                 .set_layouts(&layouts)
-                                 .push_constant_ranges(&push_constant_ranges);
-
-  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)
-          .depth_stencil_state(&depth_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((pipeline_layout, pipeline))
-}
-
-
-#[allow(unsafe_code)]
-fn init_framebuffers(device: &Device, extent: &vk::Extent2D,
-                     swapchain_image_views: &Vec<vk::ImageView>,
-                     color_image_view: &vk::ImageView,
-                     depth_image_view: &vk::ImageView,
-                     render_pass: &vk::RenderPass)
-    -> Result<Vec<vk::Framebuffer>>
-{
-  let mut framebuffers = Vec::new();
-
-  for color_resolve_image_view in swapchain_image_views {
-    let attachments = [*color_image_view,
-                       *depth_image_view,
-                       *color_resolve_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);
-  }
-
-  Ok(framebuffers)
-}
-
-
-fn init_vertex_buffer(vertices: Vec<Vertex<f32>>, instance: &Instance,
-                      physical_device: &vk::PhysicalDevice, device: &Device,
-                      queue: &vk::Queue, command_pool: &vk::CommandPool)
-    -> Result<(vk::Buffer, vk::DeviceMemory)>
-{
-  init_buffer(instance, physical_device, device, queue, command_pool,
-              vk::BufferUsageFlags::VERTEX_BUFFER, &vertices)
-}
-
-
-fn init_index_buffer(indices: Vec<u32>, instance: &Instance,
-                     physical_device: &vk::PhysicalDevice, device: &Device,
-                     queue: &vk::Queue, command_pool: &vk::CommandPool)
-    -> Result<(vk::Buffer, vk::DeviceMemory)>
-{
-  init_buffer(instance, physical_device, device, queue, command_pool,
-              vk::BufferUsageFlags::INDEX_BUFFER, &indices)
-}
-
-
-#[allow(unsafe_code)]
 fn init_texture(instance: &Instance,
                 physical_device: &vk::PhysicalDevice, device: &Device,
                 queue: &vk::Queue, command_pool: &vk::CommandPool)
@@ -909,7 +450,6 @@ fn init_texture(instance: &Instance,
                            vk::MemoryPropertyFlags::DEVICE_LOCAL)?;
 
   change_image_layout(device, queue, command_pool, &image, mip_count,
-                      vk::Format::R8G8B8A8_SRGB,
                       vk::ImageLayout::UNDEFINED,
                       vk::ImageLayout::TRANSFER_DST_OPTIMAL)?;
 
@@ -956,33 +496,6 @@ fn init_uniform_buffers(instance: &Instance,
 
 
 #[allow(unsafe_code)]
-fn init_buffer<T>(instance: &Instance,
-                  physical_device: &vk::PhysicalDevice, device: &Device,
-                  queue: &vk::Queue, command_pool: &vk::CommandPool,
-                  usage: vk::BufferUsageFlags, contents: &[T])
-    -> Result<(vk::Buffer, vk::DeviceMemory)>
-{
-  let (staging_buffer, staging_memory, size)
-          = stage_in_buffer(instance, physical_device, device, contents)?;
-
-  let final_usage = vk::BufferUsageFlags::TRANSFER_DST | usage;
-  let final_memory_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL;
-  let (final_buffer, device_memory)
-          = allocate_buffer(instance, physical_device, device,
-                            size as vk::DeviceSize, final_usage,
-                            final_memory_flags)?;
-
-  copy_buffer(device, queue, command_pool, &staging_buffer, &final_buffer,
-              size as vk::DeviceSize)?;
-
-  unsafe { device.destroy_buffer(staging_buffer, None) };
-  unsafe { device.free_memory(staging_memory, None) };
-
-  Ok((final_buffer, device_memory))
-}
-
-
-#[allow(unsafe_code)]
 fn init_sampler(device: &Device, enable_anisotropy: &EnableAnisotropy,
                 mip_count: u32)
     -> Result<vk::Sampler>
@@ -1038,57 +551,6 @@ fn init_descriptor_pool(device: &Device, count: usize)
 
 
 #[allow(unsafe_code)]
-fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout,
-                        buffers: &Vec<vk::Buffer>, pool: &vk::DescriptorPool,
-                        count: usize, texture_image_view: &vk::ImageView,
-                        sampler: &vk::Sampler)
-    -> Result<Vec<vk::DescriptorSet>>
-{
-  let layouts = vec![*layout; count];
-  let set_info = vk::DescriptorSetAllocateInfo::builder()
-          .descriptor_pool(*pool)
-          .set_layouts(&layouts);
-  let sets = unsafe { device.allocate_descriptor_sets(&set_info) }?;
-
-  for index in 0 .. count {
-    let buffer_info = vk::DescriptorBufferInfo::builder()
-            .buffer(buffers[index])
-            .offset(0)
-            .range(size_of::<UniformBlock<f32>>() as vk::DeviceSize);
-
-    let buffer_info_list = [buffer_info];
-    let uniform_block_write_info = vk::WriteDescriptorSet::builder()
-            .dst_set(sets[index])
-            .dst_binding(0)
-            .dst_array_element(0)
-            .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
-            .buffer_info(&buffer_info_list);
-
-    let image_info = vk::DescriptorImageInfo::builder()
-            .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
-            .image_view(*texture_image_view)
-            .sampler(*sampler);
-    let image_info_list = [image_info];
-    let sampler_write_info = vk::WriteDescriptorSet::builder()
-            .dst_set(sets[index])
-            .dst_binding(1)
-            .dst_array_element(0)
-            .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
-            .image_info(&image_info_list);
-
-    let write_info_list = [uniform_block_write_info, sampler_write_info];
-    let copy_info_list: [vk::CopyDescriptorSet; 0] = [];
-
-    unsafe {
-      device.update_descriptor_sets(&write_info_list, &copy_info_list)
-    };
-  }
-
-  Ok(sets)
-}
-
-
-#[allow(unsafe_code)]
 fn init_command_pools(device: &Device, indices: &QueueFamilyIndices)
     -> Result<(vk::CommandPool, vk::CommandPool)>
 {
@@ -1111,25 +573,6 @@ fn init_command_pools(device: &Device, indices: &QueueFamilyIndices)
 
 
 #[allow(unsafe_code)]
-fn init_command_buffers(device: &Device,
-                        framebuffers: &Vec<vk::Framebuffer>,
-                        command_pool: &vk::CommandPool)
-    -> Result<Vec<vk::CommandBuffer>>
-{
-  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)
-  }?;
-
-  Ok(command_buffers)
-}
-
-
-#[allow(unsafe_code)]
 fn init_concurrency(device: &Device,
                     swapchain_images: &Vec<vk::Image>)
     -> Result<Concurrency>
@@ -1278,125 +721,6 @@ fn pick_image_extent(window: &Window,
 
 
 #[allow(unsafe_code)]
-fn pick_memory_type(instance: &Instance,
-                    physical_device: &vk::PhysicalDevice,
-                    properties: &vk::MemoryPropertyFlags,
-                    requirements: &vk::MemoryRequirements)
-    -> Result<u32>
-{
-  let memory_map = unsafe {
-    instance.get_physical_device_memory_properties(*physical_device)
-  };
-
-  //   So. The memory_type_bits field is a map of which indices are suitable,
-  // based on the buffer our caller passed to
-  // get_buffer_memory_requirements(). Yes, that means there's a hard cap on
-  // how many memory types there can be, based on the size of the bitfield.
-  for index in 0 .. memory_map.memory_type_count {
-    if requirements.memory_type_bits & (1 << index) == 0 {
-      continue;
-    }
-
-    let memory_type = memory_map.memory_types[index as usize];
-
-    if memory_type.property_flags.contains(*properties) {
-      return Ok(index);
-    }
-  }
-
-  Err(Error {
-    message: "The system has no suitable memory for a buffer.".to_string()
-  })
-}
-
-
-#[allow(unsafe_code)]
-fn stage_in_buffer<T>(instance: &Instance,
-                      physical_device: &vk::PhysicalDevice, device: &Device,
-                      contents: &[T])
-    -> Result<(vk::Buffer, vk::DeviceMemory, usize)>
-{
-  let size = size_of::<T>() * contents.len();
-
-  let staging_usage = vk::BufferUsageFlags::TRANSFER_SRC;
-  let staging_memory_flags = vk::MemoryPropertyFlags::HOST_COHERENT
-                             | vk::MemoryPropertyFlags::HOST_VISIBLE;
-  let (staging_buffer, staging_memory)
-          = allocate_buffer(instance, physical_device, device,
-                            size as vk::DeviceSize, staging_usage,
-                            staging_memory_flags)?;
-
-  let host_memory = unsafe {
-    device.map_memory(staging_memory, 0, size as vk::DeviceSize,
-                      vk::MemoryMapFlags::empty())
-  }?;
-
-  unsafe {
-    copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len())
-  };
-
-  unsafe { device.unmap_memory(staging_memory) };
-
-  Ok((staging_buffer, staging_memory, size))
-}
-
-
-#[allow(unsafe_code)]
-fn allocate_buffer(instance: &Instance,
-                   physical_device: &vk::PhysicalDevice, device: &Device,
-                   size: vk::DeviceSize, usage: vk::BufferUsageFlags,
-                   memory_flags: vk::MemoryPropertyFlags)
-    -> Result<(vk::Buffer, vk::DeviceMemory)>
-{
-  let buffer_info = vk::BufferCreateInfo::builder()
-                        .size(size)
-                        .usage(usage)
-                        .sharing_mode(vk::SharingMode::EXCLUSIVE);
-
-  let buffer = unsafe { device.create_buffer(&buffer_info, None) }?;
-
-  //   The requirements are mostly what you'd think: size and alignment. The
-  // bits field is something special; see pick_memory_type() for the
-  // explanation. Despite the simplicity of this data, Vulkan wants to be the
-  // one to tell us about it, and we let it.
-  let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
-
-  let type_index = pick_memory_type(instance, physical_device,
-                                    &memory_flags, &requirements)?;
-
-  let memory_info = vk::MemoryAllocateInfo::builder()
-          .allocation_size(requirements.size)
-          .memory_type_index(type_index);
-
-  let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?;
-
-  unsafe { device.bind_buffer_memory(buffer, device_memory, 0) }?;
-
-  Ok((buffer, device_memory))
-}
-
-
-#[allow(unsafe_code)]
-fn copy_buffer(device: &Device, queue: &vk::Queue,
-               command_pool: &vk::CommandPool, source: &vk::Buffer,
-               destination: &vk::Buffer, size: vk::DeviceSize)
-    -> Result<()>
-{
-  let command_buffer = begin_transient_commands(device, command_pool)?;
-
-  let copy_info = vk::BufferCopy::builder().size(size);
-  unsafe {
-    device.cmd_copy_buffer(command_buffer, *source, *destination,
-                           &[copy_info])
-  };
-
-  end_transient_commands(command_buffer, device, queue, command_pool)?;
-
-  Ok(())
-}
-
-
-#[allow(unsafe_code)]
 fn allocate_image(instance: &Instance, physical_device: &vk::PhysicalDevice,
                   device: &Device, width: u32, height: u32, mip_count: u32,
                   sample_count: vk::SampleCountFlags, format: vk::Format,
@@ -1473,8 +797,8 @@ fn copy_buffer_to_image(device: &Device, queue: &vk::Queue,
 #[allow(unsafe_code)]
 fn change_image_layout(device: &Device, queue: &vk::Queue,
                        command_pool: &vk::CommandPool, image: &vk::Image,
-                       mip_count: u32, format: vk::Format,
-                       old: vk::ImageLayout, new: vk::ImageLayout)
+                       mip_count: u32, old: vk::ImageLayout,
+                       new: vk::ImageLayout)
     -> Result<()>
 {
   let command_buffer = begin_transient_commands(device, command_pool)?;
@@ -1691,48 +1015,3 @@ fn fill_mip_levels(device: &Device, queue: &vk::Queue,
   Ok(())
 }
 
-
-#[allow(unsafe_code)]
-fn begin_transient_commands(device: &Device, command_pool: &vk::CommandPool)
-    -> Result<vk::CommandBuffer>
-{
-  let command_buffer_allocation_info
-          = vk::CommandBufferAllocateInfo::builder()
-                .command_pool(*command_pool)
-                .level(vk::CommandBufferLevel::PRIMARY)
-                .command_buffer_count(1);
-  let command_buffer = unsafe {
-    device.allocate_command_buffers(&command_buffer_allocation_info)
-  }?[0];
-
-  let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder()
-          .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
-
-  unsafe {
-    device.begin_command_buffer(command_buffer, &command_buffer_begin_info)
-  }?;
-
-  Ok(command_buffer)
-}
-
-
-#[allow(unsafe_code)]
-fn end_transient_commands(command_buffer: vk::CommandBuffer,
-                          device: &Device, queue: &vk::Queue,
-                          command_pool: &vk::CommandPool)
-    -> Result<()>
-{
-  unsafe { device.end_command_buffer(command_buffer) }?;
-
-  let command_buffers = [command_buffer];
-  let submit_info = vk::SubmitInfo::builder()
-          .command_buffers(&command_buffers);
-  unsafe { device.queue_submit(*queue, &[submit_info], vk::Fence::null()) }?;
-
-  unsafe { device.queue_wait_idle(*queue) }?;
-
-  unsafe { device.free_command_buffers(*command_pool, &command_buffers) };
-
-  Ok(())
-}
-