diff --git a/src/app/data.rs b/src/app/data.rs index cfa60ab..88e3b18 100644 --- a/src/app/data.rs +++ b/src/app/data.rs @@ -69,6 +69,7 @@ pub(crate) struct AppData { depth_image_memory: vk::DeviceMemory, depth_image_view: vk::ImageView, // Texture + mip_levels: u32, texture_image: vk::Image, texture_image_memory: vk::DeviceMemory, texture_image_view: vk::ImageView, @@ -324,7 +325,7 @@ impl AppData { } } -pub(crate) unsafe fn check_physical_device_extensions( +unsafe fn check_physical_device_extensions( instance: &Instance, physical_device: vk::PhysicalDevice, ) -> Result<()> { @@ -475,6 +476,7 @@ impl AppData { device: &Device, ) -> Result<()> { device.device_wait_idle()?; + self.destroy_swapchain(device); self.create_swapchain(window, instance, device)?; self.create_swapchain_image_views(device)?; @@ -503,6 +505,7 @@ impl AppData { *i, self.swapchain_format, vk::ImageAspectFlags::COLOR, + 1, ) }) .collect::, _>>()?; @@ -511,9 +514,7 @@ impl AppData { } } -pub(crate) fn get_swapchain_surface_format( - formats: &[vk::SurfaceFormatKHR], -) -> vk::SurfaceFormatKHR { +fn get_swapchain_surface_format(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR { formats .iter() .cloned() @@ -524,9 +525,7 @@ pub(crate) fn get_swapchain_surface_format( .unwrap_or_else(|| formats[0]) } -pub(crate) fn get_swapchain_present_mode( - present_modes: &[vk::PresentModeKHR], -) -> vk::PresentModeKHR { +fn get_swapchain_present_mode(present_modes: &[vk::PresentModeKHR]) -> vk::PresentModeKHR { present_modes .iter() .cloned() @@ -534,10 +533,7 @@ pub(crate) fn get_swapchain_present_mode( .unwrap_or(vk::PresentModeKHR::FIFO) } -pub(crate) fn get_swapchain_extent( - window: &Window, - capabilities: vk::SurfaceCapabilitiesKHR, -) -> vk::Extent2D { +fn get_swapchain_extent(window: &Window, capabilities: vk::SurfaceCapabilitiesKHR) -> vk::Extent2D { if capabilities.current_extent.width != u32::MAX { capabilities.current_extent } else { @@ -758,10 +754,7 @@ impl AppData { } } -pub(crate) unsafe fn create_shader_module( - device: &Device, - bytecode: &[u8], -) -> Result { +unsafe fn create_shader_module(device: &Device, bytecode: &[u8]) -> Result { let bytecode = Vec::::from(bytecode); let (prefix, code, suffix) = bytecode.align_to::(); if !prefix.is_empty() || !suffix.is_empty() { @@ -829,6 +822,7 @@ impl AppData { device, self.swapchain_extent.width, self.swapchain_extent.height, + 1, format, vk::ImageTiling::OPTIMAL, vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, @@ -843,6 +837,7 @@ impl AppData { self.depth_image, format, vk::ImageAspectFlags::DEPTH, + 1, )?; Ok(()) @@ -905,6 +900,7 @@ impl AppData { let size = reader.info().raw_bytes() as u64; let (width, height) = reader.info().size(); + self.mip_levels = (width.max(height) as f32).log2().floor() as u32 + 1; // Create the staging buffer let (staging_buffer, staging_buffer_memory) = unsafe { @@ -931,9 +927,12 @@ impl AppData { device, width, height, + self.mip_levels, vk::Format::R8G8B8A8_SRGB, vk::ImageTiling::OPTIMAL, - vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, + vk::ImageUsageFlags::SAMPLED + | vk::ImageUsageFlags::TRANSFER_DST + | vk::ImageUsageFlags::TRANSFER_SRC, vk::MemoryPropertyFlags::DEVICE_LOCAL, )?; @@ -946,31 +945,176 @@ impl AppData { vk::Format::R8G8B8A8_SRGB, vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL, + self.mip_levels, )?; self.copy_buffer_to_image(device, staging_buffer, self.texture_image, width, height)?; - self.transition_image_layout( + device.destroy_buffer(staging_buffer, None); + device.free_memory(staging_buffer_memory, None); + + self.generate_mipmaps( + instance, device, self.texture_image, vk::Format::R8G8B8A8_SRGB, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + width, + height, + self.mip_levels, )?; - - device.destroy_buffer(staging_buffer, None); - device.free_memory(staging_buffer_memory, None); } Ok(()) } + unsafe fn generate_mipmaps( + &self, + instance: &Instance, + device: &Device, + image: vk::Image, + format: vk::Format, + width: u32, + height: u32, + mip_levels: u32, + ) -> Result<()> { + if !instance + .get_physical_device_format_properties(self.physical_device, format) + .optimal_tiling_features + .contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR) + { + return Err(anyhow!( + "Texture image format does not support linear blitting!" + )); + } + + let command_buffer = self.begin_single_time_commands(device)?; + + let subresource = vk::ImageSubresourceRange::builder() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_array_layer(0) + .layer_count(1) + .level_count(1); + + let mut barrier = vk::ImageMemoryBarrier::builder() + .image(image) + .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .subresource_range(subresource); + + let mut mip_width = width; + let mut mip_height = height; + + for i in 1..mip_levels { + barrier.subresource_range.base_mip_level = i - 1; + barrier.old_layout = vk::ImageLayout::TRANSFER_DST_OPTIMAL; + barrier.new_layout = vk::ImageLayout::TRANSFER_SRC_OPTIMAL; + barrier.src_access_mask = vk::AccessFlags::TRANSFER_WRITE; + barrier.dst_access_mask = vk::AccessFlags::TRANSFER_READ; + + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::TRANSFER, + vk::DependencyFlags::empty(), + &[] as &[vk::MemoryBarrier], + &[] as &[vk::BufferMemoryBarrier], + &[barrier], + ); + + let src_subresource = vk::ImageSubresourceLayers::builder() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .mip_level(i - 1) + .base_array_layer(0) + .layer_count(1); + + let dst_subresource = vk::ImageSubresourceLayers::builder() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .mip_level(i) + .base_array_layer(0) + .layer_count(1); + + let blit = vk::ImageBlit::builder() + .src_offsets([ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: mip_width as i32, + y: mip_height as i32, + z: 1, + }, + ]) + .src_subresource(src_subresource) + .dst_offsets([ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: (if mip_width > 1 { mip_width / 2 } else { 1 }) as i32, + y: (if mip_height > 1 { mip_height / 2 } else { 1 }) as i32, + z: 1, + }, + ]) + .dst_subresource(dst_subresource); + + device.cmd_blit_image( + command_buffer, + image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[blit], + vk::Filter::LINEAR, + ); + + barrier.old_layout = vk::ImageLayout::TRANSFER_SRC_OPTIMAL; + barrier.new_layout = vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL; + barrier.src_access_mask = vk::AccessFlags::TRANSFER_READ; + barrier.dst_access_mask = vk::AccessFlags::SHADER_READ; + + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::DependencyFlags::empty(), + &[] as &[vk::MemoryBarrier], + &[] as &[vk::BufferMemoryBarrier], + &[barrier], + ); + + if mip_width > 1 { + mip_width /= 2; + } + + if mip_height > 1 { + mip_height /= 2; + } + } + + barrier.subresource_range.base_mip_level = mip_levels - 1; + barrier.old_layout = vk::ImageLayout::TRANSFER_DST_OPTIMAL; + barrier.new_layout = vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL; + barrier.src_access_mask = vk::AccessFlags::TRANSFER_WRITE; + barrier.dst_access_mask = vk::AccessFlags::SHADER_READ; + + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::FRAGMENT_SHADER, + vk::DependencyFlags::empty(), + &[] as &[vk::MemoryBarrier], + &[] as &[vk::BufferMemoryBarrier], + &[barrier], + ); + + self.end_single_time_commands(device, command_buffer)?; + + Ok(()) + } + unsafe fn create_texture_image_view(&mut self, device: &Device) -> Result<()> { self.texture_image_view = create_image_view( device, self.texture_image, vk::Format::R8G8B8A8_SRGB, vk::ImageAspectFlags::COLOR, + self.mip_levels, )?; Ok(()) @@ -1385,6 +1529,7 @@ impl AppData { device: &Device, width: u32, height: u32, + mip_levels: u32, format: vk::Format, tiling: vk::ImageTiling, usage: vk::ImageUsageFlags, @@ -1397,7 +1542,7 @@ impl AppData { height, depth: 1, }) - .mip_levels(1) + .mip_levels(mip_levels) .array_layers(1) .format(format) .tiling(tiling) @@ -1428,6 +1573,7 @@ impl AppData { _format: vk::Format, old_layout: vk::ImageLayout, new_layout: vk::ImageLayout, + mip_levels: u32, ) -> Result<()> { let (src_access_mask, dst_access_mask, src_stage_mask, dst_stage_mask) = match (old_layout, new_layout) { @@ -1454,7 +1600,7 @@ impl AppData { let subresource = vk::ImageSubresourceRange::builder() .aspect_mask(vk::ImageAspectFlags::COLOR) .base_mip_level(0) - .level_count(1) + .level_count(mip_levels) .base_array_layer(0) .layer_count(1); @@ -1525,16 +1671,17 @@ impl AppData { } } -pub(crate) unsafe fn create_image_view( +unsafe fn create_image_view( device: &Device, image: vk::Image, format: vk::Format, aspects: vk::ImageAspectFlags, + mip_levels: u32, ) -> Result { let subresource_range = vk::ImageSubresourceRange::builder() .aspect_mask(aspects) .base_mip_level(0) - .level_count(1) + .level_count(mip_levels) .base_array_layer(0) .layer_count(1);