summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-08-03 19:23:13 -0700
committerIrene Knapp <ireneista@irenes.space>2026-08-03 19:23:13 -0700
commit562693fe2d0afbe9c828fa1acb4473db9752eeed (patch)
tree118248ca1809c8918990b5fd237d4a56de740aa9 /src
parent74ebf3d25e94b87a755d0ad0875332f962c8b92f (diff)
antialiasing
yay

less fuzz

or more fuzz

depending on what you think of as fuzz

Force-Push: yes
Change-Id: I207c040805d97baaa8be887a463a37863159056e
Diffstat (limited to 'src')
-rw-r--r--src/graphics_permanent.rs47
-rw-r--r--src/graphics_window_dressing.rs319
2 files changed, 221 insertions, 145 deletions
diff --git a/src/graphics_permanent.rs b/src/graphics_permanent.rs
index c19bacd..5282e21 100644
--- a/src/graphics_permanent.rs
+++ b/src/graphics_permanent.rs
@@ -123,9 +123,8 @@ impl PermanentGraphicsState {
       vulkanalia::window::create_surface(&instance, &window, &window)
     }?;
 
-    let (physical_device, device,
-         indices, graphics_queue, presentation_queue,
-         enable_anisotropy, enable_swapchain)
+    let (physical_device, device, indices, sample_count, graphics_queue,
+         presentation_queue, enable_anisotropy, enable_swapchain)
         = init_vulkan_device(&instance, &surface,
                              enable_validation, enable_portability)?;
 
@@ -135,7 +134,7 @@ impl PermanentGraphicsState {
       window, entry, instance, debug_messager, surface, device,
       graphics_queue, presentation_queue
     }, GraphicsStateForReinit {
-      physical_device, indices, descriptor_set_layout,
+      physical_device, indices, sample_count, descriptor_set_layout,
     }, enable_anisotropy, enable_swapchain))
   }
 
@@ -218,6 +217,7 @@ impl PermanentGraphicsState {
 pub struct GraphicsStateForReinit {
   pub physical_device: vk::PhysicalDevice,
   pub indices: QueueFamilyIndices,
+  pub sample_count: vk::SampleCountFlags,
   pub descriptor_set_layout: vk::DescriptorSetLayout,
 }
 
@@ -416,10 +416,12 @@ fn init_vulkan(window: &Window)
 fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
                       enable_validation: EnableValidation,
                       enable_portability: EnablePortability)
-    -> Result<(vk::PhysicalDevice, Device, QueueFamilyIndices, vk::Queue,
-               vk::Queue, EnableAnisotropy, EnableSwapchain)>
+    -> Result<(vk::PhysicalDevice, Device, QueueFamilyIndices,
+               vk::SampleCountFlags, vk::Queue, vk::Queue, EnableAnisotropy,
+               EnableSwapchain)>
 {
-  let (physical_device, indices) = pick_vulkan_device(instance, surface)?;
+  let (physical_device, indices, sample_count)
+          = pick_vulkan_device(instance, surface)?;
 
   //   We enumerate the device extensions here so they can inform
   // configuration. We already did this in score_vulkan_device(), but here
@@ -539,9 +541,8 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
     device.get_device_queue(indices.presentation, 0)
   };
 
-  Ok((physical_device, device,
-      indices, graphics_queue, presentation_queue,
-      enable_anisotropy, enable_swapchain))
+  Ok((physical_device, device, indices, sample_count, graphics_queue,
+      presentation_queue, enable_anisotropy, enable_swapchain))
 }
 
 
@@ -580,26 +581,29 @@ fn init_descriptor_set_layout(device: &Device)
 // device to connect it to.
 #[allow(unsafe_code)]
 fn pick_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR)
-    -> Result<(vk::PhysicalDevice, QueueFamilyIndices)>
+    -> Result<(vk::PhysicalDevice, QueueFamilyIndices, vk::SampleCountFlags)>
 {
   let mut best_device = None;
   let mut best_score = None;
   let mut best_indices = None;
+  let mut best_sample_count = None;
   let mut rejected = BTreeMap::new();
 
   for device in unsafe { instance.enumerate_physical_devices() }? {
     match score_vulkan_device(instance, surface, &device)? {
-      Acceptable::Accepted((new_score, new_indices)) => {
+      Acceptable::Accepted((new_score, new_indices, new_sample_count)) => {
         if let Some(old_score) = best_score {
           if new_score > old_score {
             best_device = Some(device);
             best_score = Some(new_score);
             best_indices = Some(new_indices);
+            best_sample_count = Some(new_sample_count);
           }
         } else {
           best_device = Some(device);
           best_score = Some(new_score);
           best_indices = Some(new_indices);
+          best_sample_count = Some(new_sample_count);
         }
       }
       Acceptable::Rejected(reason) => {
@@ -614,8 +618,10 @@ fn pick_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR)
     }
   }
 
-  if let (Some(device), Some(indices)) = (best_device, best_indices) {
-    Ok((device, indices))
+  if let (Some(device), Some(indices), Some(sample_count))
+             = (best_device, best_indices, best_sample_count)
+  {
+    Ok((device, indices, sample_count))
   } else if rejected.is_empty() {
     Err(Error {
       message: "The system has no GPUs of any kind.".to_string()
@@ -647,7 +653,7 @@ fn pick_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR)
 #[allow(unsafe_code)]
 fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
                        physical_device: &vk::PhysicalDevice)
-    -> Result<Acceptable<(u64, QueueFamilyIndices)>>
+    -> Result<Acceptable<(u64, QueueFamilyIndices, vk::SampleCountFlags)>>
 {
   //   Not all devices support graphics, and not all devices support
   // presenting to any given surface. We check whether this one is suitable
@@ -726,7 +732,16 @@ fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR,
     score += 1;
   }
 
-  Ok(Acceptable::Accepted((score, indices)))
+  let sample_count = properties.limits.framebuffer_color_sample_counts
+                     & properties.limits.framebuffer_depth_sample_counts;
+  //   Happily, these bit flags are arranged in the obvious way, which lets us
+  // do some math on them. The max sample count is 64, so the max score bonus
+  // we give is 4.
+  let shift = sample_count.bits().ilog2() as u64;
+  score += shift;
+  let sample_count = vk::SampleCountFlags::from_bits(1 << shift).unwrap();
+
+  Ok(Acceptable::Accepted((score, indices, sample_count)))
 }
 
 
diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs
index fd6b8d6..3c9291c 100644
--- a/src/graphics_window_dressing.rs
+++ b/src/graphics_window_dressing.rs
@@ -32,6 +32,10 @@ pub const N_SIMULTANEOUS_FRAMES: usize = 5;
 pub struct WindowDressing {
   pub swapchain: Swapchain,
 
+  color_image: vk::Image,
+  color_image_memory: vk::DeviceMemory,
+  color_image_view: vk::ImageView,
+
   depth_image: vk::Image,
   depth_image_memory: vk::DeviceMemory,
   depth_image_view: vk::ImageView,
@@ -118,26 +122,31 @@ impl WindowDressing {
     let device = &permanent.device;
     let graphics_queue = &permanent.graphics_queue;
     let physical_device = &for_reinit.physical_device;
+    let sample_count = for_reinit.sample_count;
     let indices = &for_reinit.indices;
     let descriptor_set_layout = &for_reinit.descriptor_set_layout;
 
     let swapchain = init_swapchain(
             window, instance, surface, &physical_device, device, &indices)?;
 
+    let (color_image, color_image_memory, color_image_view)
+            = init_color(instance, &physical_device, device,
+                         &swapchain.extent, sample_count, swapchain.format)?;
+
     let (depth_image, depth_image_memory, depth_image_view, depth_format)
             = init_depth(instance, &physical_device, device,
-                         &swapchain.extent)?;
+                         &swapchain.extent, sample_count)?;
 
-    let render_pass = init_render_pass(device, &swapchain.format,
-                                       &depth_format)?;
+    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,
-                            &render_pass)?;
+                            sample_count, &render_pass)?;
 
     let framebuffers = init_framebuffers(
             device, &swapchain.extent, &swapchain.image_views,
-            &depth_image_view, &render_pass)?;
+            &color_image_view, &depth_image_view, &render_pass)?;
 
     let (primary_command_pool, transient_command_pool)
             = init_command_pools(device, indices)?;
@@ -179,6 +188,9 @@ impl WindowDressing {
 
     Ok(WindowDressing {
       swapchain,
+      color_image,
+      color_image_memory,
+      color_image_view,
       depth_image,
       depth_image_memory,
       depth_image_view,
@@ -219,6 +231,7 @@ impl WindowDressing {
     let surface = &permanent.surface;
     let device = &permanent.device;
     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;
 
@@ -229,20 +242,24 @@ impl WindowDressing {
     let swapchain = init_swapchain(
             window, instance, surface, &physical_device, device, &indices)?;
 
+    let (color_image, color_image_memory, color_image_view)
+            = init_color(instance, &physical_device, device,
+                         &swapchain.extent, sample_count, swapchain.format)?;
+
     let (depth_image, depth_image_memory, depth_image_view, depth_format)
             = init_depth(instance, &physical_device, device,
-                         &swapchain.extent)?;
+                         &swapchain.extent, sample_count)?;
 
-    let render_pass = init_render_pass(device, &swapchain.format,
-                                       &depth_format)?;
+    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,
-                            &render_pass)?;
+                            sample_count, &render_pass)?;
 
     let framebuffers = init_framebuffers(
             device, &swapchain.extent, &swapchain.image_views,
-            &depth_image_view, &render_pass)?;
+            &color_image_view, &depth_image_view, &render_pass)?;
 
     let (uniform_buffers, uniform_buffer_memory)
             = init_uniform_buffers(instance, physical_device, device,
@@ -269,6 +286,9 @@ impl WindowDressing {
                                          vk::Fence::null());
 
     self.swapchain = swapchain;
+    self.color_image = color_image;
+    self.color_image_memory = color_image_memory;
+    self.color_image_view = color_image_view;
     self.depth_image = depth_image;
     self.depth_image_memory = depth_image_memory;
     self.depth_image_view = depth_image_view;
@@ -356,6 +376,9 @@ impl WindowDressing {
     unsafe { device.destroy_pipeline(self.pipeline, None) };
     unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) };
     unsafe { device.destroy_render_pass(self.render_pass, None) };
+    unsafe { device.destroy_image(self.color_image, None) };
+    unsafe { device.free_memory(self.color_image_memory, None) };
+    unsafe { device.destroy_image_view(self.color_image_view, None) };
     unsafe { device.destroy_image(self.depth_image, None) };
     unsafe { device.free_memory(self.depth_image_memory, None) };
     unsafe { device.destroy_image_view(self.depth_image_view, None) };
@@ -448,15 +471,38 @@ fn init_swapchain(window: &Window, instance: &Instance,
 
 
 #[allow(unsafe_code)]
+fn init_color(instance: &Instance, physical_device: &vk::PhysicalDevice,
+              device: &Device, extent: &vk::Extent2D,
+              sample_count: vk::SampleCountFlags, format: vk::Format)
+    -> Result<(vk::Image, vk::DeviceMemory, vk::ImageView)>
+{
+  let (image, image_memory)
+          = allocate_image(instance, physical_device, device,
+                           extent.width, extent.height, 1, sample_count,
+                           format,
+                           vk::ImageTiling::OPTIMAL,
+                           vk::ImageUsageFlags::COLOR_ATTACHMENT
+                           | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT,
+                           vk::MemoryPropertyFlags::DEVICE_LOCAL)?;
+
+  let image_view = init_image_view(device, &image, 1, format,
+                                   vk::ImageAspectFlags::COLOR)?;
+
+  Ok((image, image_memory, image_view))
+}
+
+
+#[allow(unsafe_code)]
 fn init_depth(instance: &Instance, physical_device: &vk::PhysicalDevice,
-              device: &Device, extent: &vk::Extent2D)
+              device: &Device, extent: &vk::Extent2D,
+              sample_count: vk::SampleCountFlags)
     -> Result<(vk::Image, vk::DeviceMemory, vk::ImageView, vk::Format)>
 {
   let format = pick_depth_format(instance, physical_device)?;
 
   let (image, image_memory)
           = allocate_image(instance, physical_device, device,
-                           extent.width, extent.height, 1,
+                           extent.width, extent.height, 1, sample_count,
                            format,
                            vk::ImageTiling::OPTIMAL,
                            vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
@@ -470,19 +516,19 @@ fn init_depth(instance: &Instance, physical_device: &vk::PhysicalDevice,
 
 
 #[allow(unsafe_code)]
-fn init_render_pass(device: &Device, color_format: &vk::Format,
-                    depth_format: &vk::Format)
+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(vk::SampleCountFlags::_1)
+          .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::PRESENT_SRC_KHR);
+          .final_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
 
   let color_attachment_reference = vk::AttachmentReference::builder()
           .attachment(0)
@@ -490,7 +536,7 @@ fn init_render_pass(device: &Device, color_format: &vk::Format,
 
   let depth_attachment = vk::AttachmentDescription::builder()
           .format(*depth_format)
-          .samples(vk::SampleCountFlags::_1)
+          .samples(sample_count)
           .load_op(vk::AttachmentLoadOp::CLEAR)
           .store_op(vk::AttachmentStoreOp::DONT_CARE)
           .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
@@ -502,34 +548,48 @@ fn init_render_pass(device: &Device, color_format: &vk::Format,
           .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);
-
-  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];
+          .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);
+          .attachments(&render_attachments)
+          .subpasses(&subpasses)
+          .dependencies(&dependencies);
 
   let render_pass = unsafe {
     device.create_render_pass(&render_pass_info, None)
@@ -542,7 +602,8 @@ fn init_render_pass(device: &Device, color_format: &vk::Format,
 #[allow(unsafe_code)]
 fn init_pipeline(device: &Device,
                  descriptor_set_layout: &vk::DescriptorSetLayout,
-                 extent: &vk::Extent2D, render_pass: &vk::RenderPass)
+                 extent: &vk::Extent2D, sample_count: vk::SampleCountFlags,
+                 render_pass: &vk::RenderPass)
     -> Result<(vk::PipelineLayout, vk::Pipeline)>
 {
   let vertex_binary = include_bytes!(
@@ -556,14 +617,14 @@ fn init_pipeline(device: &Device,
           device, fragment_binary)?;
 
   let vertex_stage_info = vk::PipelineShaderStageCreateInfo::builder()
-                              .stage(vk::ShaderStageFlags::VERTEX)
-                              .module(vertex_module)
-                              .name(b"main\0");
+          .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");
+          .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();
@@ -578,37 +639,36 @@ fn init_pipeline(device: &Device,
                 .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);
+          .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);
+          .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);
+          .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);
+                .rasterization_samples(sample_count);
 
   let depth_state_info = vk::PipelineDepthStencilStateCreateInfo::builder()
           .depth_test_enable(true)
@@ -619,23 +679,22 @@ fn init_pipeline(device: &Device,
           .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_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]);
+          .logic_op_enable(false)
+          .logic_op(vk::LogicOp::COPY)
+          .attachments(&blend_attachments)
+          .blend_constants([0.0, 0.0, 0.0, 0.0]);
 
   let layouts = [*descriptor_set_layout];
   let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder()
@@ -646,19 +705,18 @@ fn init_pipeline(device: &Device,
   }?;
 
   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_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(),
@@ -677,21 +735,24 @@ fn init_pipeline(device: &Device,
 #[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_image_view in swapchain_image_views {
-    let attachments = [*color_image_view, *depth_image_view];
+  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);
+            .render_pass(*render_pass)
+            .attachments(&attachments)
+            .width(extent.width)
+            .height(extent.height)
+            .layers(1);
 
     let framebuffer = unsafe {
       device.create_framebuffer(&framebuffer_info, None)
@@ -760,7 +821,7 @@ fn init_texture(instance: &Instance,
 
   let (image, image_memory)
           = allocate_image(instance, physical_device, device,
-                           width, height, mip_count,
+                           width, height, mip_count, vk::SampleCountFlags::_1,
                            vk::Format::R8G8B8A8_SRGB,
                            vk::ImageTiling::OPTIMAL,
                            vk::ImageUsageFlags::SAMPLED
@@ -1006,8 +1067,8 @@ fn init_commands(device: &Device,
     }?;
 
     let render_area = vk::Rect2D::builder()
-                          .offset(vk::Offset2D::default())
-                          .extent(*extent);
+            .offset(vk::Offset2D::default())
+            .extent(*extent);
 
     let color_clear_value = vk::ClearValue {
       color: vk::ClearColorValue {
@@ -1023,10 +1084,10 @@ fn init_commands(device: &Device,
     let clear_values = [color_clear_value, depth_clear_value];
 
     let begin_pass_info = vk::RenderPassBeginInfo::builder()
-                              .render_pass(*render_pass)
-                              .framebuffer(*framebuffer)
-                              .render_area(render_area)
-                              .clear_values(&clear_values);
+            .render_pass(*render_pass)
+            .framebuffer(*framebuffer)
+            .render_area(render_area)
+            .clear_values(&clear_values);
 
     unsafe {
       device.cmd_begin_render_pass(command_buffer, &begin_pass_info,
@@ -1126,18 +1187,18 @@ fn init_image_view(device: &Device, image: &vk::Image, mip_count: u32,
           .a(vk::ComponentSwizzle::IDENTITY);
 
   let subresource_range = vk::ImageSubresourceRange::builder()
-                              .aspect_mask(aspects)
-                              .base_mip_level(0)
-                              .level_count(mip_count)
-                              .base_array_layer(0)
-                              .layer_count(1);
+          .aspect_mask(aspects)
+          .base_mip_level(0)
+          .level_count(mip_count)
+          .base_array_layer(0)
+          .layer_count(1);
 
   let view_info = vk::ImageViewCreateInfo::builder()
-                      .image(*image)
-                      .view_type(vk::ImageViewType::_2D)
-                      .format(format)
-                      .components(components)
-                      .subresource_range(subresource_range);
+          .image(*image)
+          .view_type(vk::ImageViewType::_2D)
+          .format(format)
+          .components(components)
+          .subresource_range(subresource_range);
 
   let view = unsafe {
     device.create_image_view(&view_info, None)
@@ -1308,8 +1369,8 @@ fn allocate_buffer(instance: &Instance,
                                     &memory_flags, &requirements)?;
 
   let memory_info = vk::MemoryAllocateInfo::builder()
-                        .allocation_size(requirements.size)
-                        .memory_type_index(type_index);
+          .allocation_size(requirements.size)
+          .memory_type_index(type_index);
 
   let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?;
 
@@ -1342,8 +1403,8 @@ fn copy_buffer(device: &Device, queue: &vk::Queue,
 #[allow(unsafe_code)]
 fn allocate_image(instance: &Instance, physical_device: &vk::PhysicalDevice,
                   device: &Device, width: u32, height: u32, mip_count: u32,
-                  format: vk::Format, tiling: vk::ImageTiling,
-                  usage: vk::ImageUsageFlags,
+                  sample_count: vk::SampleCountFlags, format: vk::Format,
+                  tiling: vk::ImageTiling, usage: vk::ImageUsageFlags,
                   memory_flags: vk::MemoryPropertyFlags)
     -> Result<(vk::Image, vk::DeviceMemory)>
 {
@@ -1351,13 +1412,13 @@ fn allocate_image(instance: &Instance, physical_device: &vk::PhysicalDevice,
           .image_type(vk::ImageType::_2D)
           .extent(vk::Extent3D { width, height, depth: 1 })
           .mip_levels(mip_count)
+          .samples(sample_count)
           .array_layers(1)
           .format(format)
           .tiling(tiling)
           .initial_layout(vk::ImageLayout::UNDEFINED)
           .usage(usage)
           .sharing_mode(vk::SharingMode::EXCLUSIVE)
-          .samples(vk::SampleCountFlags::_1)
           .flags(vk::ImageCreateFlags::empty());
   let image = unsafe { device.create_image(&image_info, None) }?;
 
@@ -1423,11 +1484,11 @@ fn change_image_layout(device: &Device, queue: &vk::Queue,
   let command_buffer = begin_transient_commands(device, command_pool)?;
 
   let subresource_range = vk::ImageSubresourceRange::builder()
-                              .aspect_mask(vk::ImageAspectFlags::COLOR)
-                              .base_mip_level(0)
-                              .level_count(mip_count)
-                              .base_array_layer(0)
-                              .layer_count(1);
+          .aspect_mask(vk::ImageAspectFlags::COLOR)
+          .base_mip_level(0)
+          .level_count(mip_count)
+          .base_array_layer(0)
+          .layer_count(1);
 
   //   Notionally this is a property that our caller is in a better position
   // to know than we are, but in practice the nature of the transition