diff --git a/src/app_data.rs b/src/app_data.rs index 457029f..6e82935 100644 --- a/src/app_data.rs +++ b/src/app_data.rs @@ -48,6 +48,7 @@ pub struct AppData { pub descriptor_pool: vk::DescriptorPool, pub descriptor_sets: Vec, + pub mip_levels: u32, pub texture_image: vk::Image, pub texture_image_memory: vk::DeviceMemory, pub texture_image_view: vk::ImageView, diff --git a/src/depth_buffer.rs b/src/depth_buffer.rs index 9601ca2..cdc1eff 100644 --- a/src/depth_buffer.rs +++ b/src/depth_buffer.rs @@ -16,6 +16,7 @@ pub unsafe fn create_depth_objects( data, data.swapchain_extent.width, data.swapchain_extent.height, + 1, format, vk::ImageTiling::OPTIMAL, vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, @@ -27,7 +28,7 @@ pub unsafe fn create_depth_objects( // Image View - data.depth_image_view = image::create_image_view(device, data.depth_image, format, vk::ImageAspectFlags::DEPTH)?; + data.depth_image_view = image::create_image_view(device, data.depth_image, format, vk::ImageAspectFlags::DEPTH, 1)?; image::transition_image_layout( device, @@ -36,6 +37,7 @@ pub unsafe fn create_depth_objects( format, vk::ImageLayout::UNDEFINED, vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + 1 )?; Ok(()) diff --git a/src/image.rs b/src/image.rs index 770d886..c811228 100644 --- a/src/image.rs +++ b/src/image.rs @@ -29,6 +29,8 @@ pub unsafe fn create_texture_image( let size = reader.info().raw_bytes() as u64; let (width, height) = reader.info().size(); + data.mip_levels = (width.max(height) as f32).log2().floor() as u32 + 1; + if width != 1024 || height != 1024 || reader.info().color_type != png::ColorType::Rgba { panic!("Invalid texture image (use https://kylemayes.github.io/vulkanalia/images/viking_room.png)."); } @@ -59,9 +61,12 @@ pub unsafe fn create_texture_image( data, width, height, + data.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, )?; @@ -75,6 +80,7 @@ pub unsafe fn create_texture_image( vk::Format::R8G8B8A8_SRGB, vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL, + data.mip_levels )?; copy_buffer_to_image( @@ -86,13 +92,15 @@ pub unsafe fn create_texture_image( height, )?; - transition_image_layout( + generate_mipmaps( + instance, device, data, data.texture_image, vk::Format::R8G8B8A8_SRGB, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + width, + height, + data.mip_levels, )?; device.destroy_buffer(staging_buffer, None); @@ -107,6 +115,7 @@ pub unsafe fn create_image( data: &app_data::AppData, width: u32, height: u32, + mip_levels: u32, format: vk::Format, tiling: vk::ImageTiling, usage: vk::ImageUsageFlags, @@ -119,7 +128,7 @@ pub unsafe fn create_image( height, depth: 1, }) - .mip_levels(1) + .mip_levels(mip_levels) .array_layers(1) .format(format) .tiling(tiling) @@ -155,6 +164,7 @@ pub unsafe fn transition_image_layout( format: vk::Format, old_layout: vk::ImageLayout, new_layout: vk::ImageLayout, + mip_levels: u32, ) -> Result<()> { let command_buffer = command_buffer::begin_single_time_commands(device, data)?; @@ -198,7 +208,7 @@ pub unsafe fn transition_image_layout( let subresource = vk::ImageSubresourceRange::builder() .aspect_mask(aspect_mask) .base_mip_level(0) - .level_count(1) + .level_count(mip_levels) .base_array_layer(0) .layer_count(1); @@ -269,11 +279,12 @@ pub unsafe fn create_image_view( 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); @@ -291,7 +302,8 @@ pub unsafe fn create_texture_image_view(device: &Device, data: &mut app_data::Ap device, data.texture_image, vk::Format::R8G8B8A8_SRGB, - vk::ImageAspectFlags::COLOR + vk::ImageAspectFlags::COLOR, + data.mip_levels )?; Ok(()) @@ -312,10 +324,150 @@ pub unsafe fn create_texture_sampler(device: &Device, data: &mut app_data::AppDa .compare_enable(false) .compare_op(vk::CompareOp::ALWAYS) .mipmap_mode(vk::SamplerMipmapMode::LINEAR) - .mip_lod_bias(0.0) .min_lod(0.0) - .max_lod(0.0); + .max_lod(data.mip_levels as f32) + .mip_lod_bias(0.0); data.texture_sampler = device.create_sampler(&info, None)?; + Ok(()) +} + +unsafe fn generate_mipmaps( + instance: &Instance, + device: &Device, + data: &app_data::AppData, + image: vk::Image, + format: vk::Format, + width: u32, + height: u32, + mip_levels: u32, +) -> Result<()> { + if !instance + .get_physical_device_format_properties(data.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 = command_buffer::begin_single_time_commands(device, data)?; + + 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], + ); + + command_buffer::end_single_time_commands(device, data, command_buffer)?; + Ok(()) } \ No newline at end of file diff --git a/src/swapchain.rs b/src/swapchain.rs index c4f7400..85c48af 100644 --- a/src/swapchain.rs +++ b/src/swapchain.rs @@ -141,7 +141,7 @@ pub unsafe fn create_swapchain_image_views( data.swapchain_image_views = data .swapchain_images .iter() - .map(|i| image::create_image_view(device, *i, data.swapchain_format, vk::ImageAspectFlags::COLOR)) + .map(|i| image::create_image_view(device, *i, data.swapchain_format, vk::ImageAspectFlags::COLOR, 1)) .collect::, _>>()?; Ok(())