summary refs log tree commit diff
path: root/src/graphics_window_dressing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics_window_dressing.rs')
-rw-r--r--src/graphics_window_dressing.rs317
1 files changed, 275 insertions, 42 deletions
diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs
index 38d4188..4dddb35 100644
--- a/src/graphics_window_dressing.rs
+++ b/src/graphics_window_dressing.rs
@@ -6,7 +6,10 @@ use crate::graphics_permanent::{
 use crate::linear_algebra::{ Vertex, VERTICES, INDICES, UniformBlock };
 
 use std::collections::BTreeSet;
+use std::io::Cursor;
 use std::ptr::copy_nonoverlapping;
+
+use png::Decoder;
 use vulkanalia::{ Device, Instance };
 use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0,
                       KhrSwapchainExtensionDeviceCommands };
@@ -43,6 +46,9 @@ pub struct WindowDressing {
   index_buffer: vk::Buffer,
   index_buffer_memory: vk::DeviceMemory,
 
+  texture_image: vk::Image,
+  texture_image_memory: vk::DeviceMemory,
+
   uniform_buffers: Vec<vk::Buffer>,
   pub uniform_buffer_memory: Vec<vk::DeviceMemory>,
 
@@ -124,6 +130,11 @@ impl WindowDressing {
     let (index_buffer, index_buffer_memory)
             = init_index_buffer(instance, physical_device, device,
                                 graphics_queue, &transient_command_pool)?;
+
+    let (texture_image, texture_image_memory)
+            = init_texture(instance, physical_device, device,
+                           graphics_queue, &transient_command_pool)?;
+
     let (uniform_buffers, uniform_buffer_memory)
             = init_uniform_buffers(instance, physical_device, device,
                                    swapchain.images.len())?;
@@ -152,6 +163,8 @@ impl WindowDressing {
       vertex_buffer_memory,
       index_buffer,
       index_buffer_memory,
+      texture_image,
+      texture_image_memory,
       uniform_buffers,
       uniform_buffer_memory,
       descriptor_pool,
@@ -241,6 +254,8 @@ impl WindowDressing {
     unsafe { device.free_memory(self.vertex_buffer_memory, None) };
     unsafe { device.destroy_buffer(self.index_buffer, None) };
     unsafe { device.free_memory(self.index_buffer_memory, None) };
+    unsafe { device.destroy_image(self.texture_image, None) };
+    unsafe { device.free_memory(self.texture_image_memory, None) };
 
     for semaphore in self.concurrency.image_available_semaphores {
       unsafe { device.destroy_semaphore(semaphore, None) };
@@ -629,6 +644,54 @@ fn init_index_buffer(instance: &Instance,
               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)
+    -> Result<(vk::Image, vk::DeviceMemory)>
+{
+  let png = include_bytes!("../textures/forest_leaves_04_diff.png");
+
+  let decoder = Decoder::new(Cursor::new(png));
+  let mut reader = decoder.read_info()?;
+
+  let (width, height) = reader.info().size();
+
+  let mut pixels = vec![0; reader.info().raw_bytes()];
+  reader.next_frame(&mut pixels)?;
+
+  let (staging_buffer, staging_memory, byte_size)
+          = stage_in_buffer(instance, physical_device, device, &pixels)?;
+
+  let (image, image_memory)
+          = allocate_image(instance, physical_device, device, width, height,
+                           vk::Format::R8G8B8A8_SRGB,
+                           vk::ImageTiling::OPTIMAL,
+                           vk::ImageUsageFlags::SAMPLED
+                               | vk::ImageUsageFlags::TRANSFER_DST,
+                           vk::MemoryPropertyFlags::DEVICE_LOCAL)?;
+
+  change_image_layout(device, queue, command_pool, &image,
+                      vk::Format::R8G8B8A8_SRGB,
+                      vk::ImageLayout::UNDEFINED,
+                      vk::ImageLayout::TRANSFER_DST_OPTIMAL)?;
+
+  copy_buffer_to_image(device, queue, command_pool, &staging_buffer, &image,
+                       width, height)?;
+
+  change_image_layout(device, queue, command_pool, &image,
+                      vk::Format::R8G8B8A8_SRGB,
+                      vk::ImageLayout::TRANSFER_DST_OPTIMAL,
+                      vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)?;
+
+  unsafe { device.destroy_buffer(staging_buffer, None) };
+  unsafe { device.free_memory(staging_memory, None) };
+
+  Ok((image, image_memory))
+}
+
+
 fn init_uniform_buffers(instance: &Instance,
                         physical_device: &vk::PhysicalDevice, device: &Device,
                         count: usize)
@@ -640,7 +703,7 @@ fn init_uniform_buffers(instance: &Instance,
   for _ in 0 .. count {
     let (buffer, memory) = allocate_buffer(
             instance, physical_device, device,
-            size_of::<UniformBlock<f32>>() as u64,
+            size_of::<UniformBlock<f32>>() as vk::DeviceSize,
             vk::BufferUsageFlags::UNIFORM_BUFFER,
             vk::MemoryPropertyFlags::HOST_COHERENT
             | vk::MemoryPropertyFlags::HOST_VISIBLE)?;
@@ -659,33 +722,18 @@ fn init_buffer<T>(instance: &Instance,
                   usage: vk::BufferUsageFlags, contents: &[T])
     -> Result<(vk::Buffer, vk::DeviceMemory)>
 {
-  let size = (size_of::<T>() * contents.len()) as u64;
-
-  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, staging_usage, staging_memory_flags)?;
-
-  let host_memory = unsafe {
-    device.map_memory(staging_memory, 0, size, vk::MemoryMapFlags::empty())
-  }?;
-
-  unsafe {
-    copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len())
-  };
-
-  unsafe { device.unmap_memory(staging_memory) };
+  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, final_usage, final_memory_flags)?;
+                            size as vk::DeviceSize, final_usage,
+                            final_memory_flags)?;
 
-  copy_buffer(device, queue, command_pool,
-              &staging_buffer, &final_buffer, size)?;
+  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) };
@@ -728,7 +776,7 @@ fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout,
     let buffer_info = vk::DescriptorBufferInfo::builder()
             .buffer(buffers[index])
             .offset(0)
-            .range(size_of::<UniformBlock<f32>>() as u64);
+            .range(size_of::<UniformBlock<f32>>() as vk::DeviceSize);
 
     let buffer_info_list = [buffer_info];
     let descriptor_write_info = vk::WriteDescriptorSet::builder()
@@ -986,6 +1034,37 @@ fn pick_memory_type(instance: &Instance,
 
 
 #[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,
@@ -1008,7 +1087,6 @@ fn allocate_buffer(instance: &Instance,
   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);
@@ -1023,11 +1101,169 @@ fn allocate_buffer(instance: &Instance,
 
 #[allow(unsafe_code)]
 fn copy_buffer(device: &Device, queue: &vk::Queue,
-               command_pool: &vk::CommandPool,
-               source: &vk::Buffer, destination: &vk::Buffer,
-               size: vk::DeviceSize)
+               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,
+                  format: vk::Format, tiling: vk::ImageTiling,
+                  usage: vk::ImageUsageFlags,
+                  memory_flags: vk::MemoryPropertyFlags)
+    -> Result<(vk::Image, vk::DeviceMemory)>
+{
+  let image_info = vk::ImageCreateInfo::builder()
+          .image_type(vk::ImageType::_2D)
+          .extent(vk::Extent3D { width, height, depth: 1 })
+          .mip_levels(1)
+          .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) }?;
+
+  let requirements = unsafe { device.get_image_memory_requirements(image) };
+
+  let type_index = pick_memory_type(instance, physical_device,
+                                    &vk::MemoryPropertyFlags::DEVICE_LOCAL,
+                                    &requirements)?;
+
+  let image_memory_info = vk::MemoryAllocateInfo::builder()
+          .allocation_size(requirements.size)
+          .memory_type_index(type_index);
+  let image_memory = unsafe {
+    device.allocate_memory(&image_memory_info, None)
+  }?;
+
+  unsafe { device.bind_image_memory(image, image_memory, 0) }?;
+
+  Ok((image, image_memory))
+}
+
+
+#[allow(unsafe_code)]
+fn copy_buffer_to_image(device: &Device, queue: &vk::Queue,
+                        command_pool: &vk::CommandPool, source: &vk::Buffer,
+                        destination: &vk::Image, width: u32, height: u32)
+    -> Result<()>
+{
+  let command_buffer = begin_transient_commands(device, command_pool)?;
+
+  let subresource_layers = vk::ImageSubresourceLayers::builder()
+          .aspect_mask(vk::ImageAspectFlags::COLOR)
+          .mip_level(0)
+          .base_array_layer(0)
+          .layer_count(1);
+
+  let copy_info = vk::BufferImageCopy::builder()
+          .buffer_offset(0)
+          .buffer_row_length(0)
+          .buffer_image_height(0)
+          .image_subresource(subresource_layers)
+          .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 })
+          .image_extent(vk::Extent3D { width, height, depth: 1 });
+
+  unsafe {
+    device.cmd_copy_buffer_to_image(command_buffer, *source, *destination,
+                                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
+                                    &[copy_info])
+  };
+
+  end_transient_commands(command_buffer, device, queue, command_pool)?;
+
+  Ok(())
+}
+
+
+#[allow(unsafe_code)]
+fn change_image_layout(device: &Device, queue: &vk::Queue,
+                       command_pool: &vk::CommandPool, image: &vk::Image,
+                       format: vk::Format, old: vk::ImageLayout,
+                       new: vk::ImageLayout)
+    -> Result<()>
+{
+  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(1)
+                              .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
+  // strongly implies a particular phase of the image's lifecycle, so we just
+  // compute it here.
+  let (source_access, source_stage, destination_access, destination_stage)
+          = match (old, new)
+  {
+    (vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL)
+        => (vk::AccessFlags::empty(),
+            vk::PipelineStageFlags::TOP_OF_PIPE,
+            vk::AccessFlags::TRANSFER_WRITE,
+            vk::PipelineStageFlags::TRANSFER),
+    (vk::ImageLayout::TRANSFER_DST_OPTIMAL,
+     vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
+        => (vk::AccessFlags::TRANSFER_WRITE,
+            vk::PipelineStageFlags::TRANSFER,
+            vk::AccessFlags::SHADER_READ,
+            vk::PipelineStageFlags::FRAGMENT_SHADER),
+    _ => return Err(Error {
+      message:
+          format!("Don't know how to change from image layout {:?} to {:?}",
+                  old, new)
+    })
+  };
+
+  let barrier_info = vk::ImageMemoryBarrier::builder()
+        .image(*image)
+        .subresource_range(subresource_range)
+        .old_layout(old)
+        .new_layout(new)
+        .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
+        .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
+        .src_access_mask(source_access)
+        .dst_access_mask(destination_access);
+
+  unsafe {
+    device.cmd_pipeline_barrier(command_buffer,
+                                source_stage, destination_stage,
+                                vk::DependencyFlags::empty(),
+                                &[] as &[vk::MemoryBarrier],
+                                &[] as &[vk::BufferMemoryBarrier],
+                                &[barrier_info])
+  };
+
+  end_transient_commands(command_buffer, device, queue, command_pool)?;
+
+  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)
@@ -1044,29 +1280,26 @@ fn copy_buffer(device: &Device, queue: &vk::Queue,
     device.begin_command_buffer(command_buffer, &command_buffer_begin_info)
   }?;
 
-  let copy_info = vk::BufferCopy::builder().size(size);
-  unsafe {
-    device.cmd_copy_buffer(command_buffer, *source, *destination,
-                           &[copy_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_submit(*queue, &[submit_info], vk::Fence::null()) }?;
 
-  unsafe {
-    device.queue_wait_idle(*queue)
-  }?;
+  unsafe { device.queue_wait_idle(*queue) }?;
 
-  unsafe {
-    device.free_command_buffers(*command_pool, &command_buffers)
-  };
+  unsafe { device.free_command_buffers(*command_pool, &command_buffers) };
 
   Ok(())
 }